Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[safe/jmp/linux-2.6] / fs / lockd / clntproc.c
index ea1a694..7932c39 100644 (file)
@@ -7,6 +7,8 @@
  */
 
 #include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -16,7 +18,6 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
-#include <linux/lockd/sm_inter.h>
 
 #define NLMDBG_FACILITY                NLMDBG_CLIENT
 #define NLMCLNT_GRACE_WAIT     (5*HZ)
@@ -127,7 +128,6 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
        struct nlm_lock *lock = &argp->lock;
 
        nlmclnt_next_cookie(&argp->cookie);
-       argp->state   = nsm_local_state;
        memcpy(&lock->fh, NFS_FH(fl->fl_file->f_path.dentry->d_inode), sizeof(struct nfs_fh));
        lock->caller  = utsname()->nodename;
        lock->oh.data = req->a_owner;
@@ -166,6 +166,7 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
        /* Set up the argument struct */
        nlmclnt_setlockargs(call, fl);
 
+       lock_kernel();
        if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
                if (fl->fl_type != F_UNLCK) {
                        call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
@@ -179,6 +180,7 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
 
        fl->fl_ops->fl_release_private(fl);
        fl->fl_ops = NULL;
+       unlock_kernel();
 
        dprintk("lockd: clnt proc returns %d\n", status);
        return status;
@@ -224,7 +226,9 @@ void nlm_release_call(struct nlm_rqst *call)
 
 static void nlmclnt_rpc_release(void *data)
 {
+       lock_kernel();
        nlm_release_call(data);
+       unlock_kernel();
 }
 
 static int nlm_wait_on_grace(wait_queue_head_t *queue)
@@ -247,7 +251,7 @@ static int nlm_wait_on_grace(wait_queue_head_t *queue)
  * Generic NLM call
  */
 static int
-nlmclnt_call(struct nlm_rqst *req, u32 proc)
+nlmclnt_call(struct rpc_cred *cred, struct nlm_rqst *req, u32 proc)
 {
        struct nlm_host *host = req->a_host;
        struct rpc_clnt *clnt;
@@ -256,6 +260,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc)
        struct rpc_message msg = {
                .rpc_argp       = argp,
                .rpc_resp       = resp,
+               .rpc_cred       = cred,
        };
        int             status;
 
@@ -390,11 +395,12 @@ int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *t
  *      completion in order to be able to correctly track the lock
  *      state.
  */
-static int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
+static int nlmclnt_async_call(struct rpc_cred *cred, struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
 {
        struct rpc_message msg = {
                .rpc_argp       = &req->a_args,
                .rpc_resp       = &req->a_res,
+               .rpc_cred       = cred,
        };
        struct rpc_task *task;
        int err;
@@ -415,7 +421,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
 {
        int     status;
 
-       status = nlmclnt_call(req, NLMPROC_TEST);
+       status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_TEST);
        if (status < 0)
                goto out;
 
@@ -428,7 +434,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
                         * Report the conflicting lock back to the application.
                         */
                        fl->fl_start = req->a_res.lock.fl.fl_start;
-                       fl->fl_end = req->a_res.lock.fl.fl_start;
+                       fl->fl_end = req->a_res.lock.fl.fl_end;
                        fl->fl_type = req->a_res.lock.fl.fl_type;
                        fl->fl_pid = 0;
                        break;
@@ -453,7 +459,7 @@ static void nlmclnt_locks_release_private(struct file_lock *fl)
        nlm_put_lockowner(fl->fl_u.nfs_fl.owner);
 }
 
-static struct file_lock_operations nlmclnt_lock_ops = {
+static const struct file_lock_operations nlmclnt_lock_ops = {
        .fl_copy_lock = nlmclnt_locks_copy_lock,
        .fl_release_private = nlmclnt_locks_release_private,
 };
@@ -506,17 +512,18 @@ static int do_vfs_lock(struct file_lock *fl)
 static int
 nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
 {
+       struct rpc_cred *cred = nfs_file_cred(fl->fl_file);
        struct nlm_host *host = req->a_host;
        struct nlm_res  *resp = &req->a_res;
        struct nlm_wait *block = NULL;
        unsigned char fl_flags = fl->fl_flags;
+       unsigned char fl_type;
        int status = -ENOLCK;
 
-       if (nsm_monitor(host) < 0) {
-               printk(KERN_NOTICE "lockd: failed to monitor %s\n",
-                                       host->h_name);
+       if (nsm_monitor(host) < 0)
                goto out;
-       }
+       req->a_args.state = nsm_local_state;
+
        fl->fl_flags |= FL_ACCESS;
        status = do_vfs_lock(fl);
        fl->fl_flags = fl_flags;
@@ -525,13 +532,16 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
 
        block = nlmclnt_prepare_block(host, fl);
 again:
+       /*
+        * Initialise resp->status to a valid non-zero value,
+        * since 0 == nlm_lck_granted
+        */
+       resp->status = nlm_lck_blocked;
        for(;;) {
                /* Reboot protection */
                fl->fl_u.nfs_fl.state = host->h_state;
-               status = nlmclnt_call(req, NLMPROC_LOCK);
+               status = nlmclnt_call(cred, req, NLMPROC_LOCK);
                if (status < 0)
-                       goto out_unblock;
-               if (!req->a_args.block)
                        break;
                /* Did a reclaimer thread notify us of a server reboot? */
                if (resp->status ==  nlm_lck_denied_grace_period)
@@ -540,15 +550,22 @@ again:
                        break;
                /* Wait on an NLM blocking lock */
                status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
-               /* if we were interrupted. Send a CANCEL request to the server
-                * and exit
-                */
                if (status < 0)
-                       goto out_unblock;
+                       break;
                if (resp->status != nlm_lck_blocked)
                        break;
        }
 
+       /* if we were interrupted while blocking, then cancel the lock request
+        * and exit
+        */
+       if (resp->status == nlm_lck_blocked) {
+               if (!req->a_args.block)
+                       goto out_unlock;
+               if (nlmclnt_cancel(host, req->a_args.block, fl) == 0)
+                       goto out_unblock;
+       }
+
        if (resp->status == nlm_granted) {
                down_read(&host->h_rwsem);
                /* Check whether or not the server has rebooted */
@@ -559,19 +576,41 @@ again:
                /* Ensure the resulting lock will get added to granted list */
                fl->fl_flags |= FL_SLEEP;
                if (do_vfs_lock(fl) < 0)
-                       printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
+                       printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
                up_read(&host->h_rwsem);
                fl->fl_flags = fl_flags;
+               status = 0;
        }
-       status = nlm_stat_to_errno(resp->status);
+       if (status < 0)
+               goto out_unlock;
+       /*
+        * EAGAIN doesn't make sense for sleeping locks, and in some
+        * cases NLM_LCK_DENIED is returned for a permanent error.  So
+        * turn it into an ENOLCK.
+        */
+       if (resp->status == nlm_lck_denied && (fl_flags & FL_SLEEP))
+               status = -ENOLCK;
+       else
+               status = nlm_stat_to_errno(resp->status);
 out_unblock:
        nlmclnt_finish_block(block);
-       /* Cancel the blocked request if it is still pending */
-       if (resp->status == nlm_lck_blocked)
-               nlmclnt_cancel(host, req->a_args.block, fl);
 out:
        nlm_release_call(req);
        return status;
+out_unlock:
+       /* Fatal error: ensure that we remove the lock altogether */
+       dprintk("lockd: lock attempt ended in fatal error.\n"
+               "       Attempting to unlock.\n");
+       nlmclnt_finish_block(block);
+       fl_type = fl->fl_type;
+       fl->fl_type = F_UNLCK;
+       down_read(&host->h_rwsem);
+       do_vfs_lock(fl);
+       up_read(&host->h_rwsem);
+       fl->fl_type = fl_type;
+       fl->fl_flags = fl_flags;
+       nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
+       return status;
 }
 
 /*
@@ -594,8 +633,8 @@ nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
        nlmclnt_setlockargs(req, fl);
        req->a_args.reclaim = 1;
 
-       if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
-        && req->a_res.status == nlm_granted)
+       status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_LOCK);
+       if (status >= 0 && req->a_res.status == nlm_granted)
                return 0;
 
        printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
@@ -644,7 +683,8 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
        }
 
        atomic_inc(&req->a_count);
-       status = nlmclnt_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
+       status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,
+                       NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
        if (status < 0)
                goto out;
 
@@ -681,7 +721,9 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
 die:
        return;
  retry_rebind:
+       lock_kernel();
        nlm_rebind_host(req->a_host);
+       unlock_kernel();
  retry_unlock:
        rpc_restart_call(task);
 }
@@ -713,7 +755,8 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
        req->a_args.block = block;
 
        atomic_inc(&req->a_count);
-       status = nlmclnt_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops);
+       status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,
+                       NLMPROC_CANCEL, &nlmclnt_cancel_ops);
        if (status == 0 && req->a_res.status == nlm_lck_denied)
                status = -ENOLCK;
        nlm_release_call(req);
@@ -758,7 +801,9 @@ retry_cancel:
        /* Don't ever retry more than 3 times */
        if (req->a_retries++ >= NLMCLNT_MAX_RETRIES)
                goto die;
+       lock_kernel();
        nlm_rebind_host(req->a_host);
+       unlock_kernel();
        rpc_restart_call(task);
        rpc_delay(task, 30 * HZ);
 }