nilfs2: fix lock order reversal in nilfs_clean_segments ioctl
[safe/jmp/linux-2.6] / fs / nfs / delegation.c
index 4692fdc..968225a 100644 (file)
@@ -43,6 +43,27 @@ 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;
@@ -188,6 +209,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);
@@ -321,6 +343,12 @@ int nfs_inode_return_delegation(struct inode *inode)
        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
  */
@@ -354,37 +382,16 @@ static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
        rcu_read_unlock();
 }
 
-static int nfs_do_expire_all_delegations(void *ptr)
+static void nfs_delegation_run_state_manager(struct nfs_client *clp)
 {
-       struct nfs_client *clp = ptr;
-
-       allow_signal(SIGKILL);
-
-       if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
-               goto out;
-       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0)
-               goto out;
-       nfs_client_mark_return_all_delegations(clp);
-       nfs_client_return_marked_delegations(clp);
-out:
-       nfs_put_client(clp);
-       module_put_and_exit(0);
+       if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
+               nfs4_schedule_state_manager(clp);
 }
 
 void nfs_expire_all_delegations(struct nfs_client *clp)
 {
-       struct task_struct *task;
-
-       __module_get(THIS_MODULE);
-       atomic_inc(&clp->cl_count);
-       task = kthread_run(nfs_do_expire_all_delegations, clp,
-                               "%s-delegreturn",
-                               rpc_peeraddr2str(clp->cl_rpcclient,
-                                                       RPC_DISPLAY_ADDR));
-       if (!IS_ERR(task))
-               return;
-       nfs_put_client(clp);
-       module_put(THIS_MODULE);
+       nfs_client_mark_return_all_delegations(clp);
+       nfs_delegation_run_state_manager(clp);
 }
 
 /*
@@ -397,43 +404,24 @@ 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;
+               set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+               set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+       }
+       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);
 }
 
 /*
@@ -441,22 +429,20 @@ static int recall_thread(void *data)
  */
 int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
 {
-       struct recall_threadargs data = {
-               .inode = inode,
-               .stateid = stateid,
-       };
-       int status;
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_delegation *delegation;
 
-       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;
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data,
+                               sizeof(delegation->stateid.data)) != 0) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+       nfs_mark_return_delegation(clp, delegation);
+       rcu_read_unlock();
+       nfs_delegation_run_state_manager(clp);
+       return 0;
 }
 
 /*