diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index ebc06f2da31a..bc9d4f9a788d 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -255,6 +255,34 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat return nfs_do_return_delegation(inode, delegation, 1); } +/* + * Return all delegations that have been marked for return + */ +static void nfs_client_return_marked_delegations(struct nfs_client *clp) +{ + struct nfs_delegation *delegation; + struct inode *inode; + +restart: + rcu_read_lock(); + list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { + if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) + continue; + inode = nfs_delegation_grab_inode(delegation); + if (inode == NULL) + continue; + spin_lock(&clp->cl_lock); + 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); + iput(inode); + goto restart; + } + rcu_read_unlock(); +} + /* * This function returns the delegation without reclaiming opens * or protecting against delegation reclaims. @@ -296,63 +324,46 @@ int nfs_inode_return_delegation(struct inode *inode) /* * Return all delegations associated to a super block */ -void nfs_return_all_delegations(struct super_block *sb) +void nfs_super_return_all_delegations(struct super_block *sb) { struct nfs_client *clp = NFS_SB(sb)->nfs_client; struct nfs_delegation *delegation; - struct inode *inode; if (clp == NULL) return; -restart: rcu_read_lock(); list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { - inode = NULL; spin_lock(&delegation->lock); if (delegation->inode != NULL && delegation->inode->i_sb == sb) - inode = igrab(delegation->inode); + set_bit(NFS_DELEGATION_RETURN, &delegation->flags); spin_unlock(&delegation->lock); - if (inode == NULL) - continue; - spin_lock(&clp->cl_lock); - 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); - iput(inode); - goto restart; } rcu_read_unlock(); + nfs_client_return_marked_delegations(clp); +} + +static void nfs_client_return_all_delegations(struct nfs_client *clp) +{ + 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); + rcu_read_unlock(); + nfs_client_return_marked_delegations(clp); } static int nfs_do_expire_all_delegations(void *ptr) { struct nfs_client *clp = ptr; - struct nfs_delegation *delegation; - struct inode *inode; allow_signal(SIGKILL); -restart: + if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0) goto out; if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) goto out; - rcu_read_lock(); - list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { - inode = nfs_delegation_grab_inode(delegation); - if (inode == NULL) - continue; - spin_lock(&clp->cl_lock); - delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); - spin_unlock(&clp->cl_lock); - rcu_read_unlock(); - if (delegation) - __nfs_inode_return_delegation(inode, delegation); - iput(inode); - goto restart; - } - rcu_read_unlock(); + nfs_client_return_all_delegations(clp); out: nfs_put_client(clp); module_put_and_exit(0); @@ -379,27 +390,9 @@ void nfs_expire_all_delegations(struct nfs_client *clp) */ void nfs_handle_cb_pathdown(struct nfs_client *clp) { - struct nfs_delegation *delegation; - struct inode *inode; - if (clp == NULL) return; -restart: - rcu_read_lock(); - list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { - inode = nfs_delegation_grab_inode(delegation); - if (inode == NULL) - continue; - spin_lock(&clp->cl_lock); - 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); - iput(inode); - goto restart; - } - rcu_read_unlock(); + nfs_client_return_all_delegations(clp); } struct recall_threadargs { diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 5e9f40e0a7d8..c772bab12eea 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -20,12 +20,16 @@ struct nfs_delegation { int type; loff_t maxsize; __u64 change_attr; -#define NFS_DELEGATION_NEED_RECLAIM 0 unsigned long flags; spinlock_t lock; struct rcu_head rcu; }; +enum { + NFS_DELEGATION_NEED_RECLAIM = 0, + NFS_DELEGATION_RETURN, +}; + int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); int nfs_inode_return_delegation(struct inode *inode); @@ -33,7 +37,7 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s void nfs_inode_return_delegation_noreclaim(struct inode *inode); struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); -void nfs_return_all_delegations(struct super_block *sb); +void nfs_super_return_all_delegations(struct super_block *sb); void nfs_expire_all_delegations(struct nfs_client *clp); void nfs_handle_cb_pathdown(struct nfs_client *clp); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index d8e062fe76b1..bd4c3dd550df 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2433,7 +2433,7 @@ static void nfs4_kill_super(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); - nfs_return_all_delegations(sb); + nfs_super_return_all_delegations(sb); kill_anon_super(sb); nfs4_renewd_prepare_shutdown(server);