Re: [PATCH] NFS: Return a delegation the client fails to record

"Anna Schumaker" <[email protected]>
Newsgroups gmane.linux.nfs
Message-ID <[email protected]>
Hi Chuck,

On Thu, Jul 2, 2026, at 10:51 AM, Chuck Lever wrote:
> On Wed, Jun 17, 2026, at 8:52 AM, Chuck Lever wrote:
>> When an NFS server grants a delegation in an OPEN reply,
>> nfs_inode_set_delegation() records it on the client. However, three
>> of its error flows return without sending DELEGRETURN.
>>
>> A delegation can be relinquished only by DELEGRETURN (RFC 8881
>> Section 20.2.4), so dropping one silently leaves the server believing
>> the client still holds it. If the server happens to recall that
>> delegation, the client answers CB_RECALL with NFS4ERR_BADHANDLE
>> because it has no record of the stateid. The server revokes the
>> delegation and moves it onto its cl_revoked list, because the client
>> never sends the FREE_STATEID that would drain it. Every subsequent
>> SEQUENCE reply then carries SEQ4_STATUS_RECALLABLE_STATE_REVOKED,
>> and the client's state manager loops issuing TEST_STATEID across its
>> delegations without ever clearing the condition.
>>
>> The window is easy to reach now that a server offers a write
>> delegation on any write OPEN: a delegation recalled for one opener
>> races a re-open that the server answers with a fresh write
>> delegation.
>>
>> Instead of dropping it, hand the delegation back during these error
>> flows.
>>
>> Fixes: ade04647dd56 ("NFSv4: Ensure we honour NFS_DELEGATION_RETURNING 
>> in nfs_inode_set_delegation()")
>> Signed-off-by: Chuck Lever <[email protected]>
>> ---
>>  fs/nfs/delegation.c | 18 ++++++++++++++----
>>  1 file changed, 14 insertions(+), 4 deletions(-)
>>
>> diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
>> index 122fb3f14ffb..cb579f8c55ce 100644
>> --- a/fs/nfs/delegation.c
>> +++ b/fs/nfs/delegation.c
>> @@ -440,11 +440,14 @@ int nfs_inode_set_delegation(struct inode *inode, 
>> const struct cred *cred,
>>  	struct nfs_inode *nfsi = NFS_I(inode);
>>  	struct nfs_delegation *delegation, *old_delegation;
>>  	struct nfs_delegation *freeme = NULL;
>> +	bool orphaned = false;
>>  	int status = 0;
>> 
>>  	delegation = kmalloc_obj(*delegation, GFP_KERNEL_ACCOUNT);
>> -	if (delegation == NULL)
>> +	if (delegation == NULL) {
>> +		nfs4_proc_delegreturn(inode, cred, stateid, NULL, 0);
>>  		return -ENOMEM;
>> +	}
>>  	nfs4_stateid_copy(&delegation->stateid, stateid);
>>  	refcount_set(&delegation->refcount, 1);
>>  	delegation->type = type;
>> @@ -493,11 +496,15 @@ int nfs_inode_set_delegation(struct inode *inode, 
>> const struct cred *cred,
>>  			goto out;
>>  		}
>>  		if (test_and_set_bit(NFS_DELEGATION_RETURNING,
>> -					&old_delegation->flags))
>> +					&old_delegation->flags)) {
>> +			orphaned = true;
>>  			goto out;
>> +		}
>>  	}
>> -	if (!nfs_detach_delegations_locked(nfsi, old_delegation, clp))
>> +	if (!nfs_detach_delegations_locked(nfsi, old_delegation, clp)) {
>> +		orphaned = true;
>>  		goto out;
>> +	}
>>  	freeme = old_delegation;
>>  add_new:
>>  	/*
>> @@ -532,8 +539,11 @@ int nfs_inode_set_delegation(struct inode *inode, 
>> const struct cred *cred,
>>  		nfs_update_delegated_mtime(inode);
>>  out:
>>  	spin_unlock(&clp->cl_lock);
>> -	if (delegation != NULL)
>> +	if (delegation != NULL) {
>> +		if (orphaned)
>> +			nfs_do_return_delegation(inode, delegation, 0);
>>  		__nfs_free_delegation(delegation);
>> +	}
>>  	if (freeme != NULL) {
>>  		nfs_do_return_delegation(inode, freeme, 0);
>>  		nfs_mark_delegation_revoked(server, freeme);
>> -- 
>> 2.54.0
>
> Friendly bump! What is the disposition of this submission?

I have it in a testing branch that I haven't pushed out yet. I'm planning
to include it in the next round of bugfixes that I send out.

Anna

>
>
> -- 
> Chuck Lever
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.