linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / fs / lockd / clntproc.c
index 57b1e77..970b6a6 100644 (file)
 
 #define NLMDBG_FACILITY                NLMDBG_CLIENT
 #define NLMCLNT_GRACE_WAIT     (5*HZ)
+#define NLMCLNT_POLL_TIMEOUT   (30*HZ)
+#define NLMCLNT_MAX_RETRIES    3
 
 static int     nlmclnt_test(struct nlm_rqst *, struct file_lock *);
 static int     nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
 static int     nlmclnt_unlock(struct nlm_rqst *, struct file_lock *);
-static void    nlmclnt_unlock_callback(struct rpc_task *);
-static void    nlmclnt_cancel_callback(struct rpc_task *);
 static int     nlm_stat_to_errno(u32 stat);
 static void    nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host);
+static int     nlmclnt_cancel(struct nlm_host *, int , struct file_lock *);
+
+static const struct rpc_call_ops nlmclnt_unlock_ops;
+static const struct rpc_call_ops nlmclnt_cancel_ops;
 
 /*
  * Cookie counter for NLM requests
@@ -111,8 +115,7 @@ static struct nlm_lockowner *nlm_find_lockowner(struct nlm_host *host, fl_owner_
                }
        }
        spin_unlock(&host->h_lock);
-       if (new != NULL)
-               kfree(new);
+       kfree(new);
        return res;
 }
 
@@ -221,8 +224,7 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
                        goto done;
                }
                clnt->cl_softrtry = nfssrv->client->cl_softrtry;
-               clnt->cl_intr     = nfssrv->client->cl_intr;
-               clnt->cl_chatty   = nfssrv->client->cl_chatty;
+               clnt->cl_intr = nfssrv->client->cl_intr;
        }
 
        /* Keep the old signal mask */
@@ -298,8 +300,7 @@ nlmclnt_alloc_call(void)
                        return call;
                }
                printk("nlmclnt_alloc_call: failed, waiting for memory\n");
-               current->state = TASK_INTERRUPTIBLE;
-               schedule_timeout(5*HZ);
+               schedule_timeout_interruptible(5*HZ);
        }
        return NULL;
 }
@@ -312,6 +313,7 @@ static int nlm_wait_on_grace(wait_queue_head_t *queue)
        prepare_to_wait(queue, &wait, TASK_INTERRUPTIBLE);
        if (!signalled ()) {
                schedule_timeout(NLMCLNT_GRACE_WAIT);
+               try_to_freeze();
                if (!signalled ())
                        status = 0;
        }
@@ -322,7 +324,7 @@ static int nlm_wait_on_grace(wait_queue_head_t *queue)
 /*
  * Generic NLM call
  */
-int
+static int
 nlmclnt_call(struct nlm_rqst *req, u32 proc)
 {
        struct nlm_host *host = req->a_host;
@@ -399,8 +401,7 @@ in_grace_period:
 /*
  * Generic NLM call, async version.
  */
-int
-nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+int nlmsvc_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
 {
        struct nlm_host *host = req->a_host;
        struct rpc_clnt *clnt;
@@ -419,13 +420,12 @@ nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
        msg.rpc_proc = &clnt->cl_procinfo[proc];
 
         /* bootstrap and kick off the async RPC call */
-        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
+        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req);
 
        return status;
 }
 
-int
-nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+static int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
 {
        struct nlm_host *host = req->a_host;
        struct rpc_clnt *clnt;
@@ -448,7 +448,7 @@ nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
        /* Increment host refcount */
        nlm_get_host(host);
         /* bootstrap and kick off the async RPC call */
-        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
+        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req);
        if (status < 0)
                nlm_release_host(host);
        return status;
@@ -509,6 +509,24 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho
        fl->fl_ops = &nlmclnt_lock_ops;
 }
 
+static void do_vfs_lock(struct file_lock *fl)
+{
+       int res = 0;
+       switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
+               case FL_POSIX:
+                       res = posix_lock_file_wait(fl->fl_file, fl);
+                       break;
+               case FL_FLOCK:
+                       res = flock_lock_file_wait(fl->fl_file, fl);
+                       break;
+               default:
+                       BUG();
+       }
+       if (res < 0)
+               printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
+                               __FUNCTION__);
+}
+
 /*
  * LOCK: Try to create a lock
  *
@@ -534,7 +552,8 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
 {
        struct nlm_host *host = req->a_host;
        struct nlm_res  *resp = &req->a_res;
-       int             status;
+       long timeout;
+       int status;
 
        if (!host->h_monitored && nsm_monitor(host) < 0) {
                printk(KERN_NOTICE "lockd: failed to monitor %s\n",
@@ -543,25 +562,45 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
                goto out;
        }
 
-       do {
-               if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
-                       if (resp->status != NLM_LCK_BLOCKED)
-                               break;
-                       status = nlmclnt_block(host, fl, &resp->status);
-               }
+       if (req->a_args.block) {
+               status = nlmclnt_prepare_block(req, host, fl);
                if (status < 0)
                        goto out;
-       } while (resp->status == NLM_LCK_BLOCKED && req->a_args.block);
+       }
+       for(;;) {
+               status = nlmclnt_call(req, NLMPROC_LOCK);
+               if (status < 0)
+                       goto out_unblock;
+               if (resp->status != NLM_LCK_BLOCKED)
+                       break;
+               /* Wait on an NLM blocking lock */
+               timeout = nlmclnt_block(req, NLMCLNT_POLL_TIMEOUT);
+               /* Did a reclaimer thread notify us of a server reboot? */
+               if (resp->status ==  NLM_LCK_DENIED_GRACE_PERIOD)
+                       continue;
+               if (resp->status != NLM_LCK_BLOCKED)
+                       break;
+               if (timeout >= 0)
+                       continue;
+               /* We were interrupted. Send a CANCEL request to the server
+                * and exit
+                */
+               status = (int)timeout;
+               goto out_unblock;
+       }
 
        if (resp->status == NLM_LCK_GRANTED) {
                fl->fl_u.nfs_fl.state = host->h_state;
                fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
                fl->fl_flags |= FL_SLEEP;
-               if (posix_lock_file_wait(fl->fl_file, fl) < 0)
-                               printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
-                                               __FUNCTION__);
+               do_vfs_lock(fl);
        }
        status = nlm_stat_to_errno(resp->status);
+out_unblock:
+       nlmclnt_finish_block(req);
+       /* Cancel the blocked request if it is still pending */
+       if (resp->status == NLM_LCK_BLOCKED)
+               nlmclnt_cancel(host, req->a_args.block, fl);
 out:
        nlmclnt_release_lockargs(req);
        return status;
@@ -587,25 +626,9 @@ nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
        nlmclnt_setlockargs(req, fl);
        req->a_args.reclaim = 1;
 
-again:
-       switch ((status = nlmclnt_call(req, NLMPROC_LOCK))) {
-       case 0:
-               if (req->a_res.status == NLM_LCK_GRANTED)
-                       return 0;
-               break;
-       case -EAGAIN:
-       case -EACCES: /* portmapper might be up, but lockd isn't */
-               current->state = TASK_INTERRUPTIBLE;
-               schedule_timeout(10*HZ);
-               if (signalled()) {
-                       status = -EINTR;
-                       dprintk("lockd: reclaim got interrupted!\n");
-                       break;
-               }
-               goto again;
-       default:
-               break;
-       }
+       if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
+        && req->a_res.status == NLM_LCK_GRANTED)
+               return 0;
 
        printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
                                "(errno %d, status %d)\n", fl->fl_pid,
@@ -639,12 +662,18 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
         * reclaimed while we're stuck in the unlock call. */
        fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
 
+       /*
+        * Note: the server is supposed to either grant us the unlock
+        * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either
+        * case, we want to unlock.
+        */
+       do_vfs_lock(fl);
+
        if (req->a_flags & RPC_TASK_ASYNC) {
                status = nlmclnt_async_call(req, NLMPROC_UNLOCK,
-                                       nlmclnt_unlock_callback);
+                                       &nlmclnt_unlock_ops);
                /* Hrmf... Do the unlock early since locks_remove_posix()
                 * really expects us to free the lock synchronously */
-               posix_lock_file(fl->fl_file, fl);
                if (status < 0) {
                        nlmclnt_release_lockargs(req);
                        kfree(req);
@@ -657,7 +686,6 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
        if (status < 0)
                return status;
 
-       posix_lock_file(fl->fl_file, fl);
        if (resp->status == NLM_LCK_GRANTED)
                return 0;
 
@@ -669,10 +697,9 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
        return -ENOLCK;
 }
 
-static void
-nlmclnt_unlock_callback(struct rpc_task *task)
+static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
 {
-       struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
+       struct nlm_rqst *req = data;
        int             status = req->a_res.status;
 
        if (RPC_ASSASSINATED(task))
@@ -699,13 +726,16 @@ die:
        rpc_restart_call(task);
 }
 
+static const struct rpc_call_ops nlmclnt_unlock_ops = {
+       .rpc_call_done = nlmclnt_unlock_callback,
+};
+
 /*
  * Cancel a blocked lock request.
  * We always use an async RPC call for this in order not to hang a
  * process that has been Ctrl-C'ed.
  */
-int
-nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl)
+static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl)
 {
        struct nlm_rqst *req;
        unsigned long   flags;
@@ -726,9 +756,9 @@ nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl)
        req->a_flags = RPC_TASK_ASYNC;
 
        nlmclnt_setlockargs(req, fl);
+       req->a_args.block = block;
 
-       status = nlmclnt_async_call(req, NLMPROC_CANCEL,
-                                       nlmclnt_cancel_callback);
+       status = nlmclnt_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops);
        if (status < 0) {
                nlmclnt_release_lockargs(req);
                kfree(req);
@@ -742,10 +772,9 @@ nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl)
        return status;
 }
 
-static void
-nlmclnt_cancel_callback(struct rpc_task *task)
+static void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
 {
-       struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
+       struct nlm_rqst *req = data;
 
        if (RPC_ASSASSINATED(task))
                goto die;
@@ -779,11 +808,18 @@ die:
        return;
 
 retry_cancel:
+       /* Don't ever retry more than 3 times */
+       if (req->a_retries++ >= NLMCLNT_MAX_RETRIES)
+               goto die;
        nlm_rebind_host(req->a_host);
        rpc_restart_call(task);
        rpc_delay(task, 30 * HZ);
 }
 
+static const struct rpc_call_ops nlmclnt_cancel_ops = {
+       .rpc_call_done = nlmclnt_cancel_callback,
+};
+
 /*
  * Convert an NLM status code to a generic kernel errno
  */