]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
NFS: Properly handle the case where the delegation is revoked
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 6 Mar 2012 00:56:44 +0000 (19:56 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 2 Apr 2012 16:52:49 +0000 (09:52 -0700)
commit a1d0b5eebc4fd6e0edb02688b35f17f67f42aea5 upstream.

If we know that the delegation stateid is bad or revoked, we need to
remove that delegation as soon as possible, and then mark all the
stateids that relied on that delegation for recovery. We cannot use
the delegation as part of the recovery process.

Also note that NFSv4.1 uses a different error code (NFS4ERR_DELEG_REVOKED)
to indicate that the delegation was revoked.

Finally, ensure that setlk() and setattr() can both recover safely from
a revoked delegation.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c

index 7f2654069806f011c4361900041ef6ae64f17de4..ac889af8ccf5c68188a9b685fcda272fc47b7a03 100644 (file)
@@ -466,6 +466,17 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp)
                nfs4_schedule_state_manager(clp);
 }
 
+void nfs_remove_bad_delegation(struct inode *inode)
+{
+       struct nfs_delegation *delegation;
+
+       delegation = nfs_detach_delegation(NFS_I(inode), NFS_SERVER(inode));
+       if (delegation) {
+               nfs_inode_find_state_and_recover(inode, &delegation->stateid);
+               nfs_free_delegation(delegation);
+       }
+}
+
 /**
  * nfs_expire_all_delegation_types
  * @clp: client to process
index d9322e490c56ff98a39e79295186653e0e80589e..691a796091842bfcde8de961a8d1b97a1190cd70 100644 (file)
@@ -45,6 +45,7 @@ void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
 void nfs_handle_cb_pathdown(struct nfs_client *clp);
 int nfs_client_return_marked_delegations(struct nfs_client *clp);
 int nfs_delegations_present(struct nfs_client *clp);
+void nfs_remove_bad_delegation(struct inode *inode);
 
 void nfs_delegation_mark_reclaim(struct nfs_client *clp);
 void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
index 693ae22f873194d0c36adc384d7cb05fefbc8b6c..d6ba137f185b14b0a486a3324c8aeba5c8e44f5b 100644 (file)
@@ -324,6 +324,8 @@ extern void nfs4_put_open_state(struct nfs4_state *);
 extern void nfs4_close_state(struct nfs4_state *, fmode_t);
 extern void nfs4_close_sync(struct nfs4_state *, fmode_t);
 extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
+extern void nfs_inode_find_state_and_recover(struct inode *inode,
+               const nfs4_stateid *stateid);
 extern void nfs4_schedule_lease_recovery(struct nfs_client *);
 extern void nfs4_schedule_state_manager(struct nfs_client *);
 extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp);
index e52703080c6d40e955c041d80637f50ddc30ffe3..a51e1ddb0a23821209290bd3e5ced60f73c948df 100644 (file)
@@ -263,8 +263,11 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
        switch(errorcode) {
                case 0:
                        return 0;
+               case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_BAD_STATEID:
+                       if (state != NULL)
+                               nfs_remove_bad_delegation(state->inode);
                case -NFS4ERR_OPENMODE:
                        if (state == NULL)
                                break;
@@ -1316,8 +1319,11 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
                                 * The show must go on: exit, but mark the
                                 * stateid as needing recovery.
                                 */
+                       case -NFS4ERR_DELEG_REVOKED:
                        case -NFS4ERR_ADMIN_REVOKED:
                        case -NFS4ERR_BAD_STATEID:
+                               nfs_inode_find_state_and_recover(state->inode,
+                                               stateid);
                                nfs4_schedule_stateid_recovery(server, state);
                        case -EKEYEXPIRED:
                                /*
@@ -1893,7 +1899,9 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                           struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(inode);
-       struct nfs4_exception exception = { };
+       struct nfs4_exception exception = {
+               .state = state,
+       };
        int err;
        do {
                err = nfs4_handle_exception(server,
@@ -3707,8 +3715,11 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
        if (task->tk_status >= 0)
                return 0;
        switch(task->tk_status) {
+               case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_BAD_STATEID:
+                       if (state != NULL)
+                               nfs_remove_bad_delegation(state->inode);
                case -NFS4ERR_OPENMODE:
                        if (state == NULL)
                                break;
@@ -4526,7 +4537,9 @@ out:
 
 static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
-       struct nfs4_exception exception = { };
+       struct nfs4_exception exception = {
+               .state = state,
+       };
        int err;
 
        do {
@@ -4619,6 +4632,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
                                 * The show must go on: exit, but mark the
                                 * stateid as needing recovery.
                                 */
+                       case -NFS4ERR_DELEG_REVOKED:
                        case -NFS4ERR_ADMIN_REVOKED:
                        case -NFS4ERR_BAD_STATEID:
                        case -NFS4ERR_OPENMODE:
index a58eed784aed9e542292982fba7133aec0036cf3..0596fd65b88410c23ca15b6448cbeeec3a0f0506 100644 (file)
@@ -1071,12 +1071,37 @@ void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4
 {
        struct nfs_client *clp = server->nfs_client;
 
-       if (test_and_clear_bit(NFS_DELEGATED_STATE, &state->flags))
-               nfs_async_inode_return_delegation(state->inode, &state->stateid);
        nfs4_state_mark_reclaim_nograce(clp, state);
        nfs4_schedule_state_manager(clp);
 }
 
+void nfs_inode_find_state_and_recover(struct inode *inode,
+               const nfs4_stateid *stateid)
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_open_context *ctx;
+       struct nfs4_state *state;
+       bool found = false;
+
+       spin_lock(&inode->i_lock);
+       list_for_each_entry(ctx, &nfsi->open_files, list) {
+               state = ctx->state;
+               if (state == NULL)
+                       continue;
+               if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
+                       continue;
+               if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
+                       continue;
+               nfs4_state_mark_reclaim_nograce(clp, state);
+               found = true;
+       }
+       spin_unlock(&inode->i_lock);
+       if (found)
+               nfs4_schedule_state_manager(clp);
+}
+
+
 static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
 {
        struct inode *inode = state->inode;