Merge branch 'writeback-for-2.6.34' into nfs-for-2.6.34
[safe/jmp/linux-2.6] / fs / nfs / delegation.c
index 21eda6c..2563beb 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/kthread.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/smp_lock.h>
 #include <linux/spinlock.h>
 
 #include <linux/nfs4.h>
@@ -43,37 +44,55 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
                put_rpccred(cred);
 }
 
+void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
+}
+
+int nfs_have_delegation(struct inode *inode, fmode_t flags)
+{
+       struct nfs_delegation *delegation;
+       int ret = 0;
+
+       flags &= FMODE_READ|FMODE_WRITE;
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation != NULL && (delegation->type & flags) == flags) {
+               nfs_mark_delegation_referenced(delegation);
+               ret = 1;
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
 static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
 {
        struct inode *inode = state->inode;
        struct file_lock *fl;
-       int status;
+       int status = 0;
+
+       if (inode->i_flock == NULL)
+               goto out;
 
+       /* Protect inode->i_flock using the BKL */
+       lock_kernel();
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
                        continue;
                if (nfs_file_open_context(fl->fl_file) != ctx)
                        continue;
+               unlock_kernel();
                status = nfs4_lock_delegation_recall(state, fl);
-               if (status >= 0)
-                       continue;
-               switch (status) {
-                       default:
-                               printk(KERN_ERR "%s: unhandled error %d.\n",
-                                               __func__, status);
-                       case -NFS4ERR_EXPIRED:
-                               /* kill_proc(fl->fl_pid, SIGLOST, 1); */
-                       case -NFS4ERR_STALE_CLIENTID:
-                               nfs4_schedule_state_recovery(NFS_SERVER(inode)->nfs_client);
-                               goto out_err;
-               }
+               if (status < 0)
+                       goto out;
+               lock_kernel();
        }
-       return 0;
-out_err:
+       unlock_kernel();
+out:
        return status;
 }
 
-static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
+static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_open_context *ctx;
@@ -97,10 +116,11 @@ again:
                        err = nfs_delegation_claim_locks(ctx, state);
                put_nfs_open_context(ctx);
                if (err != 0)
-                       return;
+                       return err;
                goto again;
        }
        spin_unlock(&inode->i_lock);
+       return 0;
 }
 
 /*
@@ -188,6 +208,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
        delegation->change_attr = nfsi->change_attr;
        delegation->cred = get_rpccred(cred);
        delegation->inode = inode;
+       delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
        spin_lock_init(&delegation->lock);
 
        spin_lock(&clp->cl_lock);
@@ -241,27 +262,34 @@ static void nfs_msync_inode(struct inode *inode)
 /*
  * Basic procedure for returning a delegation to the server
  */
-static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
+static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
+       int err;
 
-       nfs_msync_inode(inode);
-       /* Guard against new delegated open calls */
+       /*
+        * Guard against new delegated open/lock/unlock calls and against
+        * state recovery
+        */
        down_write(&nfsi->rwsem);
-       nfs_delegation_claim_opens(inode, &delegation->stateid);
+       err = nfs_delegation_claim_opens(inode, &delegation->stateid);
        up_write(&nfsi->rwsem);
-       nfs_msync_inode(inode);
+       if (err)
+               goto out;
 
-       return nfs_do_return_delegation(inode, delegation, 1);
+       err = nfs_do_return_delegation(inode, delegation, issync);
+out:
+       return err;
 }
 
 /*
  * Return all delegations that have been marked for return
  */
-void nfs_client_return_marked_delegations(struct nfs_client *clp)
+int nfs_client_return_marked_delegations(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation;
        struct inode *inode;
+       int err = 0;
 
 restart:
        rcu_read_lock();
@@ -275,12 +303,18 @@ restart:
                delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
                spin_unlock(&clp->cl_lock);
                rcu_read_unlock();
-               if (delegation != NULL)
-                       __nfs_inode_return_delegation(inode, delegation);
+               if (delegation != NULL) {
+                       filemap_flush(inode->i_mapping);
+                       err = __nfs_inode_return_delegation(inode, delegation, 0);
+               }
                iput(inode);
-               goto restart;
+               if (!err)
+                       goto restart;
+               set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+               return err;
        }
        rcu_read_unlock();
+       return 0;
 }
 
 /*
@@ -315,12 +349,20 @@ int nfs_inode_return_delegation(struct inode *inode)
                spin_lock(&clp->cl_lock);
                delegation = nfs_detach_delegation_locked(nfsi, NULL);
                spin_unlock(&clp->cl_lock);
-               if (delegation != NULL)
-                       err = __nfs_inode_return_delegation(inode, delegation);
+               if (delegation != NULL) {
+                       nfs_msync_inode(inode);
+                       err = __nfs_inode_return_delegation(inode, delegation, 1);
+               }
        }
        return err;
 }
 
+static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+       set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+}
+
 /*
  * Return all delegations associated to a super block
  */
@@ -339,33 +381,47 @@ void nfs_super_return_all_delegations(struct super_block *sb)
                spin_unlock(&delegation->lock);
        }
        rcu_read_unlock();
-       nfs_client_return_marked_delegations(clp);
+       if (nfs_client_return_marked_delegations(clp) != 0)
+               nfs4_schedule_state_manager(clp);
 }
 
-static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
+static
+void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, fmode_t flags)
 {
        struct nfs_delegation *delegation;
 
        rcu_read_lock();
        list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
-               set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
-               set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+               if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
+                       continue;
+               if (delegation->type & flags)
+                       nfs_mark_return_delegation(clp, delegation);
        }
        rcu_read_unlock();
 }
 
+static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
+{
+       nfs_client_mark_return_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
+}
+
 static void nfs_delegation_run_state_manager(struct nfs_client *clp)
 {
        if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
                nfs4_schedule_state_manager(clp);
 }
 
-void nfs_expire_all_delegations(struct nfs_client *clp)
+void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
 {
-       nfs_client_mark_return_all_delegations(clp);
+       nfs_client_mark_return_all_delegation_types(clp, flags);
        nfs_delegation_run_state_manager(clp);
 }
 
+void nfs_expire_all_delegations(struct nfs_client *clp)
+{
+       nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
+}
+
 /*
  * Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
  */
@@ -376,66 +432,47 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp)
        nfs_client_mark_return_all_delegations(clp);
 }
 
-struct recall_threadargs {
-       struct inode *inode;
-       struct nfs_client *clp;
-       const nfs4_stateid *stateid;
-
-       struct completion started;
-       int result;
-};
-
-static int recall_thread(void *data)
+static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp)
 {
-       struct recall_threadargs *args = (struct recall_threadargs *)data;
-       struct inode *inode = igrab(args->inode);
-       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
-       struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_delegation *delegation;
 
-       daemonize("nfsv4-delegreturn");
-
-       nfs_msync_inode(inode);
-       down_write(&nfsi->rwsem);
-       spin_lock(&clp->cl_lock);
-       delegation = nfs_detach_delegation_locked(nfsi, args->stateid);
-       if (delegation != NULL)
-               args->result = 0;
-       else
-               args->result = -ENOENT;
-       spin_unlock(&clp->cl_lock);
-       complete(&args->started);
-       nfs_delegation_claim_opens(inode, args->stateid);
-       up_write(&nfsi->rwsem);
-       nfs_msync_inode(inode);
+       rcu_read_lock();
+       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
+               if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
+                       continue;
+               nfs_mark_return_delegation(clp, delegation);
+       }
+       rcu_read_unlock();
+}
 
-       if (delegation != NULL)
-               nfs_do_return_delegation(inode, delegation, 1);
-       iput(inode);
-       module_put_and_exit(0);
+void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
+{
+       nfs_client_mark_return_unreferenced_delegations(clp);
+       nfs_delegation_run_state_manager(clp);
 }
 
 /*
  * Asynchronous delegation recall!
  */
-int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
-{
-       struct recall_threadargs data = {
-               .inode = inode,
-               .stateid = stateid,
-       };
-       int status;
-
-       init_completion(&data.started);
-       __module_get(THIS_MODULE);
-       status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
-       if (status < 0)
-               goto out_module_put;
-       wait_for_completion(&data.started);
-       return data.result;
-out_module_put:
-       module_put(THIS_MODULE);
-       return status;
+int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid,
+                                     int (*validate_stateid)(struct nfs_delegation *delegation,
+                                                             const nfs4_stateid *stateid))
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_delegation *delegation;
+
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+
+       if (!validate_stateid(delegation, stateid)) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+
+       nfs_mark_return_delegation(clp, delegation);
+       rcu_read_unlock();
+       nfs_delegation_run_state_manager(clp);
+       return 0;
 }
 
 /*