]> www.infradead.org Git - users/hch/misc.git/commitdiff
NFS: use a hash table for delegation lookup nfs-delegation-hash
authorChristoph Hellwig <hch@lst.de>
Tue, 8 Jul 2025 08:41:29 +0000 (10:41 +0200)
committerChristoph Hellwig <hch@lst.de>
Tue, 8 Jul 2025 08:53:34 +0000 (10:53 +0200)
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>
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/delegation.h
include/linux/nfs_fs_sb.h

index c824511c0b23291ece2196bbaee974788c20f7c5..7cf2acde347df4e4b4d62a040ea40c5436fef001 100644 (file)
@@ -984,6 +984,7 @@ static DEFINE_IDA(s_sysfs_ids);
 struct nfs_server *nfs_alloc_server(void)
 {
        struct nfs_server *server;
+       int delegation_buckets, i;
 
        server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
        if (!server)
@@ -1009,11 +1010,18 @@ struct nfs_server *nfs_alloc_server(void)
        atomic_set(&server->active, 0);
        atomic_long_set(&server->nr_active_delegations, 0);
 
+       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)
+               goto out_free_server;
+       for (i = 0; i < delegation_buckets; i++)
+               INIT_HLIST_HEAD(&server->delegation_hash_table[i]);
+
        server->io_stats = nfs_alloc_iostats();
-       if (!server->io_stats) {
-               kfree(server);
-               return NULL;
-       }
+       if (!server->io_stats)
+               goto out_free_delegation_hash;
 
        server->change_attr_type = NFS4_CHANGE_TYPE_IS_UNDEFINED;
 
@@ -1026,6 +1034,12 @@ struct nfs_server *nfs_alloc_server(void)
        rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC");
 
        return server;
+
+out_free_delegation_hash:
+       kfree(server->delegation_hash_table);
+out_free_server:
+       kfree(server);
+       return NULL;
 }
 EXPORT_SYMBOL_GPL(nfs_alloc_server);
 
@@ -1034,6 +1048,7 @@ static void delayed_free(struct rcu_head *p)
        struct nfs_server *server = container_of(p, struct nfs_server, rcu);
 
        nfs_free_iostats(server->io_stats);
+       kfree(server->delegation_hash_table);
        kfree(server);
 }
 
index b10e47e762fb4ac778309651f71ecb3a59269fd0..ca97aa82ed94f144fc9d13d7adf282531c6972b6 100644 (file)
 
 #define NFS_DEFAULT_DELEGATION_WATERMARK (5000U)
 
-static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
+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);
@@ -365,6 +372,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);
@@ -527,6 +535,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;
 
@@ -1162,11 +1172,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) &&
index 8ff5ab9c5c2565bca5415d090016dc05adb20f1b..9f1fb9b39c43827a2ad1348556cdaba138ce533b 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);
 }
 
+extern unsigned nfs_delegation_watermark;
+
 #endif
index df73e97394adc89ec23eaa5de5b3aa8ade5555b2..61f48ab250fb6c35915b0a074500e4c93831ad3c 100644 (file)
@@ -258,6 +258,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;