Skip to content

Commit f5b3108

Browse files
Christoph HellwigTrond Myklebust
authored andcommitted
NFS: use a hash table for delegation lookup
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>
1 parent 2fb4af5 commit f5b3108

5 files changed

Lines changed: 58 additions & 2 deletions

File tree

fs/nfs/delegation.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@
3030
static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
3131
module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
3232

33+
static struct hlist_head *nfs_delegation_hash(struct nfs_server *server,
34+
const struct nfs_fh *fhandle)
35+
{
36+
return server->delegation_hash_table +
37+
(nfs_fhandle_hash(fhandle) & server->delegation_hash_mask);
38+
}
39+
3340
static void __nfs_free_delegation(struct nfs_delegation *delegation)
3441
{
3542
put_cred(delegation->cred);
@@ -367,6 +374,7 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi,
367374
spin_unlock(&delegation->lock);
368375
return NULL;
369376
}
377+
hlist_del_init_rcu(&delegation->hash);
370378
list_del_rcu(&delegation->super_list);
371379
delegation->inode = NULL;
372380
rcu_assign_pointer(nfsi->delegation, NULL);
@@ -529,6 +537,8 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
529537
spin_unlock(&inode->i_lock);
530538

531539
list_add_tail_rcu(&delegation->super_list, &server->delegations);
540+
hlist_add_head_rcu(&delegation->hash,
541+
nfs_delegation_hash(server, &NFS_I(inode)->fh));
532542
rcu_assign_pointer(nfsi->delegation, delegation);
533543
delegation = NULL;
534544

@@ -1166,11 +1176,12 @@ static struct inode *
11661176
nfs_delegation_find_inode_server(struct nfs_server *server,
11671177
const struct nfs_fh *fhandle)
11681178
{
1179+
struct hlist_head *head = nfs_delegation_hash(server, fhandle);
11691180
struct nfs_delegation *delegation;
11701181
struct super_block *freeme = NULL;
11711182
struct inode *res = NULL;
11721183

1173-
list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
1184+
hlist_for_each_entry_rcu(delegation, head, hash) {
11741185
spin_lock(&delegation->lock);
11751186
if (delegation->inode != NULL &&
11761187
!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
@@ -1577,3 +1588,18 @@ bool nfs4_delegation_flush_on_close(const struct inode *inode)
15771588
rcu_read_unlock();
15781589
return ret;
15791590
}
1591+
1592+
int nfs4_delegation_hash_alloc(struct nfs_server *server)
1593+
{
1594+
int delegation_buckets, i;
1595+
1596+
delegation_buckets = roundup_pow_of_two(nfs_delegation_watermark / 16);
1597+
server->delegation_hash_mask = delegation_buckets - 1;
1598+
server->delegation_hash_table = kmalloc_array(delegation_buckets,
1599+
sizeof(*server->delegation_hash_table), GFP_KERNEL);
1600+
if (!server->delegation_hash_table)
1601+
return -ENOMEM;
1602+
for (i = 0; i < delegation_buckets; i++)
1603+
INIT_HLIST_HEAD(&server->delegation_hash_table[i]);
1604+
return 0;
1605+
}

fs/nfs/delegation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* NFSv4 delegation
1515
*/
1616
struct nfs_delegation {
17+
struct hlist_node hash;
1718
struct list_head super_list;
1819
const struct cred *cred;
1920
struct inode *inode;
@@ -123,4 +124,6 @@ static inline int nfs_have_delegated_mtime(struct inode *inode)
123124
NFS_DELEGATION_FLAG_TIME);
124125
}
125126

127+
int nfs4_delegation_hash_alloc(struct nfs_server *server);
128+
126129
#endif

fs/nfs/nfs4client.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ static void nfs4_destroy_server(struct nfs_server *server)
802802
unset_pnfs_layoutdriver(server);
803803
nfs4_purge_state_owners(server, &freeme);
804804
nfs4_free_state_owners(&freeme);
805+
kfree(server->delegation_hash_table);
805806
}
806807

807808
/*
@@ -1096,6 +1097,10 @@ static int nfs4_server_common_setup(struct nfs_server *server,
10961097
{
10971098
int error;
10981099

1100+
error = nfs4_delegation_hash_alloc(server);
1101+
if (error)
1102+
return error;
1103+
10991104
/* data servers support only a subset of NFSv4.1 */
11001105
if (is_ds_only_client(server->nfs_client))
11011106
return -EPROTONOSUPPORT;

fs/nfs/nfs4proc.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10967,6 +10967,26 @@ static const struct inode_operations nfs4_file_inode_operations = {
1096710967
.listxattr = nfs4_listxattr,
1096810968
};
1096910969

10970+
static struct nfs_server *nfs4_clone_server(struct nfs_server *source,
10971+
struct nfs_fh *fh, struct nfs_fattr *fattr,
10972+
rpc_authflavor_t flavor)
10973+
{
10974+
struct nfs_server *server;
10975+
int error;
10976+
10977+
server = nfs_clone_server(source, fh, fattr, flavor);
10978+
if (IS_ERR(server))
10979+
return server;
10980+
10981+
error = nfs4_delegation_hash_alloc(server);
10982+
if (error) {
10983+
nfs_free_server(server);
10984+
return ERR_PTR(error);
10985+
}
10986+
10987+
return server;
10988+
}
10989+
1097010990
const struct nfs_rpc_ops nfs_v4_clientops = {
1097110991
.version = 4, /* protocol version */
1097210992
.dentry_ops = &nfs4_dentry_operations,
@@ -11019,7 +11039,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
1101911039
.init_client = nfs4_init_client,
1102011040
.free_client = nfs4_free_client,
1102111041
.create_server = nfs4_create_server,
11022-
.clone_server = nfs_clone_server,
11042+
.clone_server = nfs4_clone_server,
1102311043
.discover_trunking = nfs4_discover_trunking,
1102411044
.enable_swap = nfs4_enable_swap,
1102511045
.disable_swap = nfs4_disable_swap,

include/linux/nfs_fs_sb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ struct nfs_server {
255255
struct list_head layouts;
256256
struct list_head delegations;
257257
atomic_long_t nr_active_delegations;
258+
unsigned int delegation_hash_mask;
259+
struct hlist_head *delegation_hash_table;
258260
struct list_head ss_copies;
259261
struct list_head ss_src_copies;
260262

0 commit comments

Comments
 (0)