]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
NFS: use a hash table for delegation lookup
authorChristoph Hellwig <hch@lst.de>
Fri, 18 Jul 2025 08:14:50 +0000 (10:14 +0200)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Tue, 22 Jul 2025 12:10:41 +0000 (08:10 -0400)
nfs_delegation_find_inode currently has to walk the entire list of
delegations per inode, which can become pretty large, and can become even
larger when increasing the delegation watermark.

Add a hash table to speed up the delegation lookup, sized as a fraction
of the delegation watermark.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20250718081509.2607553-6-hch@lst.de
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/nfs4client.c
fs/nfs/nfs4proc.c
include/linux/nfs_fs_sb.h

index ea96f77e38c21481654da600cf4cd127b1d6718e..9d3a5f29f17fdcd842212c6ace47ae764bd8d0c9 100644 (file)
 static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
 module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
 
+static struct hlist_head *nfs_delegation_hash(struct nfs_server *server,
+               const struct nfs_fh *fhandle)
+{
+       return server->delegation_hash_table +
+               (nfs_fhandle_hash(fhandle) & server->delegation_hash_mask);
+}
+
 static void __nfs_free_delegation(struct nfs_delegation *delegation)
 {
        put_cred(delegation->cred);
@@ -367,6 +374,7 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi,
                spin_unlock(&delegation->lock);
                return NULL;
        }
+       hlist_del_init_rcu(&delegation->hash);
        list_del_rcu(&delegation->super_list);
        delegation->inode = NULL;
        rcu_assign_pointer(nfsi->delegation, NULL);
@@ -529,6 +537,8 @@ add_new:
        spin_unlock(&inode->i_lock);
 
        list_add_tail_rcu(&delegation->super_list, &server->delegations);
+       hlist_add_head_rcu(&delegation->hash,
+                       nfs_delegation_hash(server, &NFS_I(inode)->fh));
        rcu_assign_pointer(nfsi->delegation, delegation);
        delegation = NULL;
 
@@ -1166,11 +1176,12 @@ static struct inode *
 nfs_delegation_find_inode_server(struct nfs_server *server,
                                 const struct nfs_fh *fhandle)
 {
+       struct hlist_head *head = nfs_delegation_hash(server, fhandle);
        struct nfs_delegation *delegation;
        struct super_block *freeme = NULL;
        struct inode *res = NULL;
 
-       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
+       hlist_for_each_entry_rcu(delegation, head, hash) {
                spin_lock(&delegation->lock);
                if (delegation->inode != NULL &&
                    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
@@ -1577,3 +1588,18 @@ out:
        rcu_read_unlock();
        return ret;
 }
+
+int nfs4_delegation_hash_alloc(struct nfs_server *server)
+{
+       int delegation_buckets, i;
+
+       delegation_buckets = roundup_pow_of_two(nfs_delegation_watermark / 16);
+       server->delegation_hash_mask = delegation_buckets - 1;
+       server->delegation_hash_table = kmalloc_array(delegation_buckets,
+                       sizeof(*server->delegation_hash_table), GFP_KERNEL);
+       if (!server->delegation_hash_table)
+               return -ENOMEM;
+       for (i = 0; i < delegation_buckets; i++)
+               INIT_HLIST_HEAD(&server->delegation_hash_table[i]);
+       return 0;
+}
index 8ff5ab9c5c2565bca5415d090016dc05adb20f1b..08ec2e9c68a4a6bf1aacc45b03ba9e0aed7b9f4a 100644 (file)
@@ -14,6 +14,7 @@
  * NFSv4 delegation
  */
 struct nfs_delegation {
+       struct hlist_node hash;
        struct list_head super_list;
        const struct cred *cred;
        struct inode *inode;
@@ -123,4 +124,6 @@ static inline int nfs_have_delegated_mtime(struct inode *inode)
                                                 NFS_DELEGATION_FLAG_TIME);
 }
 
+int nfs4_delegation_hash_alloc(struct nfs_server *server);
+
 #endif
index 5943a192f36be7cc00eea553e007582fd9da671e..2ea98f1f116fe4d05c460709bcdadfffeda16a15 100644 (file)
@@ -802,6 +802,7 @@ static void nfs4_destroy_server(struct nfs_server *server)
        unset_pnfs_layoutdriver(server);
        nfs4_purge_state_owners(server, &freeme);
        nfs4_free_state_owners(&freeme);
+       kfree(server->delegation_hash_table);
 }
 
 /*
@@ -1096,6 +1097,10 @@ static int nfs4_server_common_setup(struct nfs_server *server,
 {
        int error;
 
+       error = nfs4_delegation_hash_alloc(server);
+       if (error)
+               return error;
+
        /* data servers support only a subset of NFSv4.1 */
        if (is_ds_only_client(server->nfs_client))
                return -EPROTONOSUPPORT;
index ef2077e185b6a7a8c193718e78a2a6519d8358af..d8bebd757af3df0efe67c257db8b38bca127a2ea 100644 (file)
@@ -10967,6 +10967,26 @@ static const struct inode_operations nfs4_file_inode_operations = {
        .listxattr      = nfs4_listxattr,
 };
 
+static struct nfs_server *nfs4_clone_server(struct nfs_server *source,
+               struct nfs_fh *fh, struct nfs_fattr *fattr,
+               rpc_authflavor_t flavor)
+{
+       struct nfs_server *server;
+       int error;
+
+       server = nfs_clone_server(source, fh, fattr, flavor);
+       if (IS_ERR(server))
+               return server;
+
+       error = nfs4_delegation_hash_alloc(server);
+       if (error) {
+               nfs_free_server(server);
+               return ERR_PTR(error);
+       }
+
+       return server;
+}
+
 const struct nfs_rpc_ops nfs_v4_clientops = {
        .version        = 4,                    /* protocol version */
        .dentry_ops     = &nfs4_dentry_operations,
@@ -11019,7 +11039,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .init_client    = nfs4_init_client,
        .free_client    = nfs4_free_client,
        .create_server  = nfs4_create_server,
-       .clone_server   = nfs_clone_server,
+       .clone_server   = nfs4_clone_server,
        .discover_trunking = nfs4_discover_trunking,
        .enable_swap    = nfs4_enable_swap,
        .disable_swap   = nfs4_disable_swap,
index a9b44f12623f01b4198e5c8c78407d5bf8fd59de..d30c0245031c07d933d6fd5fa7fadc65eb6143e0 100644 (file)
@@ -255,6 +255,8 @@ struct nfs_server {
        struct list_head        layouts;
        struct list_head        delegations;
        atomic_long_t           nr_active_delegations;
+       unsigned int            delegation_hash_mask;
+       struct hlist_head       *delegation_hash_table;
        struct list_head        ss_copies;
        struct list_head        ss_src_copies;