From: Trond Myklebust Date: Fri, 2 Jan 2026 01:16:04 +0000 (-0500) Subject: NFSv4: Don't free slots prematurely if requesting a directory delegation X-Git-Tag: v6.19-rc6~31^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3f77eda548c82564ae4a23c76b51d36c6e39c865;p=thirdparty%2Flinux.git NFSv4: Don't free slots prematurely if requesting a directory delegation When requesting a directory delegation, it is imperative to hold the slot until the delegation state has been recorded. Otherwise, if a recall comes in, the call to referring_call_exists() will assume the processing is done, and when it doesn't find a delegation, it will assume it has been returned. Fixes: 156b09482933 ("NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK") Signed-off-by: Trond Myklebust --- diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 51da62ba6559..a0885ae55abc 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4494,6 +4494,25 @@ static bool should_request_dir_deleg(struct inode *inode) } #endif /* CONFIG_NFS_V4_1 */ +static void nfs4_call_getattr_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs4_call_sync_data *data = calldata; + nfs4_setup_sequence(data->seq_server->nfs_client, data->seq_args, + data->seq_res, task); +} + +static void nfs4_call_getattr_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_call_sync_data *data = calldata; + + nfs4_sequence_process(task, data->seq_res); +} + +static const struct rpc_call_ops nfs4_call_getattr_ops = { + .rpc_call_prepare = nfs4_call_getattr_prepare, + .rpc_call_done = nfs4_call_getattr_done, +}; + static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct inode *inode) { @@ -4511,16 +4530,26 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, .rpc_argp = &args, .rpc_resp = &res, }; + struct nfs4_call_sync_data data = { + .seq_server = server, + .seq_args = &args.seq_args, + .seq_res = &res.seq_res, + }; + struct rpc_task_setup task_setup = { + .rpc_client = server->client, + .rpc_message = &msg, + .callback_ops = &nfs4_call_getattr_ops, + .callback_data = &data, + }; struct nfs4_gdd_res gdd_res; - unsigned short task_flags = 0; int status; if (nfs4_has_session(server->nfs_client)) - task_flags = RPC_TASK_MOVEABLE; + task_setup.flags = RPC_TASK_MOVEABLE; /* Is this is an attribute revalidation, subject to softreval? */ if (inode && (server->flags & NFS_MOUNT_SOFTREVAL)) - task_flags |= RPC_TASK_TIMEOUT; + task_setup.flags |= RPC_TASK_TIMEOUT; args.get_dir_deleg = should_request_dir_deleg(inode); if (args.get_dir_deleg) @@ -4530,22 +4559,24 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, nfs_fattr_init(fattr); nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0); - status = nfs4_do_call_sync(server->client, server, &msg, - &args.seq_args, &res.seq_res, task_flags); + status = nfs4_call_sync_custom(&task_setup); + if (args.get_dir_deleg) { switch (status) { case 0: if (gdd_res.status != GDD4_OK) break; - status = nfs_inode_set_delegation( - inode, current_cred(), FMODE_READ, - &gdd_res.deleg, 0, NFS4_OPEN_DELEGATE_READ); + nfs_inode_set_delegation(inode, current_cred(), + FMODE_READ, &gdd_res.deleg, 0, + NFS4_OPEN_DELEGATE_READ); break; case -ENOTSUPP: case -EOPNOTSUPP: server->caps &= ~NFS_CAP_DIR_DELEG; } } + + nfs4_sequence_free_slot(&res.seq_res); return status; }