diff options
Diffstat (limited to 'fs/nfs/delegation.c')
| -rw-r--r-- | fs/nfs/delegation.c | 121 | 
1 files changed, 82 insertions, 39 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 6390a4b5fee..57db3244f4d 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -64,17 +64,15 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags)  	return ret;  } -static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) +static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)  {  	struct inode *inode = state->inode;  	struct file_lock *fl;  	int status = 0;  	if (inode->i_flock == NULL) -		return 0; - -	if (inode->i_flock == NULL)  		goto out; +  	/* Protect inode->i_flock using the file locks lock */  	lock_flocks();  	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { @@ -83,7 +81,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_  		if (nfs_file_open_context(fl->fl_file) != ctx)  			continue;  		unlock_flocks(); -		status = nfs4_lock_delegation_recall(state, fl); +		status = nfs4_lock_delegation_recall(fl, state, stateid);  		if (status < 0)  			goto out;  		lock_flocks(); @@ -120,7 +118,7 @@ again:  		seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);  		err = nfs4_open_delegation_recall(ctx, state, stateid);  		if (!err) -			err = nfs_delegation_claim_locks(ctx, state); +			err = nfs_delegation_claim_locks(ctx, state, stateid);  		if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))  			err = -EAGAIN;  		mutex_unlock(&sp->so_delegreturn_mutex); @@ -389,6 +387,24 @@ out:  	return err;  } +static bool nfs_delegation_need_return(struct nfs_delegation *delegation) +{ +	bool ret = false; + +	if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) +		ret = true; +	if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) { +		struct inode *inode; + +		spin_lock(&delegation->lock); +		inode = delegation->inode; +		if (inode && list_empty(&NFS_I(inode)->open_files)) +			ret = true; +		spin_unlock(&delegation->lock); +	} +	return ret; +} +  /**   * nfs_client_return_marked_delegations - return previously marked delegations   * @clp: nfs_client to process @@ -411,8 +427,7 @@ restart:  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {  		list_for_each_entry_rcu(delegation, &server->delegations,  								super_list) { -			if (!test_and_clear_bit(NFS_DELEGATION_RETURN, -							&delegation->flags)) +			if (!nfs_delegation_need_return(delegation))  				continue;  			inode = nfs_delegation_grab_inode(delegation);  			if (inode == NULL) @@ -471,6 +486,13 @@ int nfs4_inode_return_delegation(struct inode *inode)  	return err;  } +static void nfs_mark_return_if_closed_delegation(struct nfs_server *server, +		struct nfs_delegation *delegation) +{ +	set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); +	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); +} +  static void nfs_mark_return_delegation(struct nfs_server *server,  		struct nfs_delegation *delegation)  { @@ -478,6 +500,45 @@ static void nfs_mark_return_delegation(struct nfs_server *server,  	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);  } +static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) +{ +	struct nfs_delegation *delegation; +	bool ret = false; + +	list_for_each_entry_rcu(delegation, &server->delegations, super_list) { +		nfs_mark_return_delegation(server, delegation); +		ret = true; +	} +	return ret; +} + +static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) +{ +	struct nfs_server *server; + +	rcu_read_lock(); +	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) +		nfs_server_mark_return_all_delegations(server); +	rcu_read_unlock(); +} + +static void nfs_delegation_run_state_manager(struct nfs_client *clp) +{ +	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) +		nfs4_schedule_state_manager(clp); +} + +/** + * nfs_expire_all_delegations + * @clp: client to process + * + */ +void nfs_expire_all_delegations(struct nfs_client *clp) +{ +	nfs_client_mark_return_all_delegations(clp); +	nfs_delegation_run_state_manager(clp); +} +  /**   * nfs_super_return_all_delegations - return delegations for one superblock   * @sb: sb to process @@ -486,24 +547,22 @@ static void nfs_mark_return_delegation(struct nfs_server *server,  void nfs_server_return_all_delegations(struct nfs_server *server)  {  	struct nfs_client *clp = server->nfs_client; -	struct nfs_delegation *delegation; +	bool need_wait;  	if (clp == NULL)  		return;  	rcu_read_lock(); -	list_for_each_entry_rcu(delegation, &server->delegations, super_list) { -		spin_lock(&delegation->lock); -		set_bit(NFS_DELEGATION_RETURN, &delegation->flags); -		spin_unlock(&delegation->lock); -	} +	need_wait = nfs_server_mark_return_all_delegations(server);  	rcu_read_unlock(); -	if (nfs_client_return_marked_delegations(clp) != 0) +	if (need_wait) {  		nfs4_schedule_state_manager(clp); +		nfs4_wait_clnt_recover(clp); +	}  } -static void nfs_mark_return_all_delegation_types(struct nfs_server *server, +static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,  						 fmode_t flags)  {  	struct nfs_delegation *delegation; @@ -512,27 +571,21 @@ static void nfs_mark_return_all_delegation_types(struct nfs_server *server,  		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))  			continue;  		if (delegation->type & flags) -			nfs_mark_return_delegation(server, delegation); +			nfs_mark_return_if_closed_delegation(server, delegation);  	}  } -static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, +static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,  							fmode_t flags)  {  	struct nfs_server *server;  	rcu_read_lock();  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) -		nfs_mark_return_all_delegation_types(server, flags); +		nfs_mark_return_unused_delegation_types(server, flags);  	rcu_read_unlock();  } -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_remove_bad_delegation(struct inode *inode)  {  	struct nfs_delegation *delegation; @@ -546,27 +599,17 @@ void nfs_remove_bad_delegation(struct inode *inode)  EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);  /** - * nfs_expire_all_delegation_types + * nfs_expire_unused_delegation_types   * @clp: client to process   * @flags: delegation types to expire   *   */ -void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) +void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)  { -	nfs_client_mark_return_all_delegation_types(clp, flags); +	nfs_client_mark_return_unused_delegation_types(clp, flags);  	nfs_delegation_run_state_manager(clp);  } -/** - * nfs_expire_all_delegations - * @clp: client to process - * - */ -void nfs_expire_all_delegations(struct nfs_client *clp) -{ -	nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); -} -  static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)  {  	struct nfs_delegation *delegation; @@ -574,7 +617,7 @@ static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {  		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))  			continue; -		nfs_mark_return_delegation(server, delegation); +		nfs_mark_return_if_closed_delegation(server, delegation);  	}  }  |