* use a hash for looking up delegation
@ 2025-07-14 11:16 Christoph Hellwig
2025-07-14 11:16 ` [PATCH 1/4] NFS: cleanup nfs_inode_reclaim_delegation Christoph Hellwig
` (3 more replies)
0 siblings, 4 replies; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-14 11:16 UTC (permalink / raw)
To: Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
Hi all,
currently recalling delegations has to walk the server->delegations
list, and then take the lock for each delegation. This can take a lot of
time and has adverse effects to the rest of the system due to the number
atomic operations, cache lines touched and a long RCU critical section.
This series first converts the delegation watermark to be per-server, as
all the state guarded by it is per-server and the commit message adding
it talks about server side overhead as well, and then adds a very simple
hash for finding the delegation for a given file handle in
nfs_delegation_find_inode_server.
With this hash sample microbenchmarks that cause delegation recalls in
reverse list order are sped up ~5 percent, although the time is still
very variable due to other factors.
Diffstat:
fs/nfs/client.c | 24 +++++++++--
fs/nfs/delegation.c | 99 ++++++++++++++++++++++++++--------------------
fs/nfs/delegation.h | 3 +
include/linux/nfs_fs_sb.h | 3 +
4 files changed, 82 insertions(+), 47 deletions(-)
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 1/4] NFS: cleanup nfs_inode_reclaim_delegation
2025-07-14 11:16 use a hash for looking up delegation Christoph Hellwig
@ 2025-07-14 11:16 ` Christoph Hellwig
2025-07-14 13:06 ` Jeff Layton
2025-07-14 11:16 ` [PATCH 2/4] NFS: move the delegation_watermark module parameter Christoph Hellwig
` (2 subsequent siblings)
3 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-14 11:16 UTC (permalink / raw)
To: Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
Reduce a level of indentation for most of the code in this function.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/nfs/delegation.c | 48 ++++++++++++++++++++++-----------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 519bdc91112c..56bb2a7e1793 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -237,34 +237,34 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation);
- if (delegation != NULL) {
- spin_lock(&delegation->lock);
- nfs4_stateid_copy(&delegation->stateid, stateid);
- delegation->type = type;
- delegation->pagemod_limit = pagemod_limit;
- oldcred = delegation->cred;
- delegation->cred = get_cred(cred);
- switch (deleg_type) {
- case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG:
- case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG:
- set_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
- break;
- default:
- clear_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
- }
- clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
- if (test_and_clear_bit(NFS_DELEGATION_REVOKED,
- &delegation->flags))
- atomic_long_inc(&nfs_active_delegations);
- spin_unlock(&delegation->lock);
- rcu_read_unlock();
- put_cred(oldcred);
- trace_nfs4_reclaim_delegation(inode, type);
- } else {
+ if (!delegation) {
rcu_read_unlock();
nfs_inode_set_delegation(inode, cred, type, stateid,
pagemod_limit, deleg_type);
+ return;
+ }
+
+ spin_lock(&delegation->lock);
+ nfs4_stateid_copy(&delegation->stateid, stateid);
+ delegation->type = type;
+ delegation->pagemod_limit = pagemod_limit;
+ oldcred = delegation->cred;
+ delegation->cred = get_cred(cred);
+ switch (deleg_type) {
+ case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG:
+ case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG:
+ set_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
+ break;
+ default:
+ clear_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
}
+ clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
+ if (test_and_clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+ atomic_long_inc(&nfs_active_delegations);
+ spin_unlock(&delegation->lock);
+ rcu_read_unlock();
+ put_cred(oldcred);
+ trace_nfs4_reclaim_delegation(inode, type);
}
static int nfs_do_return_delegation(struct inode *inode,
--
2.47.2
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 2/4] NFS: move the delegation_watermark module parameter
2025-07-14 11:16 use a hash for looking up delegation Christoph Hellwig
2025-07-14 11:16 ` [PATCH 1/4] NFS: cleanup nfs_inode_reclaim_delegation Christoph Hellwig
@ 2025-07-14 11:16 ` Christoph Hellwig
2025-07-14 13:06 ` Jeff Layton
2025-07-14 11:16 ` [PATCH 3/4] NFS: track active delegations per-server Christoph Hellwig
2025-07-14 11:16 ` [PATCH 4/4] NFS: use a hash table for delegation lookup Christoph Hellwig
3 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-14 11:16 UTC (permalink / raw)
To: Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
Keep the module_param_named next to the variable declaration instead of
somewhere unrelated, following the best practice in the rest of the
kernel.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/nfs/delegation.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 56bb2a7e1793..d036796dbe69 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -29,6 +29,7 @@
static atomic_long_t nfs_active_delegations;
static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
+module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
static void __nfs_free_delegation(struct nfs_delegation *delegation)
{
@@ -1575,5 +1576,3 @@ bool nfs4_delegation_flush_on_close(const struct inode *inode)
rcu_read_unlock();
return ret;
}
-
-module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
--
2.47.2
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 3/4] NFS: track active delegations per-server
2025-07-14 11:16 use a hash for looking up delegation Christoph Hellwig
2025-07-14 11:16 ` [PATCH 1/4] NFS: cleanup nfs_inode_reclaim_delegation Christoph Hellwig
2025-07-14 11:16 ` [PATCH 2/4] NFS: move the delegation_watermark module parameter Christoph Hellwig
@ 2025-07-14 11:16 ` Christoph Hellwig
2025-07-14 13:07 ` Jeff Layton
2025-07-14 11:16 ` [PATCH 4/4] NFS: use a hash table for delegation lookup Christoph Hellwig
3 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-14 11:16 UTC (permalink / raw)
To: Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
The active delegation watermark was added to avoid overloading servers.
Track the active delegation per-server instead of globally so that clients
talking to multiple servers aren't limited by the global limit.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/nfs/client.c | 1 +
fs/nfs/delegation.c | 35 +++++++++++++++++++----------------
include/linux/nfs_fs_sb.h | 1 +
3 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 1a55debab6e5..f55188928f67 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -1017,6 +1017,7 @@ struct nfs_server *nfs_alloc_server(void)
INIT_LIST_HEAD(&server->ss_src_copies);
atomic_set(&server->active, 0);
+ atomic_long_set(&server->nr_active_delegations, 0);
server->io_stats = nfs_alloc_iostats();
if (!server->io_stats) {
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index d036796dbe69..621b635d1c56 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -27,7 +27,6 @@
#define NFS_DEFAULT_DELEGATION_WATERMARK (15000U)
-static atomic_long_t nfs_active_delegations;
static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
@@ -38,11 +37,12 @@ static void __nfs_free_delegation(struct nfs_delegation *delegation)
kfree_rcu(delegation, rcu);
}
-static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation)
+static void nfs_mark_delegation_revoked(struct nfs_server *server,
+ struct nfs_delegation *delegation)
{
if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
- atomic_long_dec(&nfs_active_delegations);
+ atomic_long_dec(&server->nr_active_delegations);
if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
nfs_clear_verifier_delegated(delegation->inode);
}
@@ -60,9 +60,10 @@ static void nfs_put_delegation(struct nfs_delegation *delegation)
__nfs_free_delegation(delegation);
}
-static void nfs_free_delegation(struct nfs_delegation *delegation)
+static void nfs_free_delegation(struct nfs_server *server,
+ struct nfs_delegation *delegation)
{
- nfs_mark_delegation_revoked(delegation);
+ nfs_mark_delegation_revoked(server, delegation);
nfs_put_delegation(delegation);
}
@@ -261,7 +262,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
}
clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
if (test_and_clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
- atomic_long_inc(&nfs_active_delegations);
+ atomic_long_inc(&NFS_SERVER(inode)->nr_active_delegations);
spin_unlock(&delegation->lock);
rcu_read_unlock();
put_cred(oldcred);
@@ -413,7 +414,8 @@ nfs_update_delegation_cred(struct nfs_delegation *delegation,
}
static void
-nfs_update_inplace_delegation(struct nfs_delegation *delegation,
+nfs_update_inplace_delegation(struct nfs_server *server,
+ struct nfs_delegation *delegation,
const struct nfs_delegation *update)
{
if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) {
@@ -426,7 +428,7 @@ nfs_update_inplace_delegation(struct nfs_delegation *delegation,
nfs_update_delegation_cred(delegation, update->cred);
/* smp_mb__before_atomic() is implicit due to xchg() */
clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
- atomic_long_inc(&nfs_active_delegations);
+ atomic_long_inc(&server->nr_active_delegations);
}
}
}
@@ -481,7 +483,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
if (nfs4_stateid_match_other(&old_delegation->stateid,
&delegation->stateid)) {
spin_lock(&old_delegation->lock);
- nfs_update_inplace_delegation(old_delegation,
+ nfs_update_inplace_delegation(server, old_delegation,
delegation);
spin_unlock(&old_delegation->lock);
goto out;
@@ -530,7 +532,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
rcu_assign_pointer(nfsi->delegation, delegation);
delegation = NULL;
- atomic_long_inc(&nfs_active_delegations);
+ atomic_long_inc(&server->nr_active_delegations);
trace_nfs4_set_delegation(inode, type);
@@ -544,7 +546,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
__nfs_free_delegation(delegation);
if (freeme != NULL) {
nfs_do_return_delegation(inode, freeme, 0);
- nfs_free_delegation(freeme);
+ nfs_free_delegation(server, freeme);
}
return status;
}
@@ -756,7 +758,7 @@ void nfs_inode_evict_delegation(struct inode *inode)
set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
nfs_do_return_delegation(inode, delegation, 1);
- nfs_free_delegation(delegation);
+ nfs_free_delegation(NFS_SERVER(inode), delegation);
}
}
@@ -842,7 +844,8 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode)
if (!delegation)
goto out;
if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) ||
- atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) {
+ atomic_long_read(&NFS_SERVER(inode)->nr_active_delegations) >=
+ nfs_delegation_watermark) {
spin_lock(&delegation->lock);
if (delegation->inode &&
list_empty(&NFS_I(inode)->open_files) &&
@@ -1018,7 +1021,7 @@ static void nfs_revoke_delegation(struct inode *inode,
}
spin_unlock(&delegation->lock);
}
- nfs_mark_delegation_revoked(delegation);
+ nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
ret = true;
out:
rcu_read_unlock();
@@ -1050,7 +1053,7 @@ void nfs_delegation_mark_returned(struct inode *inode,
delegation->stateid.seqid = stateid->seqid;
}
- nfs_mark_delegation_revoked(delegation);
+ nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
spin_unlock(&delegation->lock);
if (nfs_detach_delegation(NFS_I(inode), delegation, NFS_SERVER(inode)))
@@ -1272,7 +1275,7 @@ static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server,
if (delegation != NULL) {
if (nfs_detach_delegation(NFS_I(inode), delegation,
server) != NULL)
- nfs_free_delegation(delegation);
+ nfs_free_delegation(server, delegation);
/* Match nfs_start_delegation_return_locked */
nfs_put_delegation(delegation);
}
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 73bed04529a7..fe930d685780 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -255,6 +255,7 @@ struct nfs_server {
struct list_head state_owners_lru;
struct list_head layouts;
struct list_head delegations;
+ atomic_long_t nr_active_delegations;
struct list_head ss_copies;
struct list_head ss_src_copies;
--
2.47.2
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-14 11:16 use a hash for looking up delegation Christoph Hellwig
` (2 preceding siblings ...)
2025-07-14 11:16 ` [PATCH 3/4] NFS: track active delegations per-server Christoph Hellwig
@ 2025-07-14 11:16 ` Christoph Hellwig
2025-07-14 13:14 ` Jeff Layton
3 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-14 11:16 UTC (permalink / raw)
To: Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
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 | 23 +++++++++++++++++++----
fs/nfs/delegation.c | 15 +++++++++++++--
fs/nfs/delegation.h | 3 +++
include/linux/nfs_fs_sb.h | 2 ++
4 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index f55188928f67..94684a476dd8 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -994,6 +994,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)
@@ -1019,11 +1020,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;
@@ -1036,6 +1044,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);
@@ -1044,6 +1058,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);
}
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 621b635d1c56..ca830ceb466e 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -27,9 +27,16 @@
#define NFS_DEFAULT_DELEGATION_WATERMARK (15000U)
-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);
@@ -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 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
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) &&
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 8ff5ab9c5c25..9f1fb9b39c43 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -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
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index fe930d685780..88212306fd87 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -256,6 +256,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;
--
2.47.2
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 1/4] NFS: cleanup nfs_inode_reclaim_delegation
2025-07-14 11:16 ` [PATCH 1/4] NFS: cleanup nfs_inode_reclaim_delegation Christoph Hellwig
@ 2025-07-14 13:06 ` Jeff Layton
0 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2025-07-14 13:06 UTC (permalink / raw)
To: Christoph Hellwig, Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
On Mon, 2025-07-14 at 13:16 +0200, Christoph Hellwig wrote:
> Reduce a level of indentation for most of the code in this function.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> fs/nfs/delegation.c | 48 ++++++++++++++++++++++-----------------------
> 1 file changed, 24 insertions(+), 24 deletions(-)
>
> diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
> index 519bdc91112c..56bb2a7e1793 100644
> --- a/fs/nfs/delegation.c
> +++ b/fs/nfs/delegation.c
> @@ -237,34 +237,34 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
>
> rcu_read_lock();
> delegation = rcu_dereference(NFS_I(inode)->delegation);
> - if (delegation != NULL) {
> - spin_lock(&delegation->lock);
> - nfs4_stateid_copy(&delegation->stateid, stateid);
> - delegation->type = type;
> - delegation->pagemod_limit = pagemod_limit;
> - oldcred = delegation->cred;
> - delegation->cred = get_cred(cred);
> - switch (deleg_type) {
> - case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG:
> - case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG:
> - set_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
> - break;
> - default:
> - clear_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
> - }
> - clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
> - if (test_and_clear_bit(NFS_DELEGATION_REVOKED,
> - &delegation->flags))
> - atomic_long_inc(&nfs_active_delegations);
> - spin_unlock(&delegation->lock);
> - rcu_read_unlock();
> - put_cred(oldcred);
> - trace_nfs4_reclaim_delegation(inode, type);
> - } else {
> + if (!delegation) {
> rcu_read_unlock();
> nfs_inode_set_delegation(inode, cred, type, stateid,
> pagemod_limit, deleg_type);
> + return;
> + }
> +
> + spin_lock(&delegation->lock);
> + nfs4_stateid_copy(&delegation->stateid, stateid);
> + delegation->type = type;
> + delegation->pagemod_limit = pagemod_limit;
> + oldcred = delegation->cred;
> + delegation->cred = get_cred(cred);
> + switch (deleg_type) {
> + case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG:
> + case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG:
> + set_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
> + break;
> + default:
> + clear_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags);
> }
> + clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
> + if (test_and_clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
> + atomic_long_inc(&nfs_active_delegations);
> + spin_unlock(&delegation->lock);
> + rcu_read_unlock();
> + put_cred(oldcred);
> + trace_nfs4_reclaim_delegation(inode, type);
> }
>
> static int nfs_do_return_delegation(struct inode *inode,
Reviewed-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/4] NFS: move the delegation_watermark module parameter
2025-07-14 11:16 ` [PATCH 2/4] NFS: move the delegation_watermark module parameter Christoph Hellwig
@ 2025-07-14 13:06 ` Jeff Layton
2025-07-15 8:03 ` Christoph Hellwig
0 siblings, 1 reply; 18+ messages in thread
From: Jeff Layton @ 2025-07-14 13:06 UTC (permalink / raw)
To: Christoph Hellwig, Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
On Mon, 2025-07-14 at 13:16 +0200, Christoph Hellwig wrote:
> Keep the module_param_named next to the variable declaration instead of
> somewhere unrelated, following the best practice in the rest of the
> kernel.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> fs/nfs/delegation.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
> index 56bb2a7e1793..d036796dbe69 100644
> --- a/fs/nfs/delegation.c
> +++ b/fs/nfs/delegation.c
> @@ -29,6 +29,7 @@
>
> static atomic_long_t nfs_active_delegations;
> static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
> +module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
>
> static void __nfs_free_delegation(struct nfs_delegation *delegation)
> {
> @@ -1575,5 +1576,3 @@ bool nfs4_delegation_flush_on_close(const struct inode *inode)
> rcu_read_unlock();
> return ret;
> }
> -
> -module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
Sure, but I'd just squash this into patch #3:
Reviewed-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 3/4] NFS: track active delegations per-server
2025-07-14 11:16 ` [PATCH 3/4] NFS: track active delegations per-server Christoph Hellwig
@ 2025-07-14 13:07 ` Jeff Layton
0 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2025-07-14 13:07 UTC (permalink / raw)
To: Christoph Hellwig, Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
On Mon, 2025-07-14 at 13:16 +0200, Christoph Hellwig wrote:
> The active delegation watermark was added to avoid overloading servers.
> Track the active delegation per-server instead of globally so that clients
> talking to multiple servers aren't limited by the global limit.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> fs/nfs/client.c | 1 +
> fs/nfs/delegation.c | 35 +++++++++++++++++++----------------
> include/linux/nfs_fs_sb.h | 1 +
> 3 files changed, 21 insertions(+), 16 deletions(-)
>
> diff --git a/fs/nfs/client.c b/fs/nfs/client.c
> index 1a55debab6e5..f55188928f67 100644
> --- a/fs/nfs/client.c
> +++ b/fs/nfs/client.c
> @@ -1017,6 +1017,7 @@ struct nfs_server *nfs_alloc_server(void)
> INIT_LIST_HEAD(&server->ss_src_copies);
>
> atomic_set(&server->active, 0);
> + atomic_long_set(&server->nr_active_delegations, 0);
>
> server->io_stats = nfs_alloc_iostats();
> if (!server->io_stats) {
> diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
> index d036796dbe69..621b635d1c56 100644
> --- a/fs/nfs/delegation.c
> +++ b/fs/nfs/delegation.c
> @@ -27,7 +27,6 @@
>
> #define NFS_DEFAULT_DELEGATION_WATERMARK (15000U)
>
> -static atomic_long_t nfs_active_delegations;
> static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
> module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
>
> @@ -38,11 +37,12 @@ static void __nfs_free_delegation(struct nfs_delegation *delegation)
> kfree_rcu(delegation, rcu);
> }
>
> -static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation)
> +static void nfs_mark_delegation_revoked(struct nfs_server *server,
> + struct nfs_delegation *delegation)
> {
> if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
> delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
> - atomic_long_dec(&nfs_active_delegations);
> + atomic_long_dec(&server->nr_active_delegations);
> if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
> nfs_clear_verifier_delegated(delegation->inode);
> }
> @@ -60,9 +60,10 @@ static void nfs_put_delegation(struct nfs_delegation *delegation)
> __nfs_free_delegation(delegation);
> }
>
> -static void nfs_free_delegation(struct nfs_delegation *delegation)
> +static void nfs_free_delegation(struct nfs_server *server,
> + struct nfs_delegation *delegation)
> {
> - nfs_mark_delegation_revoked(delegation);
> + nfs_mark_delegation_revoked(server, delegation);
> nfs_put_delegation(delegation);
> }
>
> @@ -261,7 +262,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
> }
> clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
> if (test_and_clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
> - atomic_long_inc(&nfs_active_delegations);
> + atomic_long_inc(&NFS_SERVER(inode)->nr_active_delegations);
> spin_unlock(&delegation->lock);
> rcu_read_unlock();
> put_cred(oldcred);
> @@ -413,7 +414,8 @@ nfs_update_delegation_cred(struct nfs_delegation *delegation,
> }
>
> static void
> -nfs_update_inplace_delegation(struct nfs_delegation *delegation,
> +nfs_update_inplace_delegation(struct nfs_server *server,
> + struct nfs_delegation *delegation,
> const struct nfs_delegation *update)
> {
> if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) {
> @@ -426,7 +428,7 @@ nfs_update_inplace_delegation(struct nfs_delegation *delegation,
> nfs_update_delegation_cred(delegation, update->cred);
> /* smp_mb__before_atomic() is implicit due to xchg() */
> clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
> - atomic_long_inc(&nfs_active_delegations);
> + atomic_long_inc(&server->nr_active_delegations);
> }
> }
> }
> @@ -481,7 +483,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
> if (nfs4_stateid_match_other(&old_delegation->stateid,
> &delegation->stateid)) {
> spin_lock(&old_delegation->lock);
> - nfs_update_inplace_delegation(old_delegation,
> + nfs_update_inplace_delegation(server, old_delegation,
> delegation);
> spin_unlock(&old_delegation->lock);
> goto out;
> @@ -530,7 +532,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
> rcu_assign_pointer(nfsi->delegation, delegation);
> delegation = NULL;
>
> - atomic_long_inc(&nfs_active_delegations);
> + atomic_long_inc(&server->nr_active_delegations);
>
> trace_nfs4_set_delegation(inode, type);
>
> @@ -544,7 +546,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
> __nfs_free_delegation(delegation);
> if (freeme != NULL) {
> nfs_do_return_delegation(inode, freeme, 0);
> - nfs_free_delegation(freeme);
> + nfs_free_delegation(server, freeme);
> }
> return status;
> }
> @@ -756,7 +758,7 @@ void nfs_inode_evict_delegation(struct inode *inode)
> set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
> set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
> nfs_do_return_delegation(inode, delegation, 1);
> - nfs_free_delegation(delegation);
> + nfs_free_delegation(NFS_SERVER(inode), delegation);
> }
> }
>
> @@ -842,7 +844,8 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode)
> if (!delegation)
> goto out;
> if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) ||
> - atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) {
> + atomic_long_read(&NFS_SERVER(inode)->nr_active_delegations) >=
> + nfs_delegation_watermark) {
> spin_lock(&delegation->lock);
> if (delegation->inode &&
> list_empty(&NFS_I(inode)->open_files) &&
> @@ -1018,7 +1021,7 @@ static void nfs_revoke_delegation(struct inode *inode,
> }
> spin_unlock(&delegation->lock);
> }
> - nfs_mark_delegation_revoked(delegation);
> + nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
> ret = true;
> out:
> rcu_read_unlock();
> @@ -1050,7 +1053,7 @@ void nfs_delegation_mark_returned(struct inode *inode,
> delegation->stateid.seqid = stateid->seqid;
> }
>
> - nfs_mark_delegation_revoked(delegation);
> + nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
> clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
> spin_unlock(&delegation->lock);
> if (nfs_detach_delegation(NFS_I(inode), delegation, NFS_SERVER(inode)))
> @@ -1272,7 +1275,7 @@ static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server,
> if (delegation != NULL) {
> if (nfs_detach_delegation(NFS_I(inode), delegation,
> server) != NULL)
> - nfs_free_delegation(delegation);
> + nfs_free_delegation(server, delegation);
> /* Match nfs_start_delegation_return_locked */
> nfs_put_delegation(delegation);
> }
> diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
> index 73bed04529a7..fe930d685780 100644
> --- a/include/linux/nfs_fs_sb.h
> +++ b/include/linux/nfs_fs_sb.h
> @@ -255,6 +255,7 @@ struct nfs_server {
> struct list_head state_owners_lru;
> struct list_head layouts;
> struct list_head delegations;
> + atomic_long_t nr_active_delegations;
> struct list_head ss_copies;
> struct list_head ss_src_copies;
>
Good idea. I like this.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-14 11:16 ` [PATCH 4/4] NFS: use a hash table for delegation lookup Christoph Hellwig
@ 2025-07-14 13:14 ` Jeff Layton
2025-07-14 13:18 ` Christoph Hellwig
2025-07-15 8:58 ` Christoph Hellwig
0 siblings, 2 replies; 18+ messages in thread
From: Jeff Layton @ 2025-07-14 13:14 UTC (permalink / raw)
To: Christoph Hellwig, Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
On Mon, 2025-07-14 at 13:16 +0200, Christoph Hellwig wrote:
> 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 | 23 +++++++++++++++++++----
> fs/nfs/delegation.c | 15 +++++++++++++--
> fs/nfs/delegation.h | 3 +++
> include/linux/nfs_fs_sb.h | 2 ++
> 4 files changed, 37 insertions(+), 6 deletions(-)
>
> diff --git a/fs/nfs/client.c b/fs/nfs/client.c
> index f55188928f67..94684a476dd8 100644
> --- a/fs/nfs/client.c
> +++ b/fs/nfs/client.c
> @@ -994,6 +994,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)
> @@ -1019,11 +1020,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]);
> +
This is going to get created for any mount, even v3 ones. It might be
better to only bother with this for v4 mounts. Maybe do this in
nfs4_server_common_setup() instead?
Also, I wonder if you'd be better off using the rhashtable
infrastructure instead of adding a fixed-size one? The number of
delegations in flight is very workload-dependent, so a resizeable
hashtable may be a better option.
> 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;
>
> @@ -1036,6 +1044,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);
>
> @@ -1044,6 +1058,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);
> }
>
> diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
> index 621b635d1c56..ca830ceb466e 100644
> --- a/fs/nfs/delegation.c
> +++ b/fs/nfs/delegation.c
> @@ -27,9 +27,16 @@
>
> #define NFS_DEFAULT_DELEGATION_WATERMARK (15000U)
>
> -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);
> @@ -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 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
> 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) &&
> diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
> index 8ff5ab9c5c25..9f1fb9b39c43 100644
> --- a/fs/nfs/delegation.h
> +++ b/fs/nfs/delegation.h
> @@ -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
> diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
> index fe930d685780..88212306fd87 100644
> --- a/include/linux/nfs_fs_sb.h
> +++ b/include/linux/nfs_fs_sb.h
> @@ -256,6 +256,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;
>
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-14 13:14 ` Jeff Layton
@ 2025-07-14 13:18 ` Christoph Hellwig
2025-07-15 8:58 ` Christoph Hellwig
1 sibling, 0 replies; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-14 13:18 UTC (permalink / raw)
To: Jeff Layton; +Cc: Christoph Hellwig, Trond Myklebust, Anna Schumaker, linux-nfs
On Mon, Jul 14, 2025 at 09:14:27AM -0400, Jeff Layton wrote:
> > + 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]);
> > +
>
> This is going to get created for any mount, even v3 ones. It might be
> better to only bother with this for v4 mounts. Maybe do this in
> nfs4_server_common_setup() instead?
Yeah, good idea.
> Also, I wonder if you'd be better off using the rhashtable
> infrastructure instead of adding a fixed-size one? The number of
> delegations in flight is very workload-dependent, so a resizeable
> hashtable may be a better option.
I did try that first. But the rhashtable expects the hash key to
be embedded into the structure that has the rhash_head embedded into
it, while delegations use the file handle embeded into the inode as
the key. So we'd have to bloat the deleations with a key copy for
that.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/4] NFS: move the delegation_watermark module parameter
2025-07-14 13:06 ` Jeff Layton
@ 2025-07-15 8:03 ` Christoph Hellwig
2025-07-15 11:15 ` Jeff Layton
0 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-15 8:03 UTC (permalink / raw)
To: Jeff Layton; +Cc: Christoph Hellwig, Trond Myklebust, Anna Schumaker, linux-nfs
On Mon, Jul 14, 2025 at 09:06:42AM -0400, Jeff Layton wrote:
> > -
> > -module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
>
> Sure, but I'd just squash this into patch #3:
Why? It's completely unrelated to the changes there.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-14 13:14 ` Jeff Layton
2025-07-14 13:18 ` Christoph Hellwig
@ 2025-07-15 8:58 ` Christoph Hellwig
2025-07-15 11:19 ` Jeff Layton
1 sibling, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-15 8:58 UTC (permalink / raw)
To: Jeff Layton; +Cc: Christoph Hellwig, Trond Myklebust, Anna Schumaker, linux-nfs
On Mon, Jul 14, 2025 at 09:14:27AM -0400, Jeff Layton wrote:
> > + 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]);
> > +
>
> This is going to get created for any mount, even v3 ones. It might be
> better to only bother with this for v4 mounts. Maybe do this in
> nfs4_server_common_setup() instead?
I tried that, but it crashes because the usual mount process goes
through nfs_clone_server, which then doesn't set up the hash table.
I think the best idea is to pass the version to nfs_allocate_server
and just make this code conditional, but I'm open to other suggestions.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/4] NFS: move the delegation_watermark module parameter
2025-07-15 8:03 ` Christoph Hellwig
@ 2025-07-15 11:15 ` Jeff Layton
0 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2025-07-15 11:15 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Trond Myklebust, Anna Schumaker, linux-nfs
On Tue, 2025-07-15 at 10:03 +0200, Christoph Hellwig wrote:
> On Mon, Jul 14, 2025 at 09:06:42AM -0400, Jeff Layton wrote:
> > > -
> > > -module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
> >
> > Sure, but I'd just squash this into patch #3:
>
> Why? It's completely unrelated to the changes there.
It's a trivial change and patch #3 makes changes in the same area. It's
not a big deal though.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-15 8:58 ` Christoph Hellwig
@ 2025-07-15 11:19 ` Jeff Layton
0 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2025-07-15 11:19 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Trond Myklebust, Anna Schumaker, linux-nfs
On Tue, 2025-07-15 at 10:58 +0200, Christoph Hellwig wrote:
> On Mon, Jul 14, 2025 at 09:14:27AM -0400, Jeff Layton wrote:
> > > + 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]);
> > > +
> >
> > This is going to get created for any mount, even v3 ones. It might be
> > better to only bother with this for v4 mounts. Maybe do this in
> > nfs4_server_common_setup() instead?
>
> I tried that, but it crashes because the usual mount process goes
> through nfs_clone_server, which then doesn't set up the hash table.
>
> I think the best idea is to pass the version to nfs_allocate_server
> and just make this code conditional, but I'm open to other suggestions.
The other thing you could do is move the allocation and setup into a
helper function, and have nfs_clone_server() call that if the source
server has a hashtable present.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-16 13:26 use a hash for looking up delegation v2 Christoph Hellwig
@ 2025-07-16 13:26 ` Christoph Hellwig
2025-07-17 4:35 ` kernel test robot
0 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-16 13:26 UTC (permalink / raw)
To: Trond Myklebust; +Cc: Anna Schumaker, linux-nfs
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 | 14 +++++++++++---
fs/nfs/delegation.c | 28 +++++++++++++++++++++++++++-
fs/nfs/delegation.h | 3 +++
fs/nfs/nfs4client.c | 10 +++++++++-
include/linux/nfs_fs_sb.h | 2 ++
5 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index e13eb429b8b5..5e1a5263e326 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -1158,6 +1158,12 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
server->fsid = fattr->fsid;
+ if (IS_ENABLED(CONFIG_NFS_V4) && server->delegation_hash_table) {
+ error = nfs4_delegation_hash_alloc(server);
+ if (error)
+ goto out_free_server;
+ }
+
nfs_sysfs_add_server(server);
nfs_sysfs_link_rpc_client(server,
@@ -1167,25 +1173,27 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
source->client->cl_timeout,
flavor);
if (error < 0)
- goto out_free_server;
+ goto out_free_delegation_hash;
/* probe the filesystem info for this server filesystem */
error = nfs_probe_server(server, fh);
if (error < 0)
- goto out_free_server;
+ goto out_free_delegation_hash;
if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
server->namelen = NFS4_MAXNAMLEN;
error = nfs_start_lockd(server);
if (error < 0)
- goto out_free_server;
+ goto out_free_delegation_hash;
nfs_server_insert_lists(server);
server->mount_time = jiffies;
return server;
+out_free_delegation_hash:
+ kfree(server->delegation_hash_table);
out_free_server:
nfs_free_server(server);
return ERR_PTR(error);
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index ea96f77e38c2..9d3a5f29f17f 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -30,6 +30,13 @@
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 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
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 @@ bool nfs4_delegation_flush_on_close(const struct inode *inode)
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;
+}
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 8ff5ab9c5c25..08ec2e9c68a4 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -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
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 2e623da1a787..31dc0d40209a 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -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,9 +1097,14 @@ 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 */
+ error = -EPROTONOSUPPORT;
if (is_ds_only_client(server->nfs_client))
- return -EPROTONOSUPPORT;
+ goto out;
/* We must ensure the session is initialised first */
error = nfs4_init_session(server->nfs_client);
@@ -1130,7 +1136,9 @@ static int nfs4_server_common_setup(struct nfs_server *server,
nfs_server_insert_lists(server);
server->mount_time = jiffies;
server->destroy = nfs4_destroy_server;
+ return 0;
out:
+ kfree(server->delegation_hash_table);
return error;
}
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index a9b44f12623f..d30c0245031c 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -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;
--
2.47.2
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-16 13:26 ` [PATCH 4/4] NFS: use a hash table for delegation lookup Christoph Hellwig
@ 2025-07-17 4:35 ` kernel test robot
2025-07-17 5:13 ` Christoph Hellwig
0 siblings, 1 reply; 18+ messages in thread
From: kernel test robot @ 2025-07-17 4:35 UTC (permalink / raw)
To: Christoph Hellwig, Trond Myklebust
Cc: oe-kbuild-all, Anna Schumaker, linux-nfs
Hi Christoph,
kernel test robot noticed the following build errors:
[auto build test ERROR on trondmy-nfs/linux-next]
[also build test ERROR on linus/master v6.16-rc6 next-20250716]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Christoph-Hellwig/NFS-move-the-delegation_watermark-module-parameter/20250716-222708
base: git://git.linux-nfs.org/projects/trondmy/linux-nfs.git linux-next
patch link: https://lore.kernel.org/r/20250716132657.2167548-5-hch%40lst.de
patch subject: [PATCH 4/4] NFS: use a hash table for delegation lookup
config: i386-randconfig-013-20250717 (https://download.01.org/0day-ci/archive/20250717/202507171246.w67NRPWN-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250717/202507171246.w67NRPWN-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507171246.w67NRPWN-lkp@intel.com/
All errors (new ones prefixed by >>):
ld: fs/nfs/client.o: in function `nfs_clone_server':
>> fs/nfs/client.c:1162: undefined reference to `nfs4_delegation_hash_alloc'
vim +1162 fs/nfs/client.c
1135
1136 /*
1137 * Clone an NFS2, NFS3 or NFS4 server record
1138 */
1139 struct nfs_server *nfs_clone_server(struct nfs_server *source,
1140 struct nfs_fh *fh,
1141 struct nfs_fattr *fattr,
1142 rpc_authflavor_t flavor)
1143 {
1144 struct nfs_server *server;
1145 int error;
1146
1147 server = nfs_alloc_server();
1148 if (!server)
1149 return ERR_PTR(-ENOMEM);
1150
1151 server->cred = get_cred(source->cred);
1152
1153 /* Copy data from the source */
1154 server->nfs_client = source->nfs_client;
1155 server->destroy = source->destroy;
1156 refcount_inc(&server->nfs_client->cl_count);
1157 nfs_server_copy_userdata(server, source);
1158
1159 server->fsid = fattr->fsid;
1160
1161 if (IS_ENABLED(CONFIG_NFS_V4) && server->delegation_hash_table) {
> 1162 error = nfs4_delegation_hash_alloc(server);
1163 if (error)
1164 goto out_free_server;
1165 }
1166
1167 nfs_sysfs_add_server(server);
1168
1169 nfs_sysfs_link_rpc_client(server,
1170 server->nfs_client->cl_rpcclient, "_state");
1171
1172 error = nfs_init_server_rpcclient(server,
1173 source->client->cl_timeout,
1174 flavor);
1175 if (error < 0)
1176 goto out_free_delegation_hash;
1177
1178 /* probe the filesystem info for this server filesystem */
1179 error = nfs_probe_server(server, fh);
1180 if (error < 0)
1181 goto out_free_delegation_hash;
1182
1183 if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
1184 server->namelen = NFS4_MAXNAMLEN;
1185
1186 error = nfs_start_lockd(server);
1187 if (error < 0)
1188 goto out_free_delegation_hash;
1189
1190 nfs_server_insert_lists(server);
1191 server->mount_time = jiffies;
1192
1193 return server;
1194
1195 out_free_delegation_hash:
1196 kfree(server->delegation_hash_table);
1197 out_free_server:
1198 nfs_free_server(server);
1199 return ERR_PTR(error);
1200 }
1201 EXPORT_SYMBOL_GPL(nfs_clone_server);
1202
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-17 4:35 ` kernel test robot
@ 2025-07-17 5:13 ` Christoph Hellwig
2025-07-17 16:00 ` Trond Myklebust
0 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2025-07-17 5:13 UTC (permalink / raw)
To: Trond Myklebust, Anna Schumaker, Jeff Layton; +Cc: linux-nfs
So nfs4 can be built modular, and delegation.o is built into that,
i.e. we can't call into deletation code from clone_server.
Back to passing a major number to nfs_alloc_server?
On Thu, Jul 17, 2025 at 12:35:07PM +0800, kernel test robot wrote:
> Hi Christoph,
>
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on trondmy-nfs/linux-next]
> [also build test ERROR on linus/master v6.16-rc6 next-20250716]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Christoph-Hellwig/NFS-move-the-delegation_watermark-module-parameter/20250716-222708
> base: git://git.linux-nfs.org/projects/trondmy/linux-nfs.git linux-next
> patch link: https://lore.kernel.org/r/20250716132657.2167548-5-hch%40lst.de
> patch subject: [PATCH 4/4] NFS: use a hash table for delegation lookup
> config: i386-randconfig-013-20250717 (https://download.01.org/0day-ci/archive/20250717/202507171246.w67NRPWN-lkp@intel.com/config)
> compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250717/202507171246.w67NRPWN-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202507171246.w67NRPWN-lkp@intel.com/
>
> All errors (new ones prefixed by >>):
>
> ld: fs/nfs/client.o: in function `nfs_clone_server':
> >> fs/nfs/client.c:1162: undefined reference to `nfs4_delegation_hash_alloc'
>
>
> vim +1162 fs/nfs/client.c
>
> 1135
> 1136 /*
> 1137 * Clone an NFS2, NFS3 or NFS4 server record
> 1138 */
> 1139 struct nfs_server *nfs_clone_server(struct nfs_server *source,
> 1140 struct nfs_fh *fh,
> 1141 struct nfs_fattr *fattr,
> 1142 rpc_authflavor_t flavor)
> 1143 {
> 1144 struct nfs_server *server;
> 1145 int error;
> 1146
> 1147 server = nfs_alloc_server();
> 1148 if (!server)
> 1149 return ERR_PTR(-ENOMEM);
> 1150
> 1151 server->cred = get_cred(source->cred);
> 1152
> 1153 /* Copy data from the source */
> 1154 server->nfs_client = source->nfs_client;
> 1155 server->destroy = source->destroy;
> 1156 refcount_inc(&server->nfs_client->cl_count);
> 1157 nfs_server_copy_userdata(server, source);
> 1158
> 1159 server->fsid = fattr->fsid;
> 1160
> 1161 if (IS_ENABLED(CONFIG_NFS_V4) && server->delegation_hash_table) {
> > 1162 error = nfs4_delegation_hash_alloc(server);
> 1163 if (error)
> 1164 goto out_free_server;
> 1165 }
> 1166
> 1167 nfs_sysfs_add_server(server);
> 1168
> 1169 nfs_sysfs_link_rpc_client(server,
> 1170 server->nfs_client->cl_rpcclient, "_state");
> 1171
> 1172 error = nfs_init_server_rpcclient(server,
> 1173 source->client->cl_timeout,
> 1174 flavor);
> 1175 if (error < 0)
> 1176 goto out_free_delegation_hash;
> 1177
> 1178 /* probe the filesystem info for this server filesystem */
> 1179 error = nfs_probe_server(server, fh);
> 1180 if (error < 0)
> 1181 goto out_free_delegation_hash;
> 1182
> 1183 if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
> 1184 server->namelen = NFS4_MAXNAMLEN;
> 1185
> 1186 error = nfs_start_lockd(server);
> 1187 if (error < 0)
> 1188 goto out_free_delegation_hash;
> 1189
> 1190 nfs_server_insert_lists(server);
> 1191 server->mount_time = jiffies;
> 1192
> 1193 return server;
> 1194
> 1195 out_free_delegation_hash:
> 1196 kfree(server->delegation_hash_table);
> 1197 out_free_server:
> 1198 nfs_free_server(server);
> 1199 return ERR_PTR(error);
> 1200 }
> 1201 EXPORT_SYMBOL_GPL(nfs_clone_server);
> 1202
>
> --
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki
---end quoted text---
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] NFS: use a hash table for delegation lookup
2025-07-17 5:13 ` Christoph Hellwig
@ 2025-07-17 16:00 ` Trond Myklebust
0 siblings, 0 replies; 18+ messages in thread
From: Trond Myklebust @ 2025-07-17 16:00 UTC (permalink / raw)
To: Christoph Hellwig, Anna Schumaker, Jeff Layton; +Cc: linux-nfs
On Thu, 2025-07-17 at 07:13 +0200, Christoph Hellwig wrote:
> So nfs4 can be built modular, and delegation.o is built into that,
> i.e. we can't call into deletation code from clone_server.
>
> Back to passing a major number to nfs_alloc_server?
Why not just build a small wrapper in fs/nfs/nfs4proc.c that does the
allocation after calling the regular nfs_clone_server(), and substitute
that into the nfs_v4_clientops.clone_server callback?
>
> On Thu, Jul 17, 2025 at 12:35:07PM +0800, kernel test robot wrote:
> > Hi Christoph,
> >
> > kernel test robot noticed the following build errors:
> >
> > [auto build test ERROR on trondmy-nfs/linux-next]
> > [also build test ERROR on linus/master v6.16-rc6 next-20250716]
> > [If your patch is applied to the wrong git tree, kindly drop us a
> > note.
> > And when submitting patch, we suggest to use '--base' as documented
> > in
--
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trondmy@kernel.org, trond.myklebust@hammerspace.com
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2025-07-17 16:00 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-14 11:16 use a hash for looking up delegation Christoph Hellwig
2025-07-14 11:16 ` [PATCH 1/4] NFS: cleanup nfs_inode_reclaim_delegation Christoph Hellwig
2025-07-14 13:06 ` Jeff Layton
2025-07-14 11:16 ` [PATCH 2/4] NFS: move the delegation_watermark module parameter Christoph Hellwig
2025-07-14 13:06 ` Jeff Layton
2025-07-15 8:03 ` Christoph Hellwig
2025-07-15 11:15 ` Jeff Layton
2025-07-14 11:16 ` [PATCH 3/4] NFS: track active delegations per-server Christoph Hellwig
2025-07-14 13:07 ` Jeff Layton
2025-07-14 11:16 ` [PATCH 4/4] NFS: use a hash table for delegation lookup Christoph Hellwig
2025-07-14 13:14 ` Jeff Layton
2025-07-14 13:18 ` Christoph Hellwig
2025-07-15 8:58 ` Christoph Hellwig
2025-07-15 11:19 ` Jeff Layton
-- strict thread matches above, loose matches on Subject: below --
2025-07-16 13:26 use a hash for looking up delegation v2 Christoph Hellwig
2025-07-16 13:26 ` [PATCH 4/4] NFS: use a hash table for delegation lookup Christoph Hellwig
2025-07-17 4:35 ` kernel test robot
2025-07-17 5:13 ` Christoph Hellwig
2025-07-17 16:00 ` Trond Myklebust
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox