]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK
authorAnna Schumaker <anna.schumaker@oracle.com>
Tue, 4 Nov 2025 15:06:42 +0000 (10:06 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Sun, 23 Nov 2025 21:01:47 +0000 (16:01 -0500)
This patch adds a new flag: NFS_INO_REQ_DIR_DELEG to signal that a
directory wants to request a directory delegation the next time it does
a GETATTR. I have the client request a directory delegation when doing
an access, create, or unlink call since these calls indicate that a user
is working with a directory.

Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/nfs4proc.c
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h

index 9d3a5f29f17fdcd842212c6ace47ae764bd8d0c9..b4c192f00e945196af8c1fc528c5c8f3bf11a8ea 100644 (file)
@@ -379,6 +379,7 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi,
        delegation->inode = NULL;
        rcu_assign_pointer(nfsi->delegation, NULL);
        spin_unlock(&delegation->lock);
+       clear_bit(NFS_INO_REQ_DIR_DELEG, &nfsi->flags);
        return delegation;
 }
 
index 08ec2e9c68a4a6bf1aacc45b03ba9e0aed7b9f4a..def50e8a83bf5f2f5790ccb20f50c696388574fa 100644 (file)
@@ -124,6 +124,12 @@ static inline int nfs_have_delegated_mtime(struct inode *inode)
                                                 NFS_DELEGATION_FLAG_TIME);
 }
 
+static inline void nfs_request_directory_delegation(struct inode *inode)
+{
+       if (S_ISDIR(inode->i_mode))
+               set_bit(NFS_INO_REQ_DIR_DELEG, &NFS_I(inode)->flags);
+}
+
 int nfs4_delegation_hash_alloc(struct nfs_server *server);
 
 #endif
index 3b436ba2ed3bff2504608f0c074e2120a1f3de1a..99edc1d8d7aaa99967afe2f4f61a10bdcd200148 100644 (file)
@@ -4470,6 +4470,28 @@ out:
        return status;
 }
 
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+static bool should_request_dir_deleg(struct inode *inode)
+{
+       if (!inode)
+               return false;
+       if (!S_ISDIR(inode->i_mode))
+               return false;
+       if (!nfs_server_capable(inode, NFS_CAP_DIR_DELEG))
+               return false;
+       if (!test_and_clear_bit(NFS_INO_REQ_DIR_DELEG, &(NFS_I(inode)->flags)))
+               return false;
+       if (nfs4_have_delegation(inode, FMODE_READ, 0))
+               return false;
+       return true;
+}
+#else
+static bool should_request_dir_deleg(struct inode *inode)
+{
+       return false;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
                                struct nfs_fattr *fattr, struct inode *inode)
 {
@@ -4487,7 +4509,9 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
+       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;
@@ -4496,11 +4520,26 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
        if (inode && (server->flags & NFS_MOUNT_SOFTREVAL))
                task_flags |= RPC_TASK_TIMEOUT;
 
+       args.get_dir_deleg = should_request_dir_deleg(inode);
+       if (args.get_dir_deleg)
+               res.gdd_res = &gdd_res;
+
        nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, fattr->label), inode, 0);
        nfs_fattr_init(fattr);
        nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
-       return nfs4_do_call_sync(server->client, server, &msg,
-                       &args.seq_args, &res.seq_res, task_flags);
+
+       status = nfs4_do_call_sync(server->client, server, &msg,
+                                  &args.seq_args, &res.seq_res, task_flags);
+       if (args.get_dir_deleg) {
+               if (status == -EOPNOTSUPP) {
+                       server->caps &= ~NFS_CAP_DIR_DELEG;
+               } else if (status == 0 && gdd_res.status == GDD4_OK) {
+                       status = nfs_inode_set_delegation(inode, current_cred(),
+                                                         FMODE_READ, &gdd_res.deleg,
+                                                         0, NFS4_OPEN_DELEGATE_READ);
+               }
+       }
+       return status;
 }
 
 int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -4513,8 +4552,10 @@ int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
        do {
                err = _nfs4_proc_getattr(server, fhandle, fattr, inode);
                trace_nfs4_getattr(server, fhandle, fattr, err);
-               err = nfs4_handle_exception(server, err,
-                               &exception);
+               if (err == -EOPNOTSUPP)
+                       exception.retry = true;
+               else
+                       err = nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -4778,6 +4819,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
        int status = 0;
 
        if (!nfs4_have_delegation(inode, FMODE_READ, 0)) {
+               nfs_request_directory_delegation(inode);
                res.fattr = nfs_alloc_fattr();
                if (res.fattr == NULL)
                        return -ENOMEM;
@@ -4885,6 +4927,8 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
 
        ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);
 
+       nfs_request_directory_delegation(dir);
+
        if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
                sattr->ia_mode &= ~current_umask();
        state = nfs4_do_open(dir, ctx, flags, sattr, ilabel, NULL);
@@ -4981,6 +5025,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg,
        nfs4_init_sequence(&args->seq_args, &res->seq_res, 1, 0);
 
        nfs_fattr_init(res->dir_attr);
+       nfs_request_directory_delegation(d_inode(dentry->d_parent));
 
        if (inode) {
                nfs4_inode_return_delegation(inode);
@@ -10832,6 +10877,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .minor_version = 1,
        .init_caps = NFS_CAP_READDIRPLUS
                | NFS_CAP_ATOMIC_OPEN
+               | NFS_CAP_DIR_DELEG
                | NFS_CAP_POSIX_LOCK
                | NFS_CAP_STATEID_NFSV41
                | NFS_CAP_ATOMIC_OPEN_V1
@@ -10858,6 +10904,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
        .minor_version = 2,
        .init_caps = NFS_CAP_READDIRPLUS
                | NFS_CAP_ATOMIC_OPEN
+               | NFS_CAP_DIR_DELEG
                | NFS_CAP_POSIX_LOCK
                | NFS_CAP_STATEID_NFSV41
                | NFS_CAP_ATOMIC_OPEN_V1
index c585939b6cd60f1f3ac511bc8011c38bbfb358d9..a6624edb7226a16b63a6c3fd592b04d3ff34f58c 100644 (file)
@@ -344,6 +344,7 @@ struct nfs4_copy_state {
 #define NFS_INO_LAYOUTCOMMITTING (10)          /* layoutcommit inflight */
 #define NFS_INO_LAYOUTSTATS    (11)            /* layoutstats inflight */
 #define NFS_INO_ODIRECT                (12)            /* I/O setting is O_DIRECT */
+#define NFS_INO_REQ_DIR_DELEG  (13)            /* Request a directory delegation */
 
 static inline struct nfs_inode *NFS_I(const struct inode *inode)
 {
index d30c0245031c07d933d6fd5fa7fadc65eb6143e0..4ba04de6b1caec2d4e5de09b217c497539d94d71 100644 (file)
@@ -305,6 +305,7 @@ struct nfs_server {
 #define NFS_CAP_REBOOT_LAYOUTRETURN    (1U << 8)
 #define NFS_CAP_OFFLOAD_STATUS (1U << 9)
 #define NFS_CAP_ZERO_RANGE     (1U << 10)
+#define NFS_CAP_DIR_DELEG      (1U << 11)
 #define NFS_CAP_OPEN_XOR       (1U << 12)
 #define NFS_CAP_DELEGTIME      (1U << 13)
 #define NFS_CAP_POSIX_LOCK     (1U << 14)