Commit af3b61bf authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Clean up nfs_client_return_marked_delegations()

Convert it to use the nfs_client_for_each_server() helper, and
make it more efficient by skipping delegations for inodes we
know are in the process of being freed. Also improve the efficiency
of the cursor by skipping delegations that are being freed.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent 3c9e502b
...@@ -563,21 +563,11 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation) ...@@ -563,21 +563,11 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
return ret; return ret;
} }
/** static int nfs_server_return_marked_delegations(struct nfs_server *server,
* nfs_client_return_marked_delegations - return previously marked delegations void __always_unused *data)
* @clp: nfs_client to process
*
* Note that this function is designed to be called by the state
* manager thread. For this reason, it cannot flush the dirty data,
* since that could deadlock in case of a state recovery error.
*
* Returns zero on success, or a negative errno value.
*/
int nfs_client_return_marked_delegations(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct nfs_delegation *prev; struct nfs_delegation *prev;
struct nfs_server *server;
struct inode *inode; struct inode *inode;
struct inode *place_holder = NULL; struct inode *place_holder = NULL;
struct nfs_delegation *place_holder_deleg = NULL; struct nfs_delegation *place_holder_deleg = NULL;
...@@ -587,78 +577,79 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp) ...@@ -587,78 +577,79 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp)
/* /*
* To avoid quadratic looping we hold a reference * To avoid quadratic looping we hold a reference
* to an inode place_holder. Each time we restart, we * to an inode place_holder. Each time we restart, we
* list nfs_servers from the server of that inode, and * list delegation in the server from the delegations
* delegation in the server from the delegations of that * of that inode.
* inode.
* prev is an RCU-protected pointer to a delegation which * prev is an RCU-protected pointer to a delegation which
* wasn't marked for return and might be a good choice for * wasn't marked for return and might be a good choice for
* the next place_holder. * the next place_holder.
*/ */
rcu_read_lock();
prev = NULL; prev = NULL;
delegation = NULL;
rcu_read_lock();
if (place_holder) if (place_holder)
server = NFS_SERVER(place_holder); delegation = rcu_dereference(NFS_I(place_holder)->delegation);
else if (!delegation || delegation != place_holder_deleg)
server = list_entry_rcu(clp->cl_superblocks.next, delegation = list_entry_rcu(server->delegations.next,
struct nfs_server, client_link); struct nfs_delegation, super_list);
list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) {
delegation = NULL; struct inode *to_put = NULL;
if (place_holder && server == NFS_SERVER(place_holder))
delegation = rcu_dereference(NFS_I(place_holder)->delegation); if (test_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags))
if (!delegation || delegation != place_holder_deleg) continue;
delegation = list_entry_rcu(server->delegations.next, if (!nfs_delegation_need_return(delegation)) {
struct nfs_delegation, super_list); if (nfs4_is_valid_delegation(delegation, 0))
list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) {
struct inode *to_put = NULL;
if (!nfs_delegation_need_return(delegation)) {
prev = delegation; prev = delegation;
continue; continue;
} }
if (!nfs_sb_active(server->super))
break; /* continue in outer loop */
if (prev) {
struct inode *tmp;
tmp = nfs_delegation_grab_inode(prev); if (prev) {
if (tmp) { struct inode *tmp = nfs_delegation_grab_inode(prev);
to_put = place_holder; if (tmp) {
place_holder = tmp; to_put = place_holder;
place_holder_deleg = prev; place_holder = tmp;
} place_holder_deleg = prev;
} }
}
inode = nfs_delegation_grab_inode(delegation); inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL) { if (inode == NULL) {
rcu_read_unlock();
if (to_put)
iput(to_put);
nfs_sb_deactive(server->super);
goto restart;
}
delegation = nfs_start_delegation_return_locked(NFS_I(inode));
rcu_read_unlock(); rcu_read_unlock();
iput(to_put);
goto restart;
}
delegation = nfs_start_delegation_return_locked(NFS_I(inode));
rcu_read_unlock();
if (to_put) iput(to_put);
iput(to_put);
err = nfs_end_delegation_return(inode, delegation, 0); err = nfs_end_delegation_return(inode, delegation, 0);
iput(inode); iput(inode);
nfs_sb_deactive(server->super); cond_resched();
cond_resched(); if (!err)
if (!err) goto restart;
goto restart; set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); goto out;
if (place_holder)
iput(place_holder);
return err;
}
} }
rcu_read_unlock(); rcu_read_unlock();
if (place_holder) out:
iput(place_holder); iput(place_holder);
return 0; return err;
}
/**
* nfs_client_return_marked_delegations - return previously marked delegations
* @clp: nfs_client to process
*
* Note that this function is designed to be called by the state
* manager thread. For this reason, it cannot flush the dirty data,
* since that could deadlock in case of a state recovery error.
*
* Returns zero on success, or a negative errno value.
*/
int nfs_client_return_marked_delegations(struct nfs_client *clp)
{
return nfs_client_for_each_server(clp,
nfs_server_return_marked_delegations, NULL);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment