]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
NFSv4: Don't free slots prematurely if requesting a directory delegation
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 2 Jan 2026 01:16:04 +0000 (20:16 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 5 Jan 2026 04:03:26 +0000 (23:03 -0500)
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 <trond.myklebust@hammerspace.com>
fs/nfs/nfs4proc.c

index 51da62ba655957213ff58c052a912038818cab7e..a0885ae55abc1323f5d2ec18ab59b8fc185903f4 100644 (file)
@@ -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;
 }