From 70f1e6aa3f211b70418046a66b2ebd71069ec563 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:26:52 +0100 Subject: [PATCH 01/60] NFS: remove __nfs_client_for_each_server __nfs_client_for_each_server is only called by nfs_client_for_each_server, so merge the two. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/super.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 57d372db03b9..e74164d9c081 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -212,15 +212,14 @@ void nfs_sb_deactive(struct super_block *sb) } EXPORT_SYMBOL_GPL(nfs_sb_deactive); -static int __nfs_list_for_each_server(struct list_head *head, - int (*fn)(struct nfs_server *, void *), - void *data) +int nfs_client_for_each_server(struct nfs_client *clp, + int (*fn)(struct nfs_server *server, void *data), void *data) { struct nfs_server *server, *last = NULL; int ret = 0; rcu_read_lock(); - list_for_each_entry_rcu(server, head, client_link) { + list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { if (!(server->super && nfs_sb_active(server->super))) continue; rcu_read_unlock(); @@ -239,13 +238,6 @@ static int __nfs_list_for_each_server(struct list_head *head, nfs_sb_deactive(last->super); return ret; } - -int nfs_client_for_each_server(struct nfs_client *clp, - int (*fn)(struct nfs_server *, void *), - void *data) -{ - return __nfs_list_for_each_server(&clp->cl_superblocks, fn, data); -} EXPORT_SYMBOL_GPL(nfs_client_for_each_server); /* From f8abad38684c43ce00fefc6858d16034f0f359bb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:26:53 +0100 Subject: [PATCH 02/60] NFS: remove nfs_client_mark_return_unused_delegation_types nfs_client_mark_return_unused_delegation_types is only called by nfs_expire_unused_delegation_types, so merge the two. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 8a3857a49d84..e139cc47dda2 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -1006,8 +1006,7 @@ static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, } } -static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, - fmode_t flags) +void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) { struct nfs_server *server; @@ -1015,6 +1014,8 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) nfs_mark_return_unused_delegation_types(server, flags); rcu_read_unlock(); + + nfs_delegation_run_state_manager(clp); } static void nfs_revoke_delegation(struct inode *inode, @@ -1111,18 +1112,6 @@ void nfs_remove_bad_delegation(struct inode *inode, } EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); -/** - * nfs_expire_unused_delegation_types - * @clp: client to process - * @flags: delegation types to expire - * - */ -void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) -{ - nfs_client_mark_return_unused_delegation_types(clp, flags); - nfs_delegation_run_state_manager(clp); -} - static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) { struct nfs_delegation *delegation; From 45875b7efb8a805414fe3d74f276e5fa9cf3e34e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:26:54 +0100 Subject: [PATCH 03/60] NFS: remove nfs_client_mark_return_all_delegations Fold nfs_client_mark_return_all_delegations into nfs_expire_all_delegations, which is the only caller. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index e139cc47dda2..95f410d65e3c 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -943,16 +943,6 @@ static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) return ret; } -static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) -{ - struct nfs_server *server; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs_server_mark_return_all_delegations(server); - rcu_read_unlock(); -} - static void nfs_delegation_run_state_manager(struct nfs_client *clp) { if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) @@ -966,7 +956,13 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp) */ void nfs_expire_all_delegations(struct nfs_client *clp) { - nfs_client_mark_return_all_delegations(clp); + struct nfs_server *server; + + rcu_read_lock(); + list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) + nfs_server_mark_return_all_delegations(server); + rcu_read_unlock(); + nfs_delegation_run_state_manager(clp); } From c6c9b9bc9f6aaba1d518c7b4bcdbbd9171ce82cb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:26:55 +0100 Subject: [PATCH 04/60] NFS: remove the NULL inode check in nfs4_inode_return_delegation_on_close The only caller dereferences a field in the inode just before calling nfs4_inode_return_delegation_on_close. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 95f410d65e3c..937d5d9f933f 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -860,8 +860,6 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) struct nfs_delegation *delegation; struct nfs_delegation *ret = NULL; - if (!inode) - return; rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); if (!delegation) From ee443e116151fbcf08e2296b21f28f942b18c2ed Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:26:56 +0100 Subject: [PATCH 05/60] NFS: remove nfs_inode_detach_delegation Fold it into the only caller. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 937d5d9f933f..86b8da1e2598 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -402,21 +402,6 @@ static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, return delegation; } -static struct nfs_delegation * -nfs_inode_detach_delegation(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_delegation *delegation; - - rcu_read_lock(); - delegation = rcu_dereference(nfsi->delegation); - if (delegation != NULL) - delegation = nfs_detach_delegation(nfsi, delegation, server); - rcu_read_unlock(); - return delegation; -} - static void nfs_update_delegation_cred(struct nfs_delegation *delegation, const struct cred *cred) @@ -774,15 +759,23 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp) */ void nfs_inode_evict_delegation(struct inode *inode) { + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_server *server = NFS_SERVER(inode); struct nfs_delegation *delegation; - delegation = nfs_inode_detach_delegation(inode); - if (delegation != NULL) { - 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(NFS_SERVER(inode), delegation); - } + rcu_read_lock(); + delegation = rcu_dereference(nfsi->delegation); + if (delegation) + delegation = nfs_detach_delegation(nfsi, delegation, server); + rcu_read_unlock(); + + if (!delegation) + return; + + 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(server, delegation); } /** From 635879a427b855db477e293514f74ffbfa920794 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:26:57 +0100 Subject: [PATCH 06/60] NFS: remove nfs_start_delegation_return There is only one caller, so fold it into that. With that, nfs_start_delegation_return Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 86b8da1e2598..1f899edd427f 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -336,17 +336,6 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi) return ret; } -static struct nfs_delegation * -nfs_start_delegation_return(struct nfs_inode *nfsi) -{ - struct nfs_delegation *delegation; - - rcu_read_lock(); - delegation = nfs_start_delegation_return_locked(nfsi); - rcu_read_unlock(); - return delegation; -} - static void nfs_abort_delegation_return(struct nfs_delegation *delegation, struct nfs_server *server, int err) { @@ -793,15 +782,18 @@ int nfs4_inode_return_delegation(struct inode *inode) struct nfs_inode *nfsi = NFS_I(inode); struct nfs_delegation *delegation; - delegation = nfs_start_delegation_return(nfsi); - if (delegation != NULL) { - /* Synchronous recall of any application leases */ - break_lease(inode, O_WRONLY | O_RDWR); - if (S_ISREG(inode->i_mode)) - nfs_wb_all(inode); - return nfs_end_delegation_return(inode, delegation, 1); - } - return 0; + rcu_read_lock(); + delegation = nfs_start_delegation_return_locked(nfsi); + rcu_read_unlock(); + + if (!delegation) + return 0; + + /* Synchronous recall of any application leases */ + break_lease(inode, O_WRONLY | O_RDWR); + if (S_ISREG(inode->i_mode)) + nfs_wb_all(inode); + return nfs_end_delegation_return(inode, delegation, 1); } /** From 7c80c3b8c80bc79f49c978c7d6d5166bc61816b1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:26:58 +0100 Subject: [PATCH 07/60] NFS: assert rcu_read_lock is held in nfs_start_delegation_return_locked And clean up the dereference of the delegation a bit. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 1f899edd427f..b90d30bf7a31 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -318,10 +318,14 @@ static struct nfs_delegation * nfs_start_delegation_return_locked(struct nfs_inode *nfsi) { struct nfs_delegation *ret = NULL; - struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); + struct nfs_delegation *delegation; + + lockdep_assert_in_rcu_read_lock(); + + delegation = rcu_dereference(nfsi->delegation); + if (!delegation) + return NULL; - if (delegation == NULL) - goto out; spin_lock(&delegation->lock); if (delegation->inode && !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { @@ -332,7 +336,6 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi) spin_unlock(&delegation->lock); if (ret) nfs_clear_verifier_delegated(&nfsi->vfs_inode); -out: return ret; } From 0513044b749db4e20800da0c44e4299e1c7ed50a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:26:59 +0100 Subject: [PATCH 08/60] NFS: drop the _locked postfix from nfs_start_delegation_return Now that nfs_start_delegation_return_locked is gone, and we have RCU locking asserts, drop the extra postfix. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index b90d30bf7a31..47ecf67ace91 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -315,7 +315,7 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation } static struct nfs_delegation * -nfs_start_delegation_return_locked(struct nfs_inode *nfsi) +nfs_start_delegation_return(struct nfs_inode *nfsi) { struct nfs_delegation *ret = NULL; struct nfs_delegation *delegation; @@ -588,7 +588,7 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation out_return: err = nfs_do_return_delegation(inode, delegation, issync); out: - /* Refcount matched in nfs_start_delegation_return_locked() */ + /* Refcount matched in nfs_start_delegation_return() */ nfs_put_delegation(delegation); return err; } @@ -663,7 +663,7 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server, } } - delegation = nfs_start_delegation_return_locked(NFS_I(inode)); + delegation = nfs_start_delegation_return(NFS_I(inode)); rcu_read_unlock(); iput(to_put); @@ -786,7 +786,7 @@ int nfs4_inode_return_delegation(struct inode *inode) struct nfs_delegation *delegation; rcu_read_lock(); - delegation = nfs_start_delegation_return_locked(nfsi); + delegation = nfs_start_delegation_return(nfsi); rcu_read_unlock(); if (!delegation) @@ -1263,13 +1263,13 @@ static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server, inode = nfs_delegation_grab_inode(delegation); if (inode == NULL) continue; - delegation = nfs_start_delegation_return_locked(NFS_I(inode)); + delegation = nfs_start_delegation_return(NFS_I(inode)); rcu_read_unlock(); if (delegation != NULL) { if (nfs_detach_delegation(NFS_I(inode), delegation, server) != NULL) nfs_free_delegation(server, delegation); - /* Match nfs_start_delegation_return_locked */ + /* Match nfs_start_delegation_return */ nfs_put_delegation(delegation); } iput(inode); From 86ac1b7b616bbc33f886882655d31068f0b14b8d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:00 +0100 Subject: [PATCH 09/60] NFS: remove NFS_DELEGATION_INODE_FREEING This essentially reverts commit 6f9449be53f3 ("NFS: Fix a soft lockup in the delegation recovery code") because the code walking the per-server delegation list has been fixed to just skip inodes for which nfs_delegation_grab_inode fails, instead of having to restart the entire series in commit f92214e4c312 ("NFS: Avoid unnecessary rescanning of the per-server delegation list"). Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 13 ++----------- fs/nfs/delegation.h | 1 - fs/nfs/nfs4trace.h | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 47ecf67ace91..7b05d661775b 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -308,8 +308,6 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation spin_lock(&delegation->lock); if (delegation->inode != NULL) inode = igrab(delegation->inode); - if (!inode) - set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); spin_unlock(&delegation->lock); return inode; } @@ -643,8 +641,6 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server, list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { struct inode *to_put = NULL; - if (test_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags)) - continue; if (!nfs_delegation_need_return(delegation)) { if (nfs4_is_valid_delegation(delegation, 0)) prev = delegation; @@ -765,7 +761,6 @@ void nfs_inode_evict_delegation(struct inode *inode) return; 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(server, delegation); } @@ -1253,9 +1248,7 @@ static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server, restart: rcu_read_lock(); list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_bit(NFS_DELEGATION_INODE_FREEING, - &delegation->flags) || - test_bit(NFS_DELEGATION_RETURNING, + if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) @@ -1390,9 +1383,7 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server, restart: rcu_read_lock(); list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_bit(NFS_DELEGATION_INODE_FREEING, - &delegation->flags) || - test_bit(NFS_DELEGATION_RETURNING, + if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags) == 0 || diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 46d866adb5c2..fef1f4126e8f 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -37,7 +37,6 @@ enum { NFS_DELEGATION_RETURNING, NFS_DELEGATION_REVOKED, NFS_DELEGATION_TEST_EXPIRED, - NFS_DELEGATION_INODE_FREEING, NFS_DELEGATION_RETURN_DELAYED, NFS_DELEGATION_DELEGTIME, }; diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h index 6285128e631a..18d02b4715bb 100644 --- a/fs/nfs/nfs4trace.h +++ b/fs/nfs/nfs4trace.h @@ -996,7 +996,6 @@ DEFINE_NFS4_SET_DELEGATION_EVENT(nfs4_detach_delegation); { BIT(NFS_DELEGATION_RETURNING), "RETURNING" }, \ { BIT(NFS_DELEGATION_REVOKED), "REVOKED" }, \ { BIT(NFS_DELEGATION_TEST_EXPIRED), "TEST_EXPIRED" }, \ - { BIT(NFS_DELEGATION_INODE_FREEING), "INODE_FREEING" }, \ { BIT(NFS_DELEGATION_RETURN_DELAYED), "RETURN_DELAYED" }) DECLARE_EVENT_CLASS(nfs4_delegation_event, From 8cb32b9344f65dbdcb947745f812173d4bb65601 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:01 +0100 Subject: [PATCH 10/60] NFS: open code nfs_delegation_need_return There is only a single caller, and the function can be condensed into a single if statement, making it more clear what is being tested there. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 7b05d661775b..bf1dcf186a47 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -591,22 +591,6 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation return err; } -static bool nfs_delegation_need_return(struct nfs_delegation *delegation) -{ - bool ret = false; - - trace_nfs_delegation_need_return(delegation); - - if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) - ret = true; - if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || - test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || - test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) - ret = false; - - return ret; -} - static int nfs_server_return_marked_delegations(struct nfs_server *server, void __always_unused *data) { @@ -641,11 +625,17 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server, list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { struct inode *to_put = NULL; - if (!nfs_delegation_need_return(delegation)) { + trace_nfs_delegation_need_return(delegation); + + if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags) || + test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || + test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || + test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { if (nfs4_is_valid_delegation(delegation, 0)) prev = delegation; continue; } + inode = nfs_delegation_grab_inode(delegation); if (inode == NULL) continue; From 20151c11364ec9e0110413e7098593bf7c1db83f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:02 +0100 Subject: [PATCH 11/60] NFS: remove nfs_free_delegation Open code nfs_free_delegation in the callers, because having a "free" function that wraps a revoke and put operation is a bit confusing, especially when the __free version does the actual freeing triggered by the last put. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index bf1dcf186a47..f56b1d29650a 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -72,13 +72,6 @@ static void nfs_put_delegation(struct nfs_delegation *delegation) __nfs_free_delegation(delegation); } -static void nfs_free_delegation(struct nfs_server *server, - struct nfs_delegation *delegation) -{ - nfs_mark_delegation_revoked(server, delegation); - nfs_put_delegation(delegation); -} - /** * nfs_mark_delegation_referenced - set delegation's REFERENCED flag * @delegation: delegation to process @@ -539,7 +532,8 @@ 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(server, freeme); + nfs_mark_delegation_revoked(server, freeme); + nfs_put_delegation(freeme); } return status; } @@ -752,7 +746,8 @@ void nfs_inode_evict_delegation(struct inode *inode) set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); nfs_do_return_delegation(inode, delegation, 1); - nfs_free_delegation(server, delegation); + nfs_mark_delegation_revoked(server, delegation); + nfs_put_delegation(delegation); } /** @@ -1250,8 +1245,10 @@ static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server, rcu_read_unlock(); if (delegation != NULL) { if (nfs_detach_delegation(NFS_I(inode), delegation, - server) != NULL) - nfs_free_delegation(server, delegation); + server) != NULL) { + nfs_mark_delegation_revoked(server, delegation); + nfs_put_delegation(delegation); + } /* Match nfs_start_delegation_return */ nfs_put_delegation(delegation); } From 2d80e59f29d8cd14230899dfe9c462b710521a85 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:03 +0100 Subject: [PATCH 12/60] NFS: rewrite nfs_delegations_present in terms of nr_active_delegations Renewal only cares for active delegations and not revoked ones. Replace the list empty check with reading the active delegation counter to implement this. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index f56b1d29650a..8e5cb8fc3f32 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -1453,7 +1453,7 @@ int nfs_delegations_present(struct nfs_client *clp) rcu_read_lock(); list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - if (!list_empty(&server->delegations)) { + if (atomic_long_read(&server->nr_active_delegations) > 0) { ret = 1; break; } From 3e39019e687a84ffe8b784e8a9c911bdb4d7d26c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:04 +0100 Subject: [PATCH 13/60] NFS: move delegation lookup into can_open_delegated Keep the delegation handling in a single place, and just return the stateid in an optional argument. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 65 ++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a0885ae55abc..5adaca5e476c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1609,26 +1609,37 @@ static int can_open_cached(struct nfs4_state *state, fmode_t mode, return ret; } -static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode, - enum open_claim_type4 claim) +static bool can_open_delegated(const struct inode *inode, fmode_t fmode, + enum open_claim_type4 claim, nfs4_stateid *stateid) { - if (delegation == NULL) - return 0; - if ((delegation->type & fmode) != fmode) - return 0; + struct nfs_delegation *delegation; + bool ret = false; + + rcu_read_lock(); + delegation = nfs4_get_valid_delegation(inode); + if (!delegation || (delegation->type & fmode) != fmode) + goto out_unlock; + switch (claim) { - case NFS4_OPEN_CLAIM_NULL: - case NFS4_OPEN_CLAIM_FH: - break; case NFS4_OPEN_CLAIM_PREVIOUS: - if (!test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags)) + if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags)) break; fallthrough; + case NFS4_OPEN_CLAIM_NULL: + case NFS4_OPEN_CLAIM_FH: + nfs_mark_delegation_referenced(delegation); + /* Save the delegation stateid */ + if (stateid) + nfs4_stateid_copy(stateid, &delegation->stateid); + ret = true; + break; default: - return 0; + break; } - nfs_mark_delegation_referenced(delegation); - return 1; + +out_unlock: + rcu_read_unlock(); + return ret; } static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode) @@ -1981,7 +1992,6 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) { struct nfs4_state *state = opendata->state; - struct nfs_delegation *delegation; int open_mode = opendata->o_arg.open_flags; fmode_t fmode = opendata->o_arg.fmode; enum open_claim_type4 claim = opendata->o_arg.claim; @@ -1996,15 +2006,10 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) goto out_return_state; } spin_unlock(&state->owner->so_lock); - rcu_read_lock(); - delegation = nfs4_get_valid_delegation(state->inode); - if (!can_open_delegated(delegation, fmode, claim)) { - rcu_read_unlock(); + + if (!can_open_delegated(state->inode, fmode, claim, &stateid)) break; - } - /* Save the delegation */ - nfs4_stateid_copy(&stateid, &delegation->stateid); - rcu_read_unlock(); + nfs_release_seqid(opendata->o_arg.seqid); if (!opendata->is_recover) { ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode); @@ -2556,16 +2561,14 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) * a delegation instead. */ if (data->state != NULL) { - struct nfs_delegation *delegation; - if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags, claim)) goto out_no_action; - rcu_read_lock(); - delegation = nfs4_get_valid_delegation(data->state->inode); - if (can_open_delegated(delegation, data->o_arg.fmode, claim)) - goto unlock_no_action; - rcu_read_unlock(); + if (can_open_delegated(data->state->inode, data->o_arg.fmode, + claim, NULL)) { + trace_nfs4_cached_open(data->state); + goto out_no_action; + } } /* Update client id. */ data->o_arg.clientid = clp->cl_clientid; @@ -2601,9 +2604,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) data->o_arg.createmode = NFS4_CREATE_GUARDED; } return; -unlock_no_action: - trace_nfs4_cached_open(data->state); - rcu_read_unlock(); + out_no_action: task->tk_action = NULL; out_wait: From 8f7e0b808067afaca0d370c49824c5393101302c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:05 +0100 Subject: [PATCH 14/60] NFS: return bool from nfs_detach_delegation{,_locked} nfs_detach_delegation always returns either the passed in delegation or NULL, simplify this to a bool return. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 8e5cb8fc3f32..762d79ee58e3 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -345,7 +345,7 @@ static void nfs_abort_delegation_return(struct nfs_delegation *delegation, spin_unlock(&delegation->lock); } -static struct nfs_delegation * +static bool nfs_detach_delegation_locked(struct nfs_inode *nfsi, struct nfs_delegation *delegation, struct nfs_client *clp) @@ -356,13 +356,13 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi, trace_nfs4_detach_delegation(&nfsi->vfs_inode, delegation->type); - if (deleg_cur == NULL || delegation != deleg_cur) - return NULL; + if (delegation != deleg_cur) + return false; spin_lock(&delegation->lock); if (!delegation->inode) { spin_unlock(&delegation->lock); - return NULL; + return false; } hlist_del_init_rcu(&delegation->hash); list_del_rcu(&delegation->super_list); @@ -370,19 +370,20 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi, rcu_assign_pointer(nfsi->delegation, NULL); spin_unlock(&delegation->lock); clear_bit(NFS_INO_REQ_DIR_DELEG, &nfsi->flags); - return delegation; + return true; } -static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, +static bool nfs_detach_delegation(struct nfs_inode *nfsi, struct nfs_delegation *delegation, struct nfs_server *server) { struct nfs_client *clp = server->nfs_client; + bool ret; spin_lock(&clp->cl_lock); - delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); + ret = nfs_detach_delegation_locked(nfsi, delegation, clp); spin_unlock(&clp->cl_lock); - return delegation; + return ret; } static void @@ -492,9 +493,9 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, &old_delegation->flags)) goto out; } - freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp); - if (freeme == NULL) + if (!nfs_detach_delegation_locked(nfsi, old_delegation, clp)) goto out; + freeme = old_delegation; add_new: /* * If we didn't revalidate the change attribute before setting @@ -737,8 +738,8 @@ void nfs_inode_evict_delegation(struct inode *inode) rcu_read_lock(); delegation = rcu_dereference(nfsi->delegation); - if (delegation) - delegation = nfs_detach_delegation(nfsi, delegation, server); + if (delegation && !nfs_detach_delegation(nfsi, delegation, server)) + delegation = NULL; rcu_read_unlock(); if (!delegation) @@ -1245,7 +1246,7 @@ static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server, rcu_read_unlock(); if (delegation != NULL) { if (nfs_detach_delegation(NFS_I(inode), delegation, - server) != NULL) { + server)) { nfs_mark_delegation_revoked(server, delegation); nfs_put_delegation(delegation); } From 9f6ddc90d5a2162ecfecbdb5f5ed5bd9f71cc65f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:06 +0100 Subject: [PATCH 15/60] NFS: move the deleg_cur check out of nfs_detach_delegation_locked nfs_inode_set_delegation as the only direct caller of nfs_detach_delegation_locked already check this under cl_lock, so don't repeat it. Replace the lockdep coverage for the lock that was implicitly provided by the rcu_dereference_protected call that is removed with an explicit lockdep assert to keep the coverage. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 762d79ee58e3..a64c30efa1a3 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -350,15 +350,10 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi, struct nfs_delegation *delegation, struct nfs_client *clp) { - struct nfs_delegation *deleg_cur = - rcu_dereference_protected(nfsi->delegation, - lockdep_is_held(&clp->cl_lock)); + lockdep_assert_held(&clp->cl_lock); trace_nfs4_detach_delegation(&nfsi->vfs_inode, delegation->type); - if (delegation != deleg_cur) - return false; - spin_lock(&delegation->lock); if (!delegation->inode) { spin_unlock(&delegation->lock); @@ -378,10 +373,14 @@ static bool nfs_detach_delegation(struct nfs_inode *nfsi, struct nfs_server *server) { struct nfs_client *clp = server->nfs_client; - bool ret; + struct nfs_delegation *deleg_cur; + bool ret = false; spin_lock(&clp->cl_lock); - ret = nfs_detach_delegation_locked(nfsi, delegation, clp); + deleg_cur = rcu_dereference_protected(nfsi->delegation, + lockdep_is_held(&clp->cl_lock)); + if (delegation == deleg_cur) + ret = nfs_detach_delegation_locked(nfsi, delegation, clp); spin_unlock(&clp->cl_lock); return ret; } From 23e6208755cae5afd350f6805c7ea70bfd2b336d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:07 +0100 Subject: [PATCH 16/60] NFS: simplify the detached delegation check in update_open_stateid When nfs_detach_delegation_locked detaches a delegation from an inode, it clears both nfsi->delegation and delegation->inode. Use the later in update_open_stateid to check for a detached inode, as that avoids an extra local variable, and removes the need for a RCU derefernence as we already hold the lock in the delegation. This prepares for removing the surrounding RCU critical section. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5adaca5e476c..935ec446e52e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1907,7 +1907,6 @@ int update_open_stateid(struct nfs4_state *state, { struct nfs_server *server = NFS_SERVER(state->inode); struct nfs_client *clp = server->nfs_client; - struct nfs_inode *nfsi = NFS_I(state->inode); struct nfs_delegation *deleg_cur; nfs4_stateid freeme = { }; int ret = 0; @@ -1926,7 +1925,7 @@ int update_open_stateid(struct nfs4_state *state, goto no_delegation; spin_lock(&deleg_cur->lock); - if (rcu_dereference(nfsi->delegation) != deleg_cur || + if (!deleg_cur->inode || test_bit(NFS_DELEGATION_RETURNING, &deleg_cur->flags) || (deleg_cur->type & fmode) != fmode) goto no_delegation_unlock; From 542b11c0728335a1e06f61dc71b48e9bbbe13169 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:08 +0100 Subject: [PATCH 17/60] NFS: take a delegation reference in nfs4_get_valid_delegation Currently most work on struct nfs_delegation happens directly under RCU protection. This is generally fine, despite that long RCU sections are not good for performance. But for operations later taking a reference to the delegation to perform blocking work, refcount_inc is used, which can be racy against dropping the last reference and thus lead to use after frees in extremely rare cases. Fix this by taking a reference in nfs4_get_valid_delegation using refcount_inc_not_zero so that the callers have a stabilized reference they can work with and can be moved outside the RCU critical section. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/callback_proc.c | 13 ++++++--- fs/nfs/delegation.c | 62 ++++++++++++++++++++++-------------------- fs/nfs/delegation.h | 1 + fs/nfs/nfs4proc.c | 26 +++++++++--------- 4 files changed, 56 insertions(+), 46 deletions(-) diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 8397c43358bd..57550020c819 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -51,12 +51,18 @@ __be32 nfs4_callback_getattr(void *argp, void *resp, -ntohl(res->status)); goto out; } - rcu_read_lock(); + delegation = nfs4_get_valid_delegation(inode); - if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0) + if (!delegation) goto out_iput; - res->size = i_size_read(inode); + if ((delegation->type & FMODE_WRITE) == 0) { + nfs_put_delegation(delegation); + goto out_iput; + } res->change_attr = delegation->change_attr; + nfs_put_delegation(delegation); + + res->size = i_size_read(inode); if (nfs_have_writebacks(inode)) res->change_attr++; res->atime = inode_get_atime(inode); @@ -71,7 +77,6 @@ __be32 nfs4_callback_getattr(void *argp, void *resp, FATTR4_WORD2_TIME_DELEG_MODIFY) & args->bitmap[2]; res->status = 0; out_iput: - rcu_read_unlock(); trace_nfs4_cb_getattr(cps->clp, &args->fh, inode, -ntohl(res->status)); nfs_iput_and_deactive(inode); out: diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index a64c30efa1a3..0e91ee9aaa1b 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -66,7 +66,7 @@ static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegati return delegation; } -static void nfs_put_delegation(struct nfs_delegation *delegation) +void nfs_put_delegation(struct nfs_delegation *delegation) { if (refcount_dec_and_test(&delegation->refcount)) __nfs_free_delegation(delegation); @@ -104,10 +104,14 @@ struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode) { struct nfs_delegation *delegation; + rcu_read_lock(); delegation = rcu_dereference(NFS_I(inode)->delegation); - if (nfs4_is_valid_delegation(delegation, 0)) - return delegation; - return NULL; + if (!nfs4_is_valid_delegation(delegation, 0) || + !refcount_inc_not_zero(&delegation->refcount)) + delegation = NULL; + rcu_read_unlock(); + + return delegation; } static int nfs4_do_check_delegation(struct inode *inode, fmode_t type, @@ -794,10 +798,11 @@ void nfs4_inode_set_return_delegation_on_close(struct inode *inode) if (!inode) return; - rcu_read_lock(); + delegation = nfs4_get_valid_delegation(inode); if (!delegation) - goto out; + return; + spin_lock(&delegation->lock); if (!delegation->inode) goto out_unlock; @@ -811,8 +816,7 @@ void nfs4_inode_set_return_delegation_on_close(struct inode *inode) spin_unlock(&delegation->lock); if (ret) nfs_clear_verifier_delegated(inode); -out: - rcu_read_unlock(); + nfs_put_delegation(delegation); nfs_end_delegation_return(inode, ret, 0); } @@ -828,10 +832,10 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) struct nfs_delegation *delegation; struct nfs_delegation *ret = NULL; - rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); if (!delegation) - goto out; + return; + if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) || atomic_long_read(&NFS_SERVER(inode)->nr_active_delegations) >= nfs_delegation_watermark) { @@ -847,8 +851,8 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) if (ret) nfs_clear_verifier_delegated(inode); } -out: - rcu_read_unlock(); + + nfs_put_delegation(delegation); nfs_end_delegation_return(inode, ret, 0); } @@ -863,17 +867,17 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) int nfs4_inode_make_writeable(struct inode *inode) { struct nfs_delegation *delegation; + int error = 0; - rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); - if (delegation == NULL || - (nfs4_has_session(NFS_SERVER(inode)->nfs_client) && - (delegation->type & FMODE_WRITE))) { - rcu_read_unlock(); + if (!delegation) return 0; - } - rcu_read_unlock(); - return nfs4_inode_return_delegation(inode); + + if (!nfs4_has_session(NFS_SERVER(inode)->nfs_client) || + !(delegation->type & FMODE_WRITE)) + error = nfs4_inode_return_delegation(inode); + nfs_put_delegation(delegation); + return error; } static void @@ -1116,24 +1120,24 @@ int nfs_async_inode_return_delegation(struct inode *inode, struct nfs_client *clp = server->nfs_client; struct nfs_delegation *delegation; - rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); - if (delegation == NULL) - goto out_enoent; + if (!delegation) + return -ENOENT; + if (stateid != NULL && - !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) - goto out_enoent; + !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) { + nfs_put_delegation(delegation); + return -ENOENT; + } + nfs_mark_return_delegation(server, delegation); - rcu_read_unlock(); + nfs_put_delegation(delegation); /* If there are any application leases or delegations, recall them */ break_lease(inode, O_WRONLY | O_RDWR | O_NONBLOCK); nfs_delegation_run_state_manager(clp); return 0; -out_enoent: - rcu_read_unlock(); - return -ENOENT; } static struct inode * diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index fef1f4126e8f..d1c5da3e66ea 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -80,6 +80,7 @@ bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, nfs4_state bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode); struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode); +void nfs_put_delegation(struct nfs_delegation *delegation); void nfs_mark_delegation_referenced(struct nfs_delegation *delegation); int nfs4_have_delegation(struct inode *inode, fmode_t type, int flags); int nfs4_check_delegation(struct inode *inode, fmode_t type); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 935ec446e52e..9d43016d9aa1 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1615,10 +1615,11 @@ static bool can_open_delegated(const struct inode *inode, fmode_t fmode, struct nfs_delegation *delegation; bool ret = false; - rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); - if (!delegation || (delegation->type & fmode) != fmode) - goto out_unlock; + if (!delegation) + return false; + if ((delegation->type & fmode) != fmode) + goto out_put_delegation; switch (claim) { case NFS4_OPEN_CLAIM_PREVIOUS: @@ -1637,8 +1638,8 @@ static bool can_open_delegated(const struct inode *inode, fmode_t fmode, break; } -out_unlock: - rcu_read_unlock(); +out_put_delegation: + nfs_put_delegation(delegation); return ret; } @@ -1913,10 +1914,11 @@ int update_open_stateid(struct nfs4_state *state, fmode &= (FMODE_READ|FMODE_WRITE); - rcu_read_lock(); spin_lock(&state->owner->so_lock); if (open_stateid != NULL) { + rcu_read_lock(); nfs_state_set_open_stateid(state, open_stateid, fmode, &freeme); + rcu_read_unlock(); ret = 1; } @@ -1940,11 +1942,11 @@ int update_open_stateid(struct nfs4_state *state, ret = 1; no_delegation_unlock: spin_unlock(&deleg_cur->lock); + nfs_put_delegation(deleg_cur); no_delegation: if (ret) update_open_stateflags(state, fmode); spin_unlock(&state->owner->so_lock); - rcu_read_unlock(); if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) nfs4_schedule_state_manager(clp); @@ -1978,14 +1980,12 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo struct nfs_delegation *delegation; fmode &= FMODE_READ|FMODE_WRITE; - rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); - if (delegation == NULL || (delegation->type & fmode) == fmode) { - rcu_read_unlock(); + if (!delegation) return; - } - rcu_read_unlock(); - nfs4_inode_return_delegation(inode); + if ((delegation->type & fmode) != fmode) + nfs4_inode_return_delegation(inode); + nfs_put_delegation(delegation); } static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) From e96d9ca486b8310abc3736f45b45a91f0628b041 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:09 +0100 Subject: [PATCH 18/60] NFS: don't consume a delegation reference in nfs_end_delegation_return All callers now hold references to the delegation as part of the lookup, removing the need for an extra reference for those that are actually returned which is then dropped in nfs_end_delegation_return. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 46 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 0e91ee9aaa1b..684ec87dd4eb 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -325,7 +325,6 @@ nfs_start_delegation_return(struct nfs_inode *nfsi) if (delegation->inode && !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); - /* Refcount matched in nfs_end_delegation_return() */ ret = nfs_get_delegation(delegation); } spin_unlock(&delegation->lock); @@ -578,15 +577,11 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation if (err) { nfs_abort_delegation_return(delegation, server, err); - goto out; + return err; } out_return: - err = nfs_do_return_delegation(inode, delegation, issync); -out: - /* Refcount matched in nfs_start_delegation_return() */ - nfs_put_delegation(delegation); - return err; + return nfs_do_return_delegation(inode, delegation, issync); } static int nfs_server_return_marked_delegations(struct nfs_server *server, @@ -652,7 +647,11 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server, iput(to_put); - err = nfs_end_delegation_return(inode, delegation, 0); + if (delegation) { + err = nfs_end_delegation_return(inode, delegation, 0); + nfs_put_delegation(delegation); + } + iput(inode); cond_resched(); if (!err) @@ -768,6 +767,7 @@ int nfs4_inode_return_delegation(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs_delegation *delegation; + int err; rcu_read_lock(); delegation = nfs_start_delegation_return(nfsi); @@ -780,7 +780,9 @@ int nfs4_inode_return_delegation(struct inode *inode) break_lease(inode, O_WRONLY | O_RDWR); if (S_ISREG(inode->i_mode)) nfs_wb_all(inode); - return nfs_end_delegation_return(inode, delegation, 1); + err = nfs_end_delegation_return(inode, delegation, 1); + nfs_put_delegation(delegation); + return err; } /** @@ -794,7 +796,7 @@ int nfs4_inode_return_delegation(struct inode *inode) void nfs4_inode_set_return_delegation_on_close(struct inode *inode) { struct nfs_delegation *delegation; - struct nfs_delegation *ret = NULL; + bool return_now = false; if (!inode) return; @@ -807,17 +809,17 @@ void nfs4_inode_set_return_delegation_on_close(struct inode *inode) if (!delegation->inode) goto out_unlock; if (list_empty(&NFS_I(inode)->open_files) && - !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { - /* Refcount matched in nfs_end_delegation_return() */ - ret = nfs_get_delegation(delegation); - } else + !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) + return_now = true; + else set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); out_unlock: spin_unlock(&delegation->lock); - if (ret) + if (return_now) { nfs_clear_verifier_delegated(inode); + nfs_end_delegation_return(inode, delegation, 0); + } nfs_put_delegation(delegation); - nfs_end_delegation_return(inode, ret, 0); } /** @@ -830,7 +832,7 @@ void nfs4_inode_set_return_delegation_on_close(struct inode *inode) void nfs4_inode_return_delegation_on_close(struct inode *inode) { struct nfs_delegation *delegation; - struct nfs_delegation *ret = NULL; + bool return_now = false; delegation = nfs4_get_valid_delegation(inode); if (!delegation) @@ -844,16 +846,16 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) list_empty(&NFS_I(inode)->open_files) && !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); - /* Refcount matched in nfs_end_delegation_return() */ - ret = nfs_get_delegation(delegation); + return_now = true; } spin_unlock(&delegation->lock); - if (ret) - nfs_clear_verifier_delegated(inode); } + if (return_now) { + nfs_clear_verifier_delegated(inode); + nfs_end_delegation_return(inode, delegation, 0); + } nfs_put_delegation(delegation); - nfs_end_delegation_return(inode, ret, 0); } /** From 36e3e9387b367bd0cef3437d8181a6d6cb3c3b3f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:10 +0100 Subject: [PATCH 19/60] NFS: use refcount_inc_not_zero nfs_start_delegation_return Using the unconditional reference increment means we can take a reference to a delegation already in the RCU grace period, which could cause a use after free under very unlikely conditions. Switch to use refcount_inc_not_zero instead. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 684ec87dd4eb..a3996bd70236 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -60,12 +60,6 @@ static void nfs_mark_delegation_revoked(struct nfs_server *server, } } -static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation) -{ - refcount_inc(&delegation->refcount); - return delegation; -} - void nfs_put_delegation(struct nfs_delegation *delegation) { if (refcount_dec_and_test(&delegation->refcount)) @@ -312,25 +306,29 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation static struct nfs_delegation * nfs_start_delegation_return(struct nfs_inode *nfsi) { - struct nfs_delegation *ret = NULL; struct nfs_delegation *delegation; + bool return_now = false; lockdep_assert_in_rcu_read_lock(); delegation = rcu_dereference(nfsi->delegation); - if (!delegation) + if (!delegation || !refcount_inc_not_zero(&delegation->refcount)) return NULL; spin_lock(&delegation->lock); if (delegation->inode && !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); - ret = nfs_get_delegation(delegation); + return_now = true; } spin_unlock(&delegation->lock); - if (ret) - nfs_clear_verifier_delegated(&nfsi->vfs_inode); - return ret; + + if (!return_now) { + nfs_put_delegation(delegation); + return NULL; + } + nfs_clear_verifier_delegated(&nfsi->vfs_inode); + return delegation; } static void nfs_abort_delegation_return(struct nfs_delegation *delegation, From 3365322401450971111ced702591ef78db950c28 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:11 +0100 Subject: [PATCH 20/60] NFS: use a local RCU critical section in nfs_start_delegation_return Nested RCU critical sections are fine and very cheap. Have a local one in nfs_start_delegation_return so that the function is self-contained and to prepare for simplifying the callers. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index a3996bd70236..09e0d3732fda 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -309,11 +309,13 @@ nfs_start_delegation_return(struct nfs_inode *nfsi) struct nfs_delegation *delegation; bool return_now = false; - lockdep_assert_in_rcu_read_lock(); - + rcu_read_lock(); delegation = rcu_dereference(nfsi->delegation); - if (!delegation || !refcount_inc_not_zero(&delegation->refcount)) + if (!delegation || !refcount_inc_not_zero(&delegation->refcount)) { + rcu_read_unlock(); return NULL; + } + rcu_read_unlock(); spin_lock(&delegation->lock); if (delegation->inode && @@ -767,10 +769,7 @@ int nfs4_inode_return_delegation(struct inode *inode) struct nfs_delegation *delegation; int err; - rcu_read_lock(); delegation = nfs_start_delegation_return(nfsi); - rcu_read_unlock(); - if (!delegation) return 0; From 85e056332840ec4a938cf7a082d8922533b3b169 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:12 +0100 Subject: [PATCH 21/60] NFS: reformat nfs_mark_delegation_revoked Remove a level of indentation for the main code path. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 09e0d3732fda..334bec63b72d 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -52,12 +52,13 @@ static void __nfs_free_delegation(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(&server->nr_active_delegations); - if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) - nfs_clear_verifier_delegated(delegation->inode); - } + if (test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) + return; + + delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; + atomic_long_dec(&server->nr_active_delegations); + if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) + nfs_clear_verifier_delegated(delegation->inode); } void nfs_put_delegation(struct nfs_delegation *delegation) From 0ebe655bd033fd84e312980c9eba199604631e7e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:13 +0100 Subject: [PATCH 22/60] NFS: add a separate delegation return list Searching for returnable delegations in the per-server delegations list can be very expensive. While commit e04bbf6b1bbe ("NFS: Avoid quadratic search when freeing delegations.") reduced the overhead a bit, the fact that all the non-returnable delegations have to be searched limits the amount of optimizations that can be done. Fix this by introducing a separate list that only contains delegations scheduled for return. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/client.c | 2 + fs/nfs/delegation.c | 174 +++++++++++++++++++------------------- fs/nfs/delegation.h | 2 +- fs/nfs/nfs4trace.h | 1 - include/linux/nfs_fs_sb.h | 7 +- 5 files changed, 92 insertions(+), 94 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 2aaea9c98c2c..65b3de91b441 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1060,6 +1060,8 @@ struct nfs_server *nfs_alloc_server(void) INIT_LIST_HEAD(&server->client_link); INIT_LIST_HEAD(&server->master_link); INIT_LIST_HEAD(&server->delegations); + spin_lock_init(&server->delegations_lock); + INIT_LIST_HEAD(&server->delegations_return); INIT_LIST_HEAD(&server->layouts); INIT_LIST_HEAD(&server->state_owners_lru); INIT_LIST_HEAD(&server->ss_copies); diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 334bec63b72d..d2d2dd745466 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -52,6 +52,8 @@ static void __nfs_free_delegation(struct nfs_delegation *delegation) static void nfs_mark_delegation_revoked(struct nfs_server *server, struct nfs_delegation *delegation) { + bool put_ref = false; + if (test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) return; @@ -59,6 +61,16 @@ static void nfs_mark_delegation_revoked(struct nfs_server *server, atomic_long_dec(&server->nr_active_delegations); if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) nfs_clear_verifier_delegated(delegation->inode); + + spin_lock(&server->delegations_lock); + if (!list_empty(&delegation->entry)) { + list_del_init(&delegation->entry); + put_ref = true; + } + spin_unlock(&server->delegations_lock); + + if (put_ref) + nfs_put_delegation(delegation); } void nfs_put_delegation(struct nfs_delegation *delegation) @@ -80,8 +92,12 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) static void nfs_mark_return_delegation(struct nfs_server *server, struct nfs_delegation *delegation) { - set_bit(NFS_DELEGATION_RETURN, &delegation->flags); - set_bit(NFS4SERV_DELEGRETURN, &server->delegation_flags); + spin_lock(&server->delegations_lock); + if (list_empty(&delegation->entry)) + refcount_inc(&delegation->refcount); + list_move_tail(&delegation->entry, &server->delegations_return); + spin_unlock(&server->delegations_lock); + set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); } @@ -350,7 +366,7 @@ static void nfs_abort_delegation_return(struct nfs_delegation *delegation, } static bool -nfs_detach_delegation_locked(struct nfs_inode *nfsi, +nfs_detach_delegations_locked(struct nfs_inode *nfsi, struct nfs_delegation *delegation, struct nfs_client *clp) { @@ -384,7 +400,7 @@ static bool nfs_detach_delegation(struct nfs_inode *nfsi, deleg_cur = rcu_dereference_protected(nfsi->delegation, lockdep_is_held(&clp->cl_lock)); if (delegation == deleg_cur) - ret = nfs_detach_delegation_locked(nfsi, delegation, clp); + ret = nfs_detach_delegations_locked(nfsi, delegation, clp); spin_unlock(&clp->cl_lock); return ret; } @@ -454,6 +470,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, delegation->cred = get_cred(cred); delegation->inode = inode; delegation->flags = 1<entry); switch (deleg_type) { case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG: case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG: @@ -496,7 +513,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, &old_delegation->flags)) goto out; } - if (!nfs_detach_delegation_locked(nfsi, old_delegation, clp)) + if (!nfs_detach_delegations_locked(nfsi, old_delegation, clp)) goto out; freeme = old_delegation; add_new: @@ -585,85 +602,61 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation return nfs_do_return_delegation(inode, delegation, issync); } +static int nfs_return_one_delegation(struct nfs_server *server) +{ + struct nfs_delegation *delegation; + struct inode *inode; + int err = 0; + + spin_lock(&server->delegations_lock); + delegation = list_first_entry_or_null(&server->delegations_return, + struct nfs_delegation, entry); + if (!delegation) { + spin_unlock(&server->delegations_lock); + return 0; /* no more delegations */ + } + list_del_init(&delegation->entry); + spin_unlock(&server->delegations_lock); + + spin_lock(&delegation->lock); + inode = delegation->inode; + if (!inode || !igrab(inode)) { + spin_unlock(&delegation->lock); + goto out_put_delegation; + } + if (test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || + test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) || + test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { + spin_unlock(&delegation->lock); + goto out_put_inode; + } + clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); + spin_unlock(&delegation->lock); + + nfs_clear_verifier_delegated(inode); + + err = nfs_end_delegation_return(inode, delegation, 0); + if (err) { + nfs_mark_return_delegation(server, delegation); + goto out_put_inode; + } + +out_put_inode: + iput(inode); +out_put_delegation: + nfs_put_delegation(delegation); + if (err) + return err; + return 1; /* keep going */ +} + static int nfs_server_return_marked_delegations(struct nfs_server *server, void __always_unused *data) { - struct nfs_delegation *delegation; - struct nfs_delegation *prev; - struct inode *inode; - struct inode *place_holder = NULL; - struct nfs_delegation *place_holder_deleg = NULL; - int err = 0; + int err; - if (!test_and_clear_bit(NFS4SERV_DELEGRETURN, - &server->delegation_flags)) - return 0; -restart: - /* - * To avoid quadratic looping we hold a reference - * to an inode place_holder. Each time we restart, we - * list delegation in the server from the delegations - * of that inode. - * prev is an RCU-protected pointer to a delegation which - * wasn't marked for return and might be a good choice for - * the next place_holder. - */ - prev = NULL; - delegation = NULL; - rcu_read_lock(); - if (place_holder) - delegation = rcu_dereference(NFS_I(place_holder)->delegation); - if (!delegation || delegation != place_holder_deleg) - delegation = list_entry_rcu(server->delegations.next, - struct nfs_delegation, super_list); - list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { - struct inode *to_put = NULL; - - trace_nfs_delegation_need_return(delegation); - - if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags) || - test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || - test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || - test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { - if (nfs4_is_valid_delegation(delegation, 0)) - prev = delegation; - continue; - } - - inode = nfs_delegation_grab_inode(delegation); - if (inode == NULL) - continue; - - if (prev) { - struct inode *tmp = nfs_delegation_grab_inode(prev); - if (tmp) { - to_put = place_holder; - place_holder = tmp; - place_holder_deleg = prev; - } - } - - delegation = nfs_start_delegation_return(NFS_I(inode)); - rcu_read_unlock(); - - iput(to_put); - - if (delegation) { - err = nfs_end_delegation_return(inode, delegation, 0); - nfs_put_delegation(delegation); - } - - iput(inode); + while ((err = nfs_return_one_delegation(server)) > 0) cond_resched(); - if (!err) - goto restart; - set_bit(NFS4SERV_DELEGRETURN, &server->delegation_flags); - set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); - goto out; - } - rcu_read_unlock(); -out: - iput(place_holder); return err; } @@ -674,15 +667,15 @@ static bool nfs_server_clear_delayed_delegations(struct nfs_server *server) if (!test_and_clear_bit(NFS4SERV_DELEGRETURN_DELAYED, &server->delegation_flags)) - goto out; - list_for_each_entry_rcu (d, &server->delegations, super_list) { - if (!test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags)) - continue; - nfs_mark_return_delegation(server, d); - clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags); + return false; + + spin_lock(&server->delegations_lock); + list_for_each_entry_rcu(d, &server->delegations_return, entry) { + if (test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags)) + clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags); ret = true; } -out: + return ret; } @@ -692,14 +685,17 @@ static bool nfs_client_clear_delayed_delegations(struct nfs_client *clp) bool ret = false; if (!test_and_clear_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state)) - goto out; + return false; + rcu_read_lock(); list_for_each_entry_rcu (server, &clp->cl_superblocks, client_link) { if (nfs_server_clear_delayed_delegations(server)) ret = true; } rcu_read_unlock(); -out: + + if (ret) + set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); return ret; } @@ -886,7 +882,7 @@ nfs_mark_return_if_closed_delegation(struct nfs_server *server, { struct inode *inode; - if (test_bit(NFS_DELEGATION_RETURN, &delegation->flags) || + if (!list_empty_careful(&server->delegations_return) || test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) return; spin_lock(&delegation->lock); diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index d1c5da3e66ea..a6733f034442 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -26,12 +26,12 @@ struct nfs_delegation { unsigned long flags; refcount_t refcount; spinlock_t lock; + struct list_head entry; struct rcu_head rcu; }; enum { NFS_DELEGATION_NEED_RECLAIM = 0, - NFS_DELEGATION_RETURN, NFS_DELEGATION_RETURN_IF_CLOSED, NFS_DELEGATION_REFERENCED, NFS_DELEGATION_RETURNING, diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h index 18d02b4715bb..8ff6396bc206 100644 --- a/fs/nfs/nfs4trace.h +++ b/fs/nfs/nfs4trace.h @@ -990,7 +990,6 @@ DEFINE_NFS4_SET_DELEGATION_EVENT(nfs4_detach_delegation); #define show_delegation_flags(flags) \ __print_flags(flags, "|", \ { BIT(NFS_DELEGATION_NEED_RECLAIM), "NEED_RECLAIM" }, \ - { BIT(NFS_DELEGATION_RETURN), "RETURN" }, \ { BIT(NFS_DELEGATION_RETURN_IF_CLOSED), "RETURN_IF_CLOSED" }, \ { BIT(NFS_DELEGATION_REFERENCED), "REFERENCED" }, \ { BIT(NFS_DELEGATION_RETURNING), "RETURNING" }, \ diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index c58b870f31ee..e377b8c7086e 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -259,6 +259,8 @@ struct nfs_server { struct list_head state_owners_lru; struct list_head layouts; struct list_head delegations; + spinlock_t delegations_lock; + struct list_head delegations_return; atomic_long_t nr_active_delegations; unsigned int delegation_hash_mask; struct hlist_head *delegation_hash_table; @@ -266,9 +268,8 @@ struct nfs_server { struct list_head ss_src_copies; unsigned long delegation_flags; -#define NFS4SERV_DELEGRETURN (1) -#define NFS4SERV_DELEGATION_EXPIRED (2) -#define NFS4SERV_DELEGRETURN_DELAYED (3) +#define NFS4SERV_DELEGATION_EXPIRED (1) +#define NFS4SERV_DELEGRETURN_DELAYED (2) unsigned long delegation_gen; unsigned long mig_gen; unsigned long mig_status; From 300ca8123c901605eda5eba33c83dc6eb03d0a3c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:14 +0100 Subject: [PATCH 23/60] NFS: return delegations from the end of a LRU when over the watermark Directly returning delegations on close when over the watermark is rather suboptimal as these delegations are much more likely to be reused than those that have been unused for a long time. Switch to returning unused delegations from a new LRU list when we are above the threshold and there are reclaimable delegations instead. Pass over referenced delegations during the first pass to give delegations that aren't in active used by frequently used for stat() or similar another chance to not be instantly reclaimed. This scheme works the same as the referenced flags in the VFS inode and dentry caches. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/client.c | 1 + fs/nfs/delegation.c | 61 +++++++++++++++++++++++++++++++++++++-- include/linux/nfs_fs_sb.h | 1 + 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 65b3de91b441..62aece00f810 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1062,6 +1062,7 @@ struct nfs_server *nfs_alloc_server(void) INIT_LIST_HEAD(&server->delegations); spin_lock_init(&server->delegations_lock); INIT_LIST_HEAD(&server->delegations_return); + INIT_LIST_HEAD(&server->delegations_lru); INIT_LIST_HEAD(&server->layouts); INIT_LIST_HEAD(&server->state_owners_lru); INIT_LIST_HEAD(&server->ss_copies); diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index d2d2dd745466..848cb55073fc 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -660,6 +660,60 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server, return err; } +static inline bool nfs_delegations_over_limit(struct nfs_server *server) +{ + return !list_empty_careful(&server->delegations_lru) && + atomic_long_read(&server->nr_active_delegations) > + nfs_delegation_watermark; +} + +static void nfs_delegations_return_from_lru(struct nfs_server *server) +{ + struct nfs_delegation *d, *n; + unsigned int pass = 0; + bool moved = false; + +retry: + spin_lock(&server->delegations_lock); + list_for_each_entry_safe(d, n, &server->delegations_lru, entry) { + if (!nfs_delegations_over_limit(server)) + break; + if (pass == 0 && test_bit(NFS_DELEGATION_REFERENCED, &d->flags)) + continue; + list_move_tail(&d->entry, &server->delegations_return); + moved = true; + } + spin_unlock(&server->delegations_lock); + + /* + * If we are still over the limit, try to reclaim referenced delegations + * as well. + */ + if (pass == 0 && nfs_delegations_over_limit(server)) { + pass++; + goto retry; + } + + if (moved) { + set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); + nfs4_schedule_state_manager(server->nfs_client); + } +} + +static void nfs_delegation_add_lru(struct nfs_server *server, + struct nfs_delegation *delegation) +{ + spin_lock(&server->delegations_lock); + if (list_empty(&delegation->entry)) { + list_add_tail(&delegation->entry, &server->delegations_lru); + refcount_inc(&delegation->refcount); + } + spin_unlock(&server->delegations_lock); + + if (nfs_delegations_over_limit(server)) + nfs_delegations_return_from_lru(server); +} + static bool nfs_server_clear_delayed_delegations(struct nfs_server *server) { struct nfs_delegation *d; @@ -825,6 +879,7 @@ void nfs4_inode_set_return_delegation_on_close(struct inode *inode) */ void nfs4_inode_return_delegation_on_close(struct inode *inode) { + struct nfs_server *server = NFS_SERVER(inode); struct nfs_delegation *delegation; bool return_now = false; @@ -832,9 +887,7 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) if (!delegation) return; - if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) || - atomic_long_read(&NFS_SERVER(inode)->nr_active_delegations) >= - nfs_delegation_watermark) { + if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) { spin_lock(&delegation->lock); if (delegation->inode && list_empty(&NFS_I(inode)->open_files) && @@ -848,6 +901,8 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) if (return_now) { nfs_clear_verifier_delegated(inode); nfs_end_delegation_return(inode, delegation, 0); + } else { + nfs_delegation_add_lru(server, delegation); } nfs_put_delegation(delegation); } diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index e377b8c7086e..bb13a294b69e 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -261,6 +261,7 @@ struct nfs_server { struct list_head delegations; spinlock_t delegations_lock; struct list_head delegations_return; + struct list_head delegations_lru; atomic_long_t nr_active_delegations; unsigned int delegation_hash_mask; struct hlist_head *delegation_hash_table; From a13bc3286cb380aeca0d68dcb80d7611520e0b9e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jan 2026 08:27:15 +0100 Subject: [PATCH 24/60] NFS: make nfs_mark_return_unreferenced_delegations less aggressive Currently nfs_mark_return_unreferenced_delegations marks all open but not referenced delegations (i.e., those were found by a previous pass) as return on close, which means that we'll return them on close without a way out. Replace this with only iterating delegations that are on the LRU list, and avoid delegations that are in use by an open files to avoid this. Delegations that were never referenced while open still are be prime candidates for return from the LRU if the number of delegations is over the watermark, or otherwise will be returned by the next nfs_mark_return_unreferenced_delegations pass after they are closed. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 848cb55073fc..4d5f1f3162b0 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -1129,15 +1129,21 @@ void nfs_remove_bad_delegation(struct inode *inode, } EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); -static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) +static bool nfs_mark_return_unreferenced_delegations(struct nfs_server *server) { - struct nfs_delegation *delegation; + struct nfs_delegation *d, *n; + bool marked = false; - list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) + spin_lock(&server->delegations_lock); + list_for_each_entry_safe(d, n, &server->delegations_lru, entry) { + if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &d->flags)) continue; - nfs_mark_return_if_closed_delegation(server, delegation); + list_move_tail(&d->entry, &server->delegations_return); + marked = true; } + spin_unlock(&server->delegations_lock); + + return marked; } /** @@ -1148,13 +1154,17 @@ static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) void nfs_expire_unreferenced_delegations(struct nfs_client *clp) { struct nfs_server *server; + bool marked = false; rcu_read_lock(); list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs_mark_return_unreferenced_delegations(server); + marked |= nfs_mark_return_unreferenced_delegations(server); rcu_read_unlock(); - nfs_delegation_run_state_manager(clp); + if (marked) { + set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); + nfs4_schedule_state_manager(clp); + } } /** From 615762059d284b863f9163b53679d95b3dcdd495 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 3 Jan 2026 12:14:59 -0500 Subject: [PATCH 25/60] NFS/localio: Handle short writes by retrying The current code for handling short writes in localio just truncates the I/O and then sets an error. While that is close to how the ordinary NFS code behaves, it does mean there is a chance the data that got written is lost because it isn't persisted. To fix this, change localio so that the upper layers can direct the behaviour to persist any unstable data by rewriting it, and then continuing writing until an ENOSPC is hit. Fixes: 70ba381e1a43 ("nfs: add LOCALIO support") Signed-off-by: Trond Myklebust Reviewed-by: Mike Snitzer Signed-off-by: Anna Schumaker --- fs/nfs/localio.c | 64 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index 41fbcb3f9167..00bbac6c9fe4 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -58,6 +58,11 @@ struct nfs_local_fsync_ctx { static bool localio_enabled __read_mostly = true; module_param(localio_enabled, bool, 0644); +static int nfs_local_do_read(struct nfs_local_kiocb *iocb, + const struct rpc_call_ops *call_ops); +static int nfs_local_do_write(struct nfs_local_kiocb *iocb, + const struct rpc_call_ops *call_ops); + static inline bool nfs_client_is_local(const struct nfs_client *clp) { return !!rcu_access_pointer(clp->cl_uuid.net); @@ -542,13 +547,50 @@ nfs_local_iocb_release(struct nfs_local_kiocb *iocb) nfs_local_iocb_free(iocb); } -static void -nfs_local_pgio_release(struct nfs_local_kiocb *iocb) +static void nfs_local_pgio_restart(struct nfs_local_kiocb *iocb, + struct nfs_pgio_header *hdr) +{ + int status = 0; + + iocb->kiocb.ki_pos = hdr->args.offset; + iocb->kiocb.ki_flags &= ~(IOCB_DSYNC | IOCB_SYNC | IOCB_DIRECT); + iocb->kiocb.ki_complete = NULL; + iocb->aio_complete_work = NULL; + iocb->end_iter_index = -1; + + switch (hdr->rw_mode) { + case FMODE_READ: + nfs_local_iters_init(iocb, ITER_DEST); + status = nfs_local_do_read(iocb, hdr->task.tk_ops); + break; + case FMODE_WRITE: + nfs_local_iters_init(iocb, ITER_SOURCE); + status = nfs_local_do_write(iocb, hdr->task.tk_ops); + break; + default: + status = -EOPNOTSUPP; + } + + if (status != 0) { + nfs_local_iocb_release(iocb); + hdr->task.tk_status = status; + nfs_local_hdr_release(hdr, hdr->task.tk_ops); + } +} + +static void nfs_local_pgio_release(struct nfs_local_kiocb *iocb) { struct nfs_pgio_header *hdr = iocb->hdr; + struct rpc_task *task = &hdr->task; - nfs_local_iocb_release(iocb); - nfs_local_hdr_release(hdr, hdr->task.tk_ops); + task->tk_action = NULL; + task->tk_ops->rpc_call_done(task, hdr); + + if (task->tk_action == NULL) { + nfs_local_iocb_release(iocb); + task->tk_ops->rpc_release(hdr); + } else + nfs_local_pgio_restart(iocb, hdr); } /* @@ -773,19 +815,7 @@ static void nfs_local_write_done(struct nfs_local_kiocb *iocb) pr_info_ratelimited("nfs: Unexpected direct I/O write alignment failure\n"); } - /* Handle short writes as if they are ENOSPC */ - status = hdr->res.count; - if (status > 0 && status < hdr->args.count) { - hdr->mds_offset += status; - hdr->args.offset += status; - hdr->args.pgbase += status; - hdr->args.count -= status; - nfs_set_pgio_error(hdr, -ENOSPC, hdr->args.offset); - status = -ENOSPC; - /* record -ENOSPC in terms of nfs_local_pgio_done */ - (void) nfs_local_pgio_done(iocb, status, true); - } - if (hdr->task.tk_status < 0) + if (status < 0) nfs_reset_boot_verifier(hdr->inode); } From 5fcd95831d9751435e3617e870d706cb9070b55a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 3 Jan 2026 12:15:00 -0500 Subject: [PATCH 26/60] NFS/localio: Cleanup the nfs_local_pgio_done() parameters Remove the redundant 'force' parameter. Signed-off-by: Trond Myklebust Signed-off-by: Anna Schumaker --- fs/nfs/localio.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index 00bbac6c9fe4..7f5e7f0e3e00 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -517,8 +517,7 @@ nfs_local_pgio_init(struct nfs_pgio_header *hdr, hdr->task.tk_start = ktime_get(); } -static bool -nfs_local_pgio_done(struct nfs_local_kiocb *iocb, long status, bool force) +static bool nfs_local_pgio_done(struct nfs_local_kiocb *iocb, long status) { struct nfs_pgio_header *hdr = iocb->hdr; @@ -533,9 +532,6 @@ nfs_local_pgio_done(struct nfs_local_kiocb *iocb, long status, bool force) hdr->task.tk_status = status; } - if (force) - return true; - BUG_ON(atomic_read(&iocb->n_iters) <= 0); return atomic_dec_and_test(&iocb->n_iters); } @@ -651,7 +647,7 @@ static void nfs_local_read_aio_complete(struct kiocb *kiocb, long ret) container_of(kiocb, struct nfs_local_kiocb, kiocb); /* AIO completion of DIO read should always be last to complete */ - if (unlikely(!nfs_local_pgio_done(iocb, ret, false))) + if (unlikely(!nfs_local_pgio_done(iocb, ret))) return; nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_read_aio_complete_work */ @@ -683,7 +679,7 @@ static void nfs_local_call_read(struct work_struct *work) if (status == -EIOCBQUEUED) continue; /* Break on completion, errors, or short reads */ - if (nfs_local_pgio_done(iocb, status, false) || status < 0 || + if (nfs_local_pgio_done(iocb, status) || status < 0 || (size_t)status < iov_iter_count(&iocb->iters[i])) { nfs_local_read_iocb_done(iocb); break; @@ -840,7 +836,7 @@ static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret) container_of(kiocb, struct nfs_local_kiocb, kiocb); /* AIO completion of DIO write should always be last to complete */ - if (unlikely(!nfs_local_pgio_done(iocb, ret, false))) + if (unlikely(!nfs_local_pgio_done(iocb, ret))) return; nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_write_aio_complete_work */ @@ -876,7 +872,7 @@ static void nfs_local_call_write(struct work_struct *work) if (status == -EIOCBQUEUED) continue; /* Break on completion, errors, or short writes */ - if (nfs_local_pgio_done(iocb, status, false) || status < 0 || + if (nfs_local_pgio_done(iocb, status) || status < 0 || (size_t)status < iov_iter_count(&iocb->iters[i])) { nfs_local_write_iocb_done(iocb); break; From 67435d2d8a33a75f9647724952cb1b18279d2e95 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 7 Jan 2026 11:08:55 -0500 Subject: [PATCH 27/60] NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages LOCALIO is an NFS loopback mount optimization that avoids using the network for READ, WRITE and COMMIT if the NFS client and server are determined to be on the same system. But because LOCALIO is still fundamentally "just NFS loopback mount" it is susceptible to recursion deadlock via direct reclaim, e.g.: NFS LOCALIO down to XFS and then back into NFS via nfs_writepages. Fix LOCALIO's potential for direct reclaim deadlock by ensuring that all its page cache allocations are done from GFP_NOFS context. Thanks to Ben Coddington for pointing out commit ad22c7a043c2 ("xfs: prevent stack overflows from page cache allocation"). Reported-by: John Cagle Tested-by: Allen Lu Suggested-by: Benjamin Coddington Fixes: 70ba381e1a43 ("nfs: add LOCALIO support") Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker --- fs/nfs/localio.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index 7f5e7f0e3e00..40e20b324b3f 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -291,6 +291,18 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred, } EXPORT_SYMBOL_GPL(nfs_local_open_fh); +/* + * Ensure all page cache allocations are done from GFP_NOFS context to + * prevent direct reclaim recursion back into NFS via nfs_writepages. + */ +static void +nfs_local_mapping_set_gfp_nofs_context(struct address_space *m) +{ + gfp_t gfp_mask = mapping_gfp_mask(m); + + mapping_set_gfp_mask(m, (gfp_mask & ~(__GFP_FS))); +} + static void nfs_local_iocb_free(struct nfs_local_kiocb *iocb) { @@ -315,6 +327,7 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr, return NULL; } + nfs_local_mapping_set_gfp_nofs_context(file->f_mapping); init_sync_kiocb(&iocb->kiocb, file); iocb->hdr = hdr; @@ -1000,6 +1013,8 @@ nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data) end = LLONG_MAX; } + nfs_local_mapping_set_gfp_nofs_context(filp->f_mapping); + dprintk("%s: commit %llu - %llu\n", __func__, start, end); return vfs_fsync_range(filp, start, end, 0); } From 9bb0060f7860aa4561c5b21163dd45ceb66946a9 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 7 Jan 2026 11:08:56 -0500 Subject: [PATCH 28/60] NFS/localio: use GFP_NOIO and non-memreclaim workqueue in nfs_local_commit nfslocaliod_workqueue is a non-memreclaim workqueue (it isn't initialized with WQ_MEM_RECLAIM), see commit b9f5dd57f4a5 ("nfs/localio: use dedicated workqueues for filesystem read and write"). Use nfslocaliod_workqueue for LOCALIO's SYNC work. Also, set PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO in nfs_local_fsync_work. Fixes: b9f5dd57f4a5 ("nfs/localio: use dedicated workqueues for filesystem read and write") Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker --- fs/nfs/localio.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index 40e20b324b3f..03ea1ef30117 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -1056,17 +1056,22 @@ nfs_local_fsync_ctx_free(struct nfs_local_fsync_ctx *ctx) static void nfs_local_fsync_work(struct work_struct *work) { + unsigned long old_flags = current->flags; struct nfs_local_fsync_ctx *ctx; int status; ctx = container_of(work, struct nfs_local_fsync_ctx, work); + current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO; + status = nfs_local_run_commit(nfs_to->nfsd_file_file(ctx->localio), ctx->data); nfs_local_commit_done(ctx->data, status); if (ctx->done != NULL) complete(ctx->done); nfs_local_fsync_ctx_free(ctx); + + current->flags = old_flags; } static struct nfs_local_fsync_ctx * @@ -1090,7 +1095,7 @@ int nfs_local_commit(struct nfsd_file *localio, { struct nfs_local_fsync_ctx *ctx; - ctx = nfs_local_fsync_ctx_alloc(data, localio, GFP_KERNEL); + ctx = nfs_local_fsync_ctx_alloc(data, localio, GFP_NOIO); if (!ctx) { nfs_local_commit_done(data, -ENOMEM); nfs_local_release_commit_data(localio, data, call_ops); @@ -1102,10 +1107,10 @@ int nfs_local_commit(struct nfsd_file *localio, if (how & FLUSH_SYNC) { DECLARE_COMPLETION_ONSTACK(done); ctx->done = &done; - queue_work(nfsiod_workqueue, &ctx->work); + queue_work(nfslocaliod_workqueue, &ctx->work); wait_for_completion(&done); } else - queue_work(nfsiod_workqueue, &ctx->work); + queue_work(nfslocaliod_workqueue, &ctx->work); return 0; } From e72a73957613653f50375db1f3a3fbb907a9c40b Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 7 Jan 2026 11:08:57 -0500 Subject: [PATCH 29/60] NFS/localio: remove -EAGAIN handling in nfs_local_doio() Handling -EAGAIN in nfs_local_doio() was introduced with commit 0978e5b85fc08 (nfs_do_local_{read,write} were made to have negative checks for correspoding iter method) but commit e43e9a3a3d66 since eliminated the possibility for this -EAGAIN early return. So remove nfs_local_doio()'s -EAGAIN handling that calls nfs_localio_disable_client() -- while it should never happen from nfs_do_local_{read,write} this particular -EAGAIN handling is now "dead" and so it has become a liability. Fixes: e43e9a3a3d66 ("nfs/localio: refactor iocb initialization") Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker --- fs/nfs/localio.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index 03ea1ef30117..fc2cc9fb3752 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -985,8 +985,6 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio, } if (status != 0) { - if (status == -EAGAIN) - nfs_localio_disable_client(clp); nfs_local_iocb_release(iocb); hdr->task.tk_status = status; nfs_local_hdr_release(hdr, call_ops); From 1309c52de15b6a4204e569ea1b181c4e9dc25927 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 7 Jan 2026 11:08:58 -0500 Subject: [PATCH 30/60] NFS/localio: switch nfs_local_do_read and nfs_local_do_write to return void Both nfs_local_do_read and nfs_local_do_write only return 0 at the end, so switch them to returning void. Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker --- fs/nfs/localio.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index fc2cc9fb3752..3b47be4e693a 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -58,10 +58,10 @@ struct nfs_local_fsync_ctx { static bool localio_enabled __read_mostly = true; module_param(localio_enabled, bool, 0644); -static int nfs_local_do_read(struct nfs_local_kiocb *iocb, - const struct rpc_call_ops *call_ops); -static int nfs_local_do_write(struct nfs_local_kiocb *iocb, +static void nfs_local_do_read(struct nfs_local_kiocb *iocb, const struct rpc_call_ops *call_ops); +static void nfs_local_do_write(struct nfs_local_kiocb *iocb, + const struct rpc_call_ops *call_ops); static inline bool nfs_client_is_local(const struct nfs_client *clp) { @@ -570,17 +570,17 @@ static void nfs_local_pgio_restart(struct nfs_local_kiocb *iocb, switch (hdr->rw_mode) { case FMODE_READ: nfs_local_iters_init(iocb, ITER_DEST); - status = nfs_local_do_read(iocb, hdr->task.tk_ops); + nfs_local_do_read(iocb, hdr->task.tk_ops); break; case FMODE_WRITE: nfs_local_iters_init(iocb, ITER_SOURCE); - status = nfs_local_do_write(iocb, hdr->task.tk_ops); + nfs_local_do_write(iocb, hdr->task.tk_ops); break; default: status = -EOPNOTSUPP; } - if (status != 0) { + if (unlikely(status != 0)) { nfs_local_iocb_release(iocb); hdr->task.tk_status = status; nfs_local_hdr_release(hdr, hdr->task.tk_ops); @@ -700,9 +700,8 @@ static void nfs_local_call_read(struct work_struct *work) } } -static int -nfs_local_do_read(struct nfs_local_kiocb *iocb, - const struct rpc_call_ops *call_ops) +static void nfs_local_do_read(struct nfs_local_kiocb *iocb, + const struct rpc_call_ops *call_ops) { struct nfs_pgio_header *hdr = iocb->hdr; @@ -714,8 +713,6 @@ nfs_local_do_read(struct nfs_local_kiocb *iocb, INIT_WORK(&iocb->work, nfs_local_call_read); queue_work(nfslocaliod_workqueue, &iocb->work); - - return 0; } static void @@ -896,9 +893,8 @@ static void nfs_local_call_write(struct work_struct *work) current->flags = old_flags; } -static int -nfs_local_do_write(struct nfs_local_kiocb *iocb, - const struct rpc_call_ops *call_ops) +static void nfs_local_do_write(struct nfs_local_kiocb *iocb, + const struct rpc_call_ops *call_ops) { struct nfs_pgio_header *hdr = iocb->hdr; @@ -922,8 +918,6 @@ nfs_local_do_write(struct nfs_local_kiocb *iocb, INIT_WORK(&iocb->work, nfs_local_call_write); queue_work(nfslocaliod_workqueue, &iocb->work); - - return 0; } static struct nfs_local_kiocb * @@ -973,10 +967,10 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio, switch (hdr->rw_mode) { case FMODE_READ: - status = nfs_local_do_read(iocb, call_ops); + nfs_local_do_read(iocb, call_ops); break; case FMODE_WRITE: - status = nfs_local_do_write(iocb, call_ops); + nfs_local_do_write(iocb, call_ops); break; default: dprintk("%s: invalid mode: %d\n", __func__, @@ -984,7 +978,7 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio, status = -EOPNOTSUPP; } - if (status != 0) { + if (unlikely(status != 0)) { nfs_local_iocb_release(iocb); hdr->task.tk_status = status; nfs_local_hdr_release(hdr, call_ops); From e5e45ea615a13edac86d6f84bbd5fc1c5d532a10 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 7 Nov 2025 16:30:51 -0500 Subject: [PATCH 31/60] NFS: Move nfs40_call_sync_ops into nfs40proc.c This is the first step in extracting NFS v4.0 into its own set of files that can be disabled through Kconfig. Signed-off-by: Anna Schumaker --- fs/nfs/Makefile | 2 +- fs/nfs/nfs40.h | 8 ++++++++ fs/nfs/nfs40proc.c | 24 ++++++++++++++++++++++++ fs/nfs/nfs4_fs.h | 6 ++++++ fs/nfs/nfs4proc.c | 25 +------------------------ 5 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 fs/nfs/nfs40.h create mode 100644 fs/nfs/nfs40proc.c diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 9fb2f2cac87e..937c775a04a3 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -27,7 +27,7 @@ CFLAGS_nfs4trace.o += -I$(src) nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ delegation.o nfs4idmap.o callback.o callback_xdr.o callback_proc.o \ nfs4namespace.o nfs4getroot.o nfs4client.o nfs4session.o \ - dns_resolve.o nfs4trace.o + dns_resolve.o nfs4trace.o nfs40proc.o nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o pnfs_nfs.o diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h new file mode 100644 index 000000000000..58a59109987a --- /dev/null +++ b/fs/nfs/nfs40.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_FS_NFS_NFS4_0_H +#define __LINUX_FS_NFS_NFS4_0_H + + +extern const struct rpc_call_ops nfs40_call_sync_ops; + +#endif /* __LINUX_FS_NFS_NFS4_0_H */ diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c new file mode 100644 index 000000000000..6d27dedad055 --- /dev/null +++ b/fs/nfs/nfs40proc.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include +#include "nfs4_fs.h" + +static void nfs40_call_sync_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs4_call_sync_data *data = calldata; + nfs4_setup_sequence(data->seq_server->nfs_client, + data->seq_args, data->seq_res, task); +} + +static void nfs40_call_sync_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_call_sync_data *data = calldata; + nfs4_sequence_done(task, data->seq_res); +} + +const struct rpc_call_ops nfs40_call_sync_ops = { + .rpc_call_prepare = nfs40_call_sync_prepare, + .rpc_call_done = nfs40_call_sync_done, +}; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index c34c89af9c7d..575343261c9a 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -286,6 +286,12 @@ int nfs4_replace_transport(struct nfs_server *server, size_t nfs_parse_server_name(char *string, size_t len, struct sockaddr_storage *ss, size_t salen, struct net *net, int port); /* nfs4proc.c */ +struct nfs4_call_sync_data { + const struct nfs_server *seq_server; + struct nfs4_sequence_args *seq_args; + struct nfs4_sequence_res *seq_res; +}; + extern int nfs4_handle_exception(struct nfs_server *, int, struct nfs4_exception *); extern int nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 9d43016d9aa1..fbbc4e37b4b1 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -67,6 +67,7 @@ #include "nfs4idmap.h" #include "nfs4session.h" #include "fscache.h" +#include "nfs40.h" #include "nfs42.h" #include "nfs4trace.h" @@ -769,12 +770,6 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp do_renew_lease(clp, timestamp); } -struct nfs4_call_sync_data { - const struct nfs_server *seq_server; - struct nfs4_sequence_args *seq_args; - struct nfs4_sequence_res *seq_res; -}; - void nfs4_init_sequence(struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply, int privileged) @@ -1174,24 +1169,6 @@ int nfs4_setup_sequence(struct nfs_client *client, } EXPORT_SYMBOL_GPL(nfs4_setup_sequence); -static void nfs40_call_sync_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs4_call_sync_data *data = calldata; - nfs4_setup_sequence(data->seq_server->nfs_client, - data->seq_args, data->seq_res, task); -} - -static void nfs40_call_sync_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_call_sync_data *data = calldata; - nfs4_sequence_done(task, data->seq_res); -} - -static const struct rpc_call_ops nfs40_call_sync_ops = { - .rpc_call_prepare = nfs40_call_sync_prepare, - .rpc_call_done = nfs40_call_sync_done, -}; - static int nfs4_call_sync_custom(struct rpc_task_setup *task_setup) { int ret; From c96c05fcfe395e93f738d924dfd40571ca4302ca Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 13 Nov 2025 11:10:17 -0500 Subject: [PATCH 32/60] NFS: Split out the nfs40_reboot_recovery_ops into nfs40client.c Signed-off-by: Anna Schumaker --- fs/nfs/Makefile | 2 +- fs/nfs/internal.h | 3 - fs/nfs/nfs40.h | 7 ++ fs/nfs/nfs40client.c | 186 +++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs40proc.c | 9 +++ fs/nfs/nfs4_fs.h | 7 ++ fs/nfs/nfs4client.c | 132 +----------------------------- fs/nfs/nfs4proc.c | 13 +-- fs/nfs/nfs4state.c | 49 ------------ 9 files changed, 214 insertions(+), 194 deletions(-) create mode 100644 fs/nfs/nfs40client.c diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 937c775a04a3..d05e69c00fe1 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -27,7 +27,7 @@ CFLAGS_nfs4trace.o += -I$(src) nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ delegation.o nfs4idmap.o callback.o callback_xdr.o callback_proc.o \ nfs4namespace.o nfs4getroot.o nfs4client.o nfs4session.o \ - dns_resolve.o nfs4trace.o nfs40proc.o + dns_resolve.o nfs4trace.o nfs40proc.o nfs40client.o nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o pnfs_nfs.o diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 2e596244799f..e99998e515c0 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -739,9 +739,6 @@ extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq, loff_t offset); /* nfs4proc.c */ extern struct nfs_client *nfs4_init_client(struct nfs_client *clp, const struct nfs_client_initdata *); -extern int nfs40_walk_client_list(struct nfs_client *clp, - struct nfs_client **result, - const struct cred *cred); extern int nfs41_walk_client_list(struct nfs_client *clp, struct nfs_client **result, const struct cred *cred); diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index 58a59109987a..ad57172b49a3 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -3,6 +3,13 @@ #define __LINUX_FS_NFS_NFS4_0_H +/* nfs40proc.c */ extern const struct rpc_call_ops nfs40_call_sync_ops; +extern const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops; + +/* nfs40state.c */ +int nfs40_discover_server_trunking(struct nfs_client *clp, + struct nfs_client **result, + const struct cred *cred); #endif /* __LINUX_FS_NFS_NFS4_0_H */ diff --git a/fs/nfs/nfs40client.c b/fs/nfs/nfs40client.c new file mode 100644 index 000000000000..fae4ff584b1b --- /dev/null +++ b/fs/nfs/nfs40client.c @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include "nfs4_fs.h" +#include "callback.h" +#include "internal.h" +#include "netns.h" +#include "nfs40.h" + +#define NFSDBG_FACILITY NFSDBG_CLIENT + +/* + * SETCLIENTID just did a callback update with the callback ident in + * "drop," but server trunking discovery claims "drop" and "keep" are + * actually the same server. Swap the callback IDs so that "keep" + * will continue to use the callback ident the server now knows about, + * and so that "keep"'s original callback ident is destroyed when + * "drop" is freed. + */ +static void nfs4_swap_callback_idents(struct nfs_client *keep, + struct nfs_client *drop) +{ + struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id); + unsigned int save = keep->cl_cb_ident; + + if (keep->cl_cb_ident == drop->cl_cb_ident) + return; + + dprintk("%s: keeping callback ident %u and dropping ident %u\n", + __func__, keep->cl_cb_ident, drop->cl_cb_ident); + + spin_lock(&nn->nfs_client_lock); + + idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident); + keep->cl_cb_ident = drop->cl_cb_ident; + + idr_replace(&nn->cb_ident_idr, drop, save); + drop->cl_cb_ident = save; + + spin_unlock(&nn->nfs_client_lock); +} + +static bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2) +{ + return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0; +} + +/** + * nfs40_walk_client_list - Find server that recognizes a client ID + * + * @new: nfs_client with client ID to test + * @result: OUT: found nfs_client, or new + * @cred: credential to use for trunking test + * + * Returns zero, a negative errno, or a negative NFS4ERR status. + * If zero is returned, an nfs_client pointer is planted in "result." + * + * NB: nfs40_walk_client_list() relies on the new nfs_client being + * the last nfs_client on the list. + */ +static int nfs40_walk_client_list(struct nfs_client *new, + struct nfs_client **result, + const struct cred *cred) +{ + struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); + struct nfs_client *pos, *prev = NULL; + struct nfs4_setclientid_res clid = { + .clientid = new->cl_clientid, + .confirm = new->cl_confirm, + }; + int status = -NFS4ERR_STALE_CLIENTID; + + spin_lock(&nn->nfs_client_lock); + list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { + + if (pos == new) + goto found; + + status = nfs4_match_client(pos, new, &prev, nn); + if (status < 0) + goto out_unlock; + if (status != 0) + continue; + /* + * We just sent a new SETCLIENTID, which should have + * caused the server to return a new cl_confirm. So if + * cl_confirm is the same, then this is a different + * server that just returned the same cl_confirm by + * coincidence: + */ + if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm, + &new->cl_confirm)) + continue; + /* + * But if the cl_confirm's are different, then the only + * way that a SETCLIENTID_CONFIRM to pos can succeed is + * if new and pos point to the same server: + */ +found: + refcount_inc(&pos->cl_count); + spin_unlock(&nn->nfs_client_lock); + + nfs_put_client(prev); + prev = pos; + + status = nfs4_proc_setclientid_confirm(pos, &clid, cred); + switch (status) { + case -NFS4ERR_STALE_CLIENTID: + break; + case 0: + nfs4_swap_callback_idents(pos, new); + pos->cl_confirm = new->cl_confirm; + nfs_mark_client_ready(pos, NFS_CS_READY); + + prev = NULL; + *result = pos; + goto out; + case -ERESTARTSYS: + case -ETIMEDOUT: + /* The callback path may have been inadvertently + * changed. Schedule recovery! + */ + nfs4_schedule_path_down_recovery(pos); + goto out; + default: + goto out; + } + + spin_lock(&nn->nfs_client_lock); + } +out_unlock: + spin_unlock(&nn->nfs_client_lock); + + /* No match found. The server lost our clientid */ +out: + nfs_put_client(prev); + return status; +} + +/** + * nfs40_discover_server_trunking - Detect server IP address trunking (mv0) + * + * @clp: nfs_client under test + * @result: OUT: found nfs_client, or clp + * @cred: credential to use for trunking test + * + * Returns zero, a negative errno, or a negative NFS4ERR status. + * If zero is returned, an nfs_client pointer is planted in + * "result". + * + * Note: The returned client may not yet be marked ready. + */ +int nfs40_discover_server_trunking(struct nfs_client *clp, + struct nfs_client **result, + const struct cred *cred) +{ + struct nfs4_setclientid_res clid = { + .clientid = clp->cl_clientid, + .confirm = clp->cl_confirm, + }; + struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); + unsigned short port; + int status; + + port = nn->nfs_callback_tcpport; + if (clp->cl_addr.ss_family == AF_INET6) + port = nn->nfs_callback_tcpport6; + + status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); + if (status != 0) + goto out; + clp->cl_clientid = clid.clientid; + clp->cl_confirm = clid.confirm; + + status = nfs40_walk_client_list(clp, result, cred); + if (status == 0) { + /* Sustain the lease, even if it's empty. If the clientid4 + * goes stale it's of no use for trunking discovery. */ + nfs4_schedule_state_renewal(*result); + + /* If the client state need to recover, do it. */ + if (clp->cl_state) + nfs4_schedule_state_manager(clp); + } +out: + return status; +} diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 6d27dedad055..b2cc7519b226 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -22,3 +22,12 @@ const struct rpc_call_ops nfs40_call_sync_ops = { .rpc_call_prepare = nfs40_call_sync_prepare, .rpc_call_done = nfs40_call_sync_done, }; + +const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { + .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, + .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, + .recover_open = nfs4_open_reclaim, + .recover_lock = nfs4_lock_reclaim, + .establish_clid = nfs4_init_clientid, + .detect_trunking = nfs40_discover_server_trunking, +}; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 575343261c9a..246a43d172f9 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -277,6 +277,11 @@ int nfs_atomic_open(struct inode *, struct dentry *, struct file *, /* fs_context.c */ extern struct file_system_type nfs4_fs_type; +/* nfs4client.c */ +struct nfs_net; +int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, + struct nfs_client **prev, struct nfs_net *nn); + /* nfs4namespace.c */ struct rpc_clnt *nfs4_negotiate_security(struct rpc_clnt *, struct inode *, const struct qstr *); @@ -345,6 +350,8 @@ extern void nfs4_update_changeattr(struct inode *dir, unsigned long cache_validity); extern int nfs4_buf_to_pages_noslab(const void *buf, size_t buflen, struct page **pages); +extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); +extern int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request); #if defined(CONFIG_NFS_V4_1) extern int nfs41_sequence_done(struct rpc_task *, struct nfs4_sequence_res *); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 96bccefbe2cb..517cf8af2943 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -490,37 +490,6 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp, return ERR_PTR(error); } -/* - * SETCLIENTID just did a callback update with the callback ident in - * "drop," but server trunking discovery claims "drop" and "keep" are - * actually the same server. Swap the callback IDs so that "keep" - * will continue to use the callback ident the server now knows about, - * and so that "keep"'s original callback ident is destroyed when - * "drop" is freed. - */ -static void nfs4_swap_callback_idents(struct nfs_client *keep, - struct nfs_client *drop) -{ - struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id); - unsigned int save = keep->cl_cb_ident; - - if (keep->cl_cb_ident == drop->cl_cb_ident) - return; - - dprintk("%s: keeping callback ident %u and dropping ident %u\n", - __func__, keep->cl_cb_ident, drop->cl_cb_ident); - - spin_lock(&nn->nfs_client_lock); - - idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident); - keep->cl_cb_ident = drop->cl_cb_ident; - - idr_replace(&nn->cb_ident_idr, drop, save); - drop->cl_cb_ident = save; - - spin_unlock(&nn->nfs_client_lock); -} - static bool nfs4_match_client_owner_id(const struct nfs_client *clp1, const struct nfs_client *clp2) { @@ -529,13 +498,8 @@ static bool nfs4_match_client_owner_id(const struct nfs_client *clp1, return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0; } -static bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2) -{ - return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0; -} - -static int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, - struct nfs_client **prev, struct nfs_net *nn) +int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, + struct nfs_client **prev, struct nfs_net *nn) { int status; @@ -578,98 +542,6 @@ static int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, return 0; } -/** - * nfs40_walk_client_list - Find server that recognizes a client ID - * - * @new: nfs_client with client ID to test - * @result: OUT: found nfs_client, or new - * @cred: credential to use for trunking test - * - * Returns zero, a negative errno, or a negative NFS4ERR status. - * If zero is returned, an nfs_client pointer is planted in "result." - * - * NB: nfs40_walk_client_list() relies on the new nfs_client being - * the last nfs_client on the list. - */ -int nfs40_walk_client_list(struct nfs_client *new, - struct nfs_client **result, - const struct cred *cred) -{ - struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); - struct nfs_client *pos, *prev = NULL; - struct nfs4_setclientid_res clid = { - .clientid = new->cl_clientid, - .confirm = new->cl_confirm, - }; - int status = -NFS4ERR_STALE_CLIENTID; - - spin_lock(&nn->nfs_client_lock); - list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { - - if (pos == new) - goto found; - - status = nfs4_match_client(pos, new, &prev, nn); - if (status < 0) - goto out_unlock; - if (status != 0) - continue; - /* - * We just sent a new SETCLIENTID, which should have - * caused the server to return a new cl_confirm. So if - * cl_confirm is the same, then this is a different - * server that just returned the same cl_confirm by - * coincidence: - */ - if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm, - &new->cl_confirm)) - continue; - /* - * But if the cl_confirm's are different, then the only - * way that a SETCLIENTID_CONFIRM to pos can succeed is - * if new and pos point to the same server: - */ -found: - refcount_inc(&pos->cl_count); - spin_unlock(&nn->nfs_client_lock); - - nfs_put_client(prev); - prev = pos; - - status = nfs4_proc_setclientid_confirm(pos, &clid, cred); - switch (status) { - case -NFS4ERR_STALE_CLIENTID: - break; - case 0: - nfs4_swap_callback_idents(pos, new); - pos->cl_confirm = new->cl_confirm; - nfs_mark_client_ready(pos, NFS_CS_READY); - - prev = NULL; - *result = pos; - goto out; - case -ERESTARTSYS: - case -ETIMEDOUT: - /* The callback path may have been inadvertently - * changed. Schedule recovery! - */ - nfs4_schedule_path_down_recovery(pos); - goto out; - default: - goto out; - } - - spin_lock(&nn->nfs_client_lock); - } -out_unlock: - spin_unlock(&nn->nfs_client_lock); - - /* No match found. The server lost our clientid */ -out: - nfs_put_client(prev); - return status; -} - #ifdef CONFIG_NFS_V4_1 /* * Returns true if the server major ids match diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index fbbc4e37b4b1..e3047bd989a5 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2331,7 +2331,7 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state return err; } -static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) { struct nfs_open_context *ctx; int ret; @@ -7649,7 +7649,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f return ret; } -static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) +int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) { struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_exception exception = { @@ -10804,15 +10804,6 @@ static bool nfs4_match_stateid(const nfs4_stateid *s1, } -static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { - .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, - .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, - .recover_open = nfs4_open_reclaim, - .recover_lock = nfs4_lock_reclaim, - .establish_clid = nfs4_init_clientid, - .detect_trunking = nfs40_discover_server_trunking, -}; - #if defined(CONFIG_NFS_V4_1) static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index dba51c622cf3..ead4d1ac4d0b 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -142,55 +142,6 @@ int nfs4_init_clientid(struct nfs_client *clp, const struct cred *cred) return status; } -/** - * nfs40_discover_server_trunking - Detect server IP address trunking (mv0) - * - * @clp: nfs_client under test - * @result: OUT: found nfs_client, or clp - * @cred: credential to use for trunking test - * - * Returns zero, a negative errno, or a negative NFS4ERR status. - * If zero is returned, an nfs_client pointer is planted in - * "result". - * - * Note: The returned client may not yet be marked ready. - */ -int nfs40_discover_server_trunking(struct nfs_client *clp, - struct nfs_client **result, - const struct cred *cred) -{ - struct nfs4_setclientid_res clid = { - .clientid = clp->cl_clientid, - .confirm = clp->cl_confirm, - }; - struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); - unsigned short port; - int status; - - port = nn->nfs_callback_tcpport; - if (clp->cl_addr.ss_family == AF_INET6) - port = nn->nfs_callback_tcpport6; - - status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); - if (status != 0) - goto out; - clp->cl_clientid = clid.clientid; - clp->cl_confirm = clid.confirm; - - status = nfs40_walk_client_list(clp, result, cred); - if (status == 0) { - /* Sustain the lease, even if it's empty. If the clientid4 - * goes stale it's of no use for trunking discovery. */ - nfs4_schedule_state_renewal(*result); - - /* If the client state need to recover, do it. */ - if (clp->cl_state) - nfs4_schedule_state_manager(clp); - } -out: - return status; -} - const struct cred *nfs4_get_machine_cred(struct nfs_client *clp) { return get_cred(rpc_machine_cred()); From 0e854d761e2bc316b810296c493d479fef92b2c6 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 13 Nov 2025 13:17:59 -0500 Subject: [PATCH 33/60] NFS: Split out the nfs40_nograce_recovery_ops into nfs40proc.c Signed-off-by: Anna Schumaker --- fs/nfs/nfs40.h | 1 + fs/nfs/nfs40proc.c | 22 ++++++++++++++++++++++ fs/nfs/nfs4_fs.h | 6 ++++++ fs/nfs/nfs4proc.c | 34 +++++----------------------------- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index ad57172b49a3..c64e5ff6c9ff 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -6,6 +6,7 @@ /* nfs40proc.c */ extern const struct rpc_call_ops nfs40_call_sync_ops; extern const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops; +extern const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops; /* nfs40state.c */ int nfs40_discover_server_trunking(struct nfs_client *clp, diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index b2cc7519b226..867afdf4ecf4 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -18,6 +18,20 @@ static void nfs40_call_sync_done(struct rpc_task *task, void *calldata) nfs4_sequence_done(task, data->seq_res); } +static void nfs40_clear_delegation_stateid(struct nfs4_state *state) +{ + if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL) + nfs_finish_clear_delegation_stateid(state, NULL); +} + +static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) +{ + /* NFSv4.0 doesn't allow for delegation recovery on open expire */ + nfs40_clear_delegation_stateid(state); + nfs_state_clear_open_state_flags(state); + return nfs4_open_expired(sp, state); +} + const struct rpc_call_ops nfs40_call_sync_ops = { .rpc_call_prepare = nfs40_call_sync_prepare, .rpc_call_done = nfs40_call_sync_done, @@ -31,3 +45,11 @@ const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { .establish_clid = nfs4_init_clientid, .detect_trunking = nfs40_discover_server_trunking, }; + +const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { + .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE, + .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, + .recover_open = nfs40_open_expired, + .recover_lock = nfs4_lock_expired, + .establish_clid = nfs4_init_clientid, +}; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 246a43d172f9..885a78a3c22c 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -351,7 +351,13 @@ extern void nfs4_update_changeattr(struct inode *dir, extern int nfs4_buf_to_pages_noslab(const void *buf, size_t buflen, struct page **pages); extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); +extern int nfs4_open_expired(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request); +extern int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request); +extern void nfs_state_clear_delegation(struct nfs4_state *state); +extern void nfs_finish_clear_delegation_stateid(struct nfs4_state *state, + const nfs4_stateid *stateid); +extern void nfs_state_clear_open_state_flags(struct nfs4_state *state); #if defined(CONFIG_NFS_V4_1) extern int nfs41_sequence_done(struct rpc_task *, struct nfs4_sequence_res *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e3047bd989a5..a4529b375f88 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1848,7 +1848,7 @@ static void nfs_state_set_open_stateid(struct nfs4_state *state, write_sequnlock(&state->seqlock); } -static void nfs_state_clear_open_state_flags(struct nfs4_state *state) +void nfs_state_clear_open_state_flags(struct nfs4_state *state) { clear_bit(NFS_O_RDWR_STATE, &state->flags); clear_bit(NFS_O_WRONLY_STATE, &state->flags); @@ -1870,7 +1870,7 @@ static void nfs_state_set_delegation(struct nfs4_state *state, write_sequnlock(&state->seqlock); } -static void nfs_state_clear_delegation(struct nfs4_state *state) +void nfs_state_clear_delegation(struct nfs4_state *state) { write_seqlock(&state->seqlock); nfs4_stateid_copy(&state->stateid, &state->open_stateid); @@ -2862,7 +2862,7 @@ static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state return err; } -static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) +int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) { struct nfs_open_context *ctx; int ret; @@ -2875,27 +2875,13 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta return ret; } -static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state, +void nfs_finish_clear_delegation_stateid(struct nfs4_state *state, const nfs4_stateid *stateid) { nfs_remove_bad_delegation(state->inode, stateid); nfs_state_clear_delegation(state); } -static void nfs40_clear_delegation_stateid(struct nfs4_state *state) -{ - if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL) - nfs_finish_clear_delegation_stateid(state, NULL); -} - -static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) -{ - /* NFSv4.0 doesn't allow for delegation recovery on open expire */ - nfs40_clear_delegation_stateid(state); - nfs_state_clear_open_state_flags(state); - return nfs4_open_expired(sp, state); -} - static int nfs40_test_and_free_expired_stateid(struct nfs_server *server, nfs4_stateid *stateid, const struct cred *cred) { @@ -7669,7 +7655,7 @@ int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) return err; } -static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request) +int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request) { struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_exception exception = { @@ -10814,17 +10800,7 @@ static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = { .reclaim_complete = nfs41_proc_reclaim_complete, .detect_trunking = nfs41_discover_server_trunking, }; -#endif /* CONFIG_NFS_V4_1 */ -static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { - .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE, - .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, - .recover_open = nfs40_open_expired, - .recover_lock = nfs4_lock_expired, - .establish_clid = nfs4_init_clientid, -}; - -#if defined(CONFIG_NFS_V4_1) static const struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE, .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, From 963707b122ac22b03e9c0e54137045c139271ff1 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 13 Nov 2025 15:15:29 -0500 Subject: [PATCH 34/60] NFS: Split out the nfs40_state_renewal_ops into nfs40proc.c Signed-off-by: Anna Schumaker --- fs/nfs/nfs40.h | 1 + fs/nfs/nfs40proc.c | 100 ++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 102 +-------------------------------------------- 4 files changed, 103 insertions(+), 101 deletions(-) diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index c64e5ff6c9ff..fd606b4a044a 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -7,6 +7,7 @@ extern const struct rpc_call_ops nfs40_call_sync_ops; extern const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops; extern const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops; +extern const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops; /* nfs40state.c */ int nfs40_discover_server_trunking(struct nfs_client *clp, diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 867afdf4ecf4..96ce463c951b 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -3,7 +3,9 @@ #include #include #include +#include "internal.h" #include "nfs4_fs.h" +#include "nfs4trace.h" static void nfs40_call_sync_prepare(struct rpc_task *task, void *calldata) { @@ -32,6 +34,98 @@ static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st return nfs4_open_expired(sp, state); } +struct nfs4_renewdata { + struct nfs_client *client; + unsigned long timestamp; +}; + +/* + * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special + * standalone procedure for queueing an asynchronous RENEW. + */ +static void nfs4_renew_release(void *calldata) +{ + struct nfs4_renewdata *data = calldata; + struct nfs_client *clp = data->client; + + if (refcount_read(&clp->cl_count) > 1) + nfs4_schedule_state_renewal(clp); + nfs_put_client(clp); + kfree(data); +} + +static void nfs4_renew_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_renewdata *data = calldata; + struct nfs_client *clp = data->client; + unsigned long timestamp = data->timestamp; + + trace_nfs4_renew_async(clp, task->tk_status); + switch (task->tk_status) { + case 0: + break; + case -NFS4ERR_LEASE_MOVED: + nfs4_schedule_lease_moved_recovery(clp); + break; + default: + /* Unless we're shutting down, schedule state recovery! */ + if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0) + return; + if (task->tk_status != NFS4ERR_CB_PATH_DOWN) { + nfs4_schedule_lease_recovery(clp); + return; + } + nfs4_schedule_path_down_recovery(clp); + } + do_renew_lease(clp, timestamp); +} + +static const struct rpc_call_ops nfs4_renew_ops = { + .rpc_call_done = nfs4_renew_done, + .rpc_release = nfs4_renew_release, +}; + +static int nfs4_proc_async_renew(struct nfs_client *clp, const struct cred *cred, unsigned renew_flags) +{ + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], + .rpc_argp = clp, + .rpc_cred = cred, + }; + struct nfs4_renewdata *data; + + if (renew_flags == 0) + return 0; + if (!refcount_inc_not_zero(&clp->cl_count)) + return -EIO; + data = kmalloc(sizeof(*data), GFP_NOFS); + if (data == NULL) { + nfs_put_client(clp); + return -ENOMEM; + } + data->client = clp; + data->timestamp = jiffies; + return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT, + &nfs4_renew_ops, data); +} + +static int nfs4_proc_renew(struct nfs_client *clp, const struct cred *cred) +{ + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], + .rpc_argp = clp, + .rpc_cred = cred, + }; + unsigned long now = jiffies; + int status; + + status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); + if (status < 0) + return status; + do_renew_lease(clp, now); + return 0; +} + const struct rpc_call_ops nfs40_call_sync_ops = { .rpc_call_prepare = nfs40_call_sync_prepare, .rpc_call_done = nfs40_call_sync_done, @@ -53,3 +147,9 @@ const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { .recover_lock = nfs4_lock_expired, .establish_clid = nfs4_init_clientid, }; + +const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { + .sched_state_renewal = nfs4_proc_async_renew, + .get_state_renewal_cred = nfs4_get_renew_cred, + .renew_lease = nfs4_proc_renew, +}; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 885a78a3c22c..b43fb84145e0 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -358,6 +358,7 @@ extern void nfs_state_clear_delegation(struct nfs4_state *state); extern void nfs_finish_clear_delegation_stateid(struct nfs4_state *state, const nfs4_stateid *stateid); extern void nfs_state_clear_open_state_flags(struct nfs4_state *state); +extern void do_renew_lease(struct nfs_client *clp, unsigned long timestamp); #if defined(CONFIG_NFS_V4_1) extern int nfs41_sequence_done(struct rpc_task *, struct nfs4_sequence_res *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a4529b375f88..dc513bcae3c6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -754,7 +754,7 @@ static bool _nfs4_is_integrity_protected(struct nfs_client *clp) return (flavor == RPC_AUTH_GSS_KRB5I) || (flavor == RPC_AUTH_GSS_KRB5P); } -static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp) +void do_renew_lease(struct nfs_client *clp, unsigned long timestamp) { spin_lock(&clp->cl_lock); if (time_before(clp->cl_last_renewal,timestamp)) @@ -5971,98 +5971,6 @@ int nfs4_proc_commit(struct file *dst, __u64 offset, __u32 count, struct nfs_com return status; } -struct nfs4_renewdata { - struct nfs_client *client; - unsigned long timestamp; -}; - -/* - * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special - * standalone procedure for queueing an asynchronous RENEW. - */ -static void nfs4_renew_release(void *calldata) -{ - struct nfs4_renewdata *data = calldata; - struct nfs_client *clp = data->client; - - if (refcount_read(&clp->cl_count) > 1) - nfs4_schedule_state_renewal(clp); - nfs_put_client(clp); - kfree(data); -} - -static void nfs4_renew_done(struct rpc_task *task, void *calldata) -{ - struct nfs4_renewdata *data = calldata; - struct nfs_client *clp = data->client; - unsigned long timestamp = data->timestamp; - - trace_nfs4_renew_async(clp, task->tk_status); - switch (task->tk_status) { - case 0: - break; - case -NFS4ERR_LEASE_MOVED: - nfs4_schedule_lease_moved_recovery(clp); - break; - default: - /* Unless we're shutting down, schedule state recovery! */ - if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0) - return; - if (task->tk_status != NFS4ERR_CB_PATH_DOWN) { - nfs4_schedule_lease_recovery(clp); - return; - } - nfs4_schedule_path_down_recovery(clp); - } - do_renew_lease(clp, timestamp); -} - -static const struct rpc_call_ops nfs4_renew_ops = { - .rpc_call_done = nfs4_renew_done, - .rpc_release = nfs4_renew_release, -}; - -static int nfs4_proc_async_renew(struct nfs_client *clp, const struct cred *cred, unsigned renew_flags) -{ - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], - .rpc_argp = clp, - .rpc_cred = cred, - }; - struct nfs4_renewdata *data; - - if (renew_flags == 0) - return 0; - if (!refcount_inc_not_zero(&clp->cl_count)) - return -EIO; - data = kmalloc(sizeof(*data), GFP_NOFS); - if (data == NULL) { - nfs_put_client(clp); - return -ENOMEM; - } - data->client = clp; - data->timestamp = jiffies; - return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT, - &nfs4_renew_ops, data); -} - -static int nfs4_proc_renew(struct nfs_client *clp, const struct cred *cred) -{ - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], - .rpc_argp = clp, - .rpc_cred = cred, - }; - unsigned long now = jiffies; - int status; - - status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); - if (status < 0) - return status; - do_renew_lease(clp, now); - return 0; -} - static bool nfs4_server_supports_acls(const struct nfs_server *server, enum nfs4_acl_type type) { @@ -10808,15 +10716,7 @@ static const struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = { .recover_lock = nfs41_lock_expired, .establish_clid = nfs41_init_clientid, }; -#endif /* CONFIG_NFS_V4_1 */ -static const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { - .sched_state_renewal = nfs4_proc_async_renew, - .get_state_renewal_cred = nfs4_get_renew_cred, - .renew_lease = nfs4_proc_renew, -}; - -#if defined(CONFIG_NFS_V4_1) static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { .sched_state_renewal = nfs41_proc_async_sequence, .get_state_renewal_cred = nfs4_get_machine_cred, From b6ee9a9ba7a506d69171d637c1eab42a479c4c6c Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 13 Nov 2025 17:11:55 -0500 Subject: [PATCH 35/60] NFS: Split out the nfs40_mig_recovery_ops to nfs40proc.c Signed-off-by: Anna Schumaker --- fs/nfs/nfs40.h | 1 + fs/nfs/nfs40proc.c | 101 +++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4_fs.h | 6 +++ fs/nfs/nfs4proc.c | 115 +++------------------------------------------ 4 files changed, 114 insertions(+), 109 deletions(-) diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index fd606b4a044a..a04fb2390fa7 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -8,6 +8,7 @@ extern const struct rpc_call_ops nfs40_call_sync_ops; extern const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops; extern const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops; extern const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops; +extern const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops; /* nfs40state.c */ int nfs40_discover_server_trunking(struct nfs_client *clp, diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 96ce463c951b..736e782a27e1 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -126,6 +126,102 @@ static int nfs4_proc_renew(struct nfs_client *clp, const struct cred *cred) return 0; } +/* + * This operation also signals the server that this client is + * performing migration recovery. The server can stop returning + * NFS4ERR_LEASE_MOVED to this client. A RENEW operation is + * appended to this compound to identify the client ID which is + * performing recovery. + */ +static int _nfs40_proc_get_locations(struct nfs_server *server, + struct nfs_fh *fhandle, + struct nfs4_fs_locations *locations, + struct page *page, const struct cred *cred) +{ + struct rpc_clnt *clnt = server->client; + u32 bitmask[2] = { + [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, + }; + struct nfs4_fs_locations_arg args = { + .clientid = server->nfs_client->cl_clientid, + .fh = fhandle, + .page = page, + .bitmask = bitmask, + .migration = 1, /* skip LOOKUP */ + .renew = 1, /* append RENEW */ + }; + struct nfs4_fs_locations_res res = { + .fs_locations = locations, + .migration = 1, + .renew = 1, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS], + .rpc_argp = &args, + .rpc_resp = &res, + .rpc_cred = cred, + }; + unsigned long now = jiffies; + int status; + + nfs_fattr_init(locations->fattr); + locations->server = server; + locations->nlocations = 0; + + nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); + status = nfs4_call_sync_sequence(clnt, server, &msg, + &args.seq_args, &res.seq_res); + if (status) + return status; + + renew_lease(server, now); + return 0; +} + +/* + * This operation also signals the server that this client is + * performing "lease moved" recovery. The server can stop + * returning NFS4ERR_LEASE_MOVED to this client. A RENEW operation + * is appended to this compound to identify the client ID which is + * performing recovery. + */ +static int _nfs40_proc_fsid_present(struct inode *inode, const struct cred *cred) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; + struct rpc_clnt *clnt = server->client; + struct nfs4_fsid_present_arg args = { + .fh = NFS_FH(inode), + .clientid = clp->cl_clientid, + .renew = 1, /* append RENEW */ + }; + struct nfs4_fsid_present_res res = { + .renew = 1, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSID_PRESENT], + .rpc_argp = &args, + .rpc_resp = &res, + .rpc_cred = cred, + }; + unsigned long now = jiffies; + int status; + + res.fh = nfs_alloc_fhandle(); + if (res.fh == NULL) + return -ENOMEM; + + nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); + status = nfs4_call_sync_sequence(clnt, server, &msg, + &args.seq_args, &res.seq_res); + nfs_free_fhandle(res.fh); + if (status) + return status; + + do_renew_lease(clp, now); + return 0; +} + const struct rpc_call_ops nfs40_call_sync_ops = { .rpc_call_prepare = nfs40_call_sync_prepare, .rpc_call_done = nfs40_call_sync_done, @@ -153,3 +249,8 @@ const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { .get_state_renewal_cred = nfs4_get_renew_cred, .renew_lease = nfs4_proc_renew, }; + +const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = { + .get_locations = _nfs40_proc_get_locations, + .fsid_present = _nfs40_proc_fsid_present, +}; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index b43fb84145e0..7fcab74f01fb 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -304,9 +304,15 @@ extern int nfs4_async_handle_error(struct rpc_task *task, extern int nfs4_call_sync(struct rpc_clnt *, struct nfs_server *, struct rpc_message *, struct nfs4_sequence_args *, struct nfs4_sequence_res *, int); +extern int nfs4_call_sync_sequence(struct rpc_clnt *clnt, + struct nfs_server *server, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res); extern void nfs4_init_sequence(struct nfs4_sequence_args *, struct nfs4_sequence_res *, int, int); extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, const struct cred *, struct nfs4_setclientid_res *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, const struct cred *); +extern void renew_lease(const struct nfs_server *server, unsigned long timestamp); extern int nfs4_proc_get_rootfh(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, bool); extern int nfs4_proc_bind_conn_to_session(struct nfs_client *, const struct cred *cred); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index dc513bcae3c6..0a8f11997ea6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -762,7 +762,7 @@ void do_renew_lease(struct nfs_client *clp, unsigned long timestamp) spin_unlock(&clp->cl_lock); } -static void renew_lease(const struct nfs_server *server, unsigned long timestamp) +void renew_lease(const struct nfs_server *server, unsigned long timestamp) { struct nfs_client *clp = server->nfs_client; @@ -1207,11 +1207,11 @@ static int nfs4_do_call_sync(struct rpc_clnt *clnt, return nfs4_call_sync_custom(&task_setup); } -static int nfs4_call_sync_sequence(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res) +int nfs4_call_sync_sequence(struct rpc_clnt *clnt, + struct nfs_server *server, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res) { unsigned short task_flags = 0; @@ -8279,58 +8279,6 @@ int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, return err; } -/* - * This operation also signals the server that this client is - * performing migration recovery. The server can stop returning - * NFS4ERR_LEASE_MOVED to this client. A RENEW operation is - * appended to this compound to identify the client ID which is - * performing recovery. - */ -static int _nfs40_proc_get_locations(struct nfs_server *server, - struct nfs_fh *fhandle, - struct nfs4_fs_locations *locations, - struct page *page, const struct cred *cred) -{ - struct rpc_clnt *clnt = server->client; - u32 bitmask[2] = { - [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, - }; - struct nfs4_fs_locations_arg args = { - .clientid = server->nfs_client->cl_clientid, - .fh = fhandle, - .page = page, - .bitmask = bitmask, - .migration = 1, /* skip LOOKUP */ - .renew = 1, /* append RENEW */ - }; - struct nfs4_fs_locations_res res = { - .fs_locations = locations, - .migration = 1, - .renew = 1, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS], - .rpc_argp = &args, - .rpc_resp = &res, - .rpc_cred = cred, - }; - unsigned long now = jiffies; - int status; - - nfs_fattr_init(locations->fattr); - locations->server = server; - locations->nlocations = 0; - - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); - status = nfs4_call_sync_sequence(clnt, server, &msg, - &args.seq_args, &res.seq_res); - if (status) - return status; - - renew_lease(server, now); - return 0; -} - #ifdef CONFIG_NFS_V4_1 /* @@ -8443,50 +8391,6 @@ int nfs4_proc_get_locations(struct nfs_server *server, return status; } -/* - * This operation also signals the server that this client is - * performing "lease moved" recovery. The server can stop - * returning NFS4ERR_LEASE_MOVED to this client. A RENEW operation - * is appended to this compound to identify the client ID which is - * performing recovery. - */ -static int _nfs40_proc_fsid_present(struct inode *inode, const struct cred *cred) -{ - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; - struct rpc_clnt *clnt = server->client; - struct nfs4_fsid_present_arg args = { - .fh = NFS_FH(inode), - .clientid = clp->cl_clientid, - .renew = 1, /* append RENEW */ - }; - struct nfs4_fsid_present_res res = { - .renew = 1, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSID_PRESENT], - .rpc_argp = &args, - .rpc_resp = &res, - .rpc_cred = cred, - }; - unsigned long now = jiffies; - int status; - - res.fh = nfs_alloc_fhandle(); - if (res.fh == NULL) - return -ENOMEM; - - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); - status = nfs4_call_sync_sequence(clnt, server, &msg, - &args.seq_args, &res.seq_res); - nfs_free_fhandle(res.fh); - if (status) - return status; - - do_renew_lease(clp, now); - return 0; -} - #ifdef CONFIG_NFS_V4_1 /* @@ -10722,14 +10626,7 @@ static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { .get_state_renewal_cred = nfs4_get_machine_cred, .renew_lease = nfs4_proc_sequence, }; -#endif -static const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = { - .get_locations = _nfs40_proc_get_locations, - .fsid_present = _nfs40_proc_fsid_present, -}; - -#if defined(CONFIG_NFS_V4_1) static const struct nfs4_mig_recovery_ops nfs41_mig_recovery_ops = { .get_locations = _nfs41_proc_get_locations, .fsid_present = _nfs41_proc_fsid_present, From 0cba208fb979509721031d2469b31c45140e3063 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 14 Nov 2025 14:26:36 -0500 Subject: [PATCH 36/60] NFS: Move the NFS v4.0 minor version ops into nfs40proc.c Signed-off-by: Anna Schumaker --- fs/nfs/nfs40.h | 1 + fs/nfs/nfs40proc.c | 106 +++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4_fs.h | 4 ++ fs/nfs/nfs4proc.c | 115 ++------------------------------------------- 4 files changed, 115 insertions(+), 111 deletions(-) diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index a04fb2390fa7..05ba9f1afe7c 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -9,6 +9,7 @@ extern const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops; extern const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops; extern const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops; extern const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops; +extern const struct nfs4_minor_version_ops nfs_v4_0_minor_ops; /* nfs40state.c */ int nfs40_discover_server_trunking(struct nfs_client *clp, diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 736e782a27e1..36802f9b94b5 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -126,6 +126,13 @@ static int nfs4_proc_renew(struct nfs_client *clp, const struct cred *cred) return 0; } +static int nfs40_test_and_free_expired_stateid(struct nfs_server *server, + nfs4_stateid *stateid, + const struct cred *cred) +{ + return -NFS4ERR_BAD_STATEID; +} + /* * This operation also signals the server that this client is * performing migration recovery. The server can stop returning @@ -222,6 +229,86 @@ static int _nfs40_proc_fsid_present(struct inode *inode, const struct cred *cred return 0; } +struct nfs_release_lockowner_data { + struct nfs4_lock_state *lsp; + struct nfs_server *server; + struct nfs_release_lockowner_args args; + struct nfs_release_lockowner_res res; + unsigned long timestamp; +}; + +static void nfs4_release_lockowner_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs_release_lockowner_data *data = calldata; + struct nfs_server *server = data->server; + nfs4_setup_sequence(server->nfs_client, &data->args.seq_args, + &data->res.seq_res, task); + data->args.lock_owner.clientid = server->nfs_client->cl_clientid; + data->timestamp = jiffies; +} + +static void nfs4_release_lockowner_done(struct rpc_task *task, void *calldata) +{ + struct nfs_release_lockowner_data *data = calldata; + struct nfs_server *server = data->server; + + nfs40_sequence_done(task, &data->res.seq_res); + + switch (task->tk_status) { + case 0: + renew_lease(server, data->timestamp); + break; + case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_EXPIRED: + nfs4_schedule_lease_recovery(server->nfs_client); + break; + case -NFS4ERR_LEASE_MOVED: + case -NFS4ERR_DELAY: + if (nfs4_async_handle_error(task, server, + NULL, NULL) == -EAGAIN) + rpc_restart_call_prepare(task); + } +} + +static void nfs4_release_lockowner_release(void *calldata) +{ + struct nfs_release_lockowner_data *data = calldata; + nfs4_free_lock_state(data->server, data->lsp); + kfree(calldata); +} + +static const struct rpc_call_ops nfs4_release_lockowner_ops = { + .rpc_call_prepare = nfs4_release_lockowner_prepare, + .rpc_call_done = nfs4_release_lockowner_done, + .rpc_release = nfs4_release_lockowner_release, +}; + +static void +nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp) +{ + struct nfs_release_lockowner_data *data; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER], + }; + + if (server->nfs_client->cl_mvops->minor_version != 0) + return; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return; + data->lsp = lsp; + data->server = server; + data->args.lock_owner.clientid = server->nfs_client->cl_clientid; + data->args.lock_owner.id = lsp->ls_seqid.owner_id; + data->args.lock_owner.s_dev = server->s_dev; + + msg.rpc_argp = &data->args; + msg.rpc_resp = &data->res; + nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0); + rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data); +} + const struct rpc_call_ops nfs40_call_sync_ops = { .rpc_call_prepare = nfs40_call_sync_prepare, .rpc_call_done = nfs40_call_sync_done, @@ -254,3 +341,22 @@ const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = { .get_locations = _nfs40_proc_get_locations, .fsid_present = _nfs40_proc_fsid_present, }; + +const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { + .minor_version = 0, + .init_caps = NFS_CAP_READDIRPLUS + | NFS_CAP_ATOMIC_OPEN + | NFS_CAP_POSIX_LOCK, + .init_client = nfs40_init_client, + .shutdown_client = nfs40_shutdown_client, + .match_stateid = nfs4_match_stateid, + .find_root_sec = nfs4_find_root_sec, + .free_lock_state = nfs4_release_lockowner, + .test_and_free_expired = nfs40_test_and_free_expired_stateid, + .alloc_seqid = nfs_alloc_seqid, + .call_sync_ops = &nfs40_call_sync_ops, + .reboot_recovery_ops = &nfs40_reboot_recovery_ops, + .nograce_recovery_ops = &nfs40_nograce_recovery_ops, + .state_renewal_ops = &nfs40_state_renewal_ops, + .mig_recovery_ops = &nfs40_mig_recovery_ops, +}; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 7fcab74f01fb..18f04906c5fa 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -310,6 +310,7 @@ extern int nfs4_call_sync_sequence(struct rpc_clnt *clnt, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res); extern void nfs4_init_sequence(struct nfs4_sequence_args *, struct nfs4_sequence_res *, int, int); +extern int nfs40_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res); extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, const struct cred *, struct nfs4_setclientid_res *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, const struct cred *); extern void renew_lease(const struct nfs_server *server, unsigned long timestamp); @@ -365,6 +366,9 @@ extern void nfs_finish_clear_delegation_stateid(struct nfs4_state *state, const nfs4_stateid *stateid); extern void nfs_state_clear_open_state_flags(struct nfs4_state *state); extern void do_renew_lease(struct nfs_client *clp, unsigned long timestamp); +extern bool nfs4_match_stateid(const nfs4_stateid *s1, const nfs4_stateid *s2); +extern int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fattr *fattr); #if defined(CONFIG_NFS_V4_1) extern int nfs41_sequence_done(struct rpc_task *, struct nfs4_sequence_res *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0a8f11997ea6..136fcc71cbe4 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -795,8 +795,8 @@ static void nfs40_sequence_free_slot(struct nfs4_sequence_res *res) res->sr_slot = NULL; } -static int nfs40_sequence_done(struct rpc_task *task, - struct nfs4_sequence_res *res) +int nfs40_sequence_done(struct rpc_task *task, + struct nfs4_sequence_res *res) { if (res->sr_slot != NULL) nfs40_sequence_free_slot(res); @@ -2882,12 +2882,6 @@ void nfs_finish_clear_delegation_stateid(struct nfs4_state *state, nfs_state_clear_delegation(state); } -static int nfs40_test_and_free_expired_stateid(struct nfs_server *server, - nfs4_stateid *stateid, const struct cred *cred) -{ - return -NFS4ERR_BAD_STATEID; -} - #if defined(CONFIG_NFS_V4_1) static int nfs41_test_and_free_expired_stateid(struct nfs_server *server, nfs4_stateid *stateid, const struct cred *cred) @@ -4289,7 +4283,7 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, * Returns zero on success, or a negative NFS4ERR value, or a * negative errno value. */ -static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, +int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { /* Per 3530bis 15.33.5 */ @@ -7868,86 +7862,6 @@ int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, return nfs4_handle_delegation_recall_error(server, state, stateid, fl, err); } -struct nfs_release_lockowner_data { - struct nfs4_lock_state *lsp; - struct nfs_server *server; - struct nfs_release_lockowner_args args; - struct nfs_release_lockowner_res res; - unsigned long timestamp; -}; - -static void nfs4_release_lockowner_prepare(struct rpc_task *task, void *calldata) -{ - struct nfs_release_lockowner_data *data = calldata; - struct nfs_server *server = data->server; - nfs4_setup_sequence(server->nfs_client, &data->args.seq_args, - &data->res.seq_res, task); - data->args.lock_owner.clientid = server->nfs_client->cl_clientid; - data->timestamp = jiffies; -} - -static void nfs4_release_lockowner_done(struct rpc_task *task, void *calldata) -{ - struct nfs_release_lockowner_data *data = calldata; - struct nfs_server *server = data->server; - - nfs40_sequence_done(task, &data->res.seq_res); - - switch (task->tk_status) { - case 0: - renew_lease(server, data->timestamp); - break; - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_EXPIRED: - nfs4_schedule_lease_recovery(server->nfs_client); - break; - case -NFS4ERR_LEASE_MOVED: - case -NFS4ERR_DELAY: - if (nfs4_async_handle_error(task, server, - NULL, NULL) == -EAGAIN) - rpc_restart_call_prepare(task); - } -} - -static void nfs4_release_lockowner_release(void *calldata) -{ - struct nfs_release_lockowner_data *data = calldata; - nfs4_free_lock_state(data->server, data->lsp); - kfree(calldata); -} - -static const struct rpc_call_ops nfs4_release_lockowner_ops = { - .rpc_call_prepare = nfs4_release_lockowner_prepare, - .rpc_call_done = nfs4_release_lockowner_done, - .rpc_release = nfs4_release_lockowner_release, -}; - -static void -nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp) -{ - struct nfs_release_lockowner_data *data; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER], - }; - - if (server->nfs_client->cl_mvops->minor_version != 0) - return; - - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return; - data->lsp = lsp; - data->server = server; - data->args.lock_owner.clientid = server->nfs_client->cl_clientid; - data->args.lock_owner.id = lsp->ls_seqid.owner_id; - data->args.lock_owner.s_dev = server->s_dev; - - msg.rpc_argp = &data->args; - msg.rpc_resp = &data->res; - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0); - rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data); -} - #define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" static int nfs4_xattr_set_nfs4_acl(const struct xattr_handler *handler, @@ -10593,7 +10507,7 @@ static bool nfs41_match_stateid(const nfs4_stateid *s1, #endif /* CONFIG_NFS_V4_1 */ -static bool nfs4_match_stateid(const nfs4_stateid *s1, +bool nfs4_match_stateid(const nfs4_stateid *s1, const nfs4_stateid *s2) { trace_nfs4_match_stateid(s1, s2); @@ -10631,28 +10545,7 @@ static const struct nfs4_mig_recovery_ops nfs41_mig_recovery_ops = { .get_locations = _nfs41_proc_get_locations, .fsid_present = _nfs41_proc_fsid_present, }; -#endif /* CONFIG_NFS_V4_1 */ -static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { - .minor_version = 0, - .init_caps = NFS_CAP_READDIRPLUS - | NFS_CAP_ATOMIC_OPEN - | NFS_CAP_POSIX_LOCK, - .init_client = nfs40_init_client, - .shutdown_client = nfs40_shutdown_client, - .match_stateid = nfs4_match_stateid, - .find_root_sec = nfs4_find_root_sec, - .free_lock_state = nfs4_release_lockowner, - .test_and_free_expired = nfs40_test_and_free_expired_stateid, - .alloc_seqid = nfs_alloc_seqid, - .call_sync_ops = &nfs40_call_sync_ops, - .reboot_recovery_ops = &nfs40_reboot_recovery_ops, - .nograce_recovery_ops = &nfs40_nograce_recovery_ops, - .state_renewal_ops = &nfs40_state_renewal_ops, - .mig_recovery_ops = &nfs40_mig_recovery_ops, -}; - -#if defined(CONFIG_NFS_V4_1) static struct nfs_seqid * nfs_alloc_no_seqid(struct nfs_seqid_counter *arg1, gfp_t arg2) { From c30493d8c517e6138c9ba303a9b6484c8f0bd744 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 14 Nov 2025 14:35:38 -0500 Subject: [PATCH 37/60] NFS: Make the various NFS v4.0 operations static again They don't need to be visible outside of nfs40proc.c anymore now that the minor version ops have been moved over. Signed-off-by: Anna Schumaker --- fs/nfs/nfs40.h | 5 ----- fs/nfs/nfs40proc.c | 10 +++++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index 05ba9f1afe7c..272e1ffdb161 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -4,11 +4,6 @@ /* nfs40proc.c */ -extern const struct rpc_call_ops nfs40_call_sync_ops; -extern const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops; -extern const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops; -extern const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops; -extern const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops; extern const struct nfs4_minor_version_ops nfs_v4_0_minor_ops; /* nfs40state.c */ diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 36802f9b94b5..5968a3318d14 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -309,12 +309,12 @@ nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp) rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data); } -const struct rpc_call_ops nfs40_call_sync_ops = { +static const struct rpc_call_ops nfs40_call_sync_ops = { .rpc_call_prepare = nfs40_call_sync_prepare, .rpc_call_done = nfs40_call_sync_done, }; -const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { +static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, .recover_open = nfs4_open_reclaim, @@ -323,7 +323,7 @@ const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { .detect_trunking = nfs40_discover_server_trunking, }; -const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { +static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE, .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, .recover_open = nfs40_open_expired, @@ -331,13 +331,13 @@ const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { .establish_clid = nfs4_init_clientid, }; -const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { +static const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { .sched_state_renewal = nfs4_proc_async_renew, .get_state_renewal_cred = nfs4_get_renew_cred, .renew_lease = nfs4_proc_renew, }; -const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = { +static const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = { .get_locations = _nfs40_proc_get_locations, .fsid_present = _nfs40_proc_fsid_present, }; From c695ac2d60f495a50aa2aceb4fdeb587e1008f65 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 14 Nov 2025 14:48:36 -0500 Subject: [PATCH 38/60] NFS: Move nfs40_shutdown_client into nfs40client.c Signed-off-by: Anna Schumaker --- fs/nfs/nfs40.h | 3 +++ fs/nfs/nfs40client.c | 9 +++++++++ fs/nfs/nfs40proc.c | 1 + fs/nfs/nfs4_fs.h | 1 - fs/nfs/nfs4client.c | 8 -------- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index 272e1ffdb161..9369bb08825a 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -3,6 +3,9 @@ #define __LINUX_FS_NFS_NFS4_0_H +/* nfs40client.c */ +void nfs40_shutdown_client(struct nfs_client *); + /* nfs40proc.c */ extern const struct nfs4_minor_version_ops nfs_v4_0_minor_ops; diff --git a/fs/nfs/nfs40client.c b/fs/nfs/nfs40client.c index fae4ff584b1b..4a19ad9df789 100644 --- a/fs/nfs/nfs40client.c +++ b/fs/nfs/nfs40client.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include #include "nfs4_fs.h" +#include "nfs4session.h" #include "callback.h" #include "internal.h" #include "netns.h" @@ -44,6 +45,14 @@ static bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2) return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0; } +void nfs40_shutdown_client(struct nfs_client *clp) +{ + if (clp->cl_slot_tbl) { + nfs4_shutdown_slot_table(clp->cl_slot_tbl); + kfree(clp->cl_slot_tbl); + } +} + /** * nfs40_walk_client_list - Find server that recognizes a client ID * diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 5968a3318d14..0399e2e68c6b 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -5,6 +5,7 @@ #include #include "internal.h" #include "nfs4_fs.h" +#include "nfs40.h" #include "nfs4trace.h" static void nfs40_call_sync_prepare(struct rpc_task *task, void *calldata) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 18f04906c5fa..3a81a658e5d2 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -482,7 +482,6 @@ extern const u32 nfs4_pathconf_bitmap[3]; extern const u32 nfs4_fsinfo_bitmap[3]; extern const u32 nfs4_fs_locations_bitmap[3]; -void nfs40_shutdown_client(struct nfs_client *); void nfs41_shutdown_client(struct nfs_client *); int nfs40_init_client(struct nfs_client *); int nfs41_init_client(struct nfs_client *); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 517cf8af2943..d83a8a2a3c70 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -189,14 +189,6 @@ void nfs41_shutdown_client(struct nfs_client *clp) } #endif /* CONFIG_NFS_V4_1 */ -void nfs40_shutdown_client(struct nfs_client *clp) -{ - if (clp->cl_slot_tbl) { - nfs4_shutdown_slot_table(clp->cl_slot_tbl); - kfree(clp->cl_slot_tbl); - } -} - struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) { char buf[INET6_ADDRSTRLEN + 1]; From 214359fe1689999048809cf752a997847d538dc6 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 14 Nov 2025 15:15:19 -0500 Subject: [PATCH 39/60] NFS: Move nfs40_init_client into nfs40client.c Signed-off-by: Anna Schumaker --- fs/nfs/nfs40.h | 1 + fs/nfs/nfs40client.c | 27 +++++++++++++++++++++++++++ fs/nfs/nfs4_fs.h | 1 - fs/nfs/nfs4client.c | 27 --------------------------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index 9369bb08825a..5a9c5d367b12 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -5,6 +5,7 @@ /* nfs40client.c */ void nfs40_shutdown_client(struct nfs_client *); +int nfs40_init_client(struct nfs_client *); /* nfs40proc.c */ extern const struct nfs4_minor_version_ops nfs_v4_0_minor_ops; diff --git a/fs/nfs/nfs40client.c b/fs/nfs/nfs40client.c index 4a19ad9df789..cd105dc1cee1 100644 --- a/fs/nfs/nfs40client.c +++ b/fs/nfs/nfs40client.c @@ -53,6 +53,33 @@ void nfs40_shutdown_client(struct nfs_client *clp) } } +/** + * nfs40_init_client - nfs_client initialization tasks for NFSv4.0 + * @clp: nfs_client to initialize + * + * Returns zero on success, or a negative errno if some error occurred. + */ +int nfs40_init_client(struct nfs_client *clp) +{ + struct nfs4_slot_table *tbl; + int ret; + + tbl = kzalloc(sizeof(*tbl), GFP_NOFS); + if (tbl == NULL) + return -ENOMEM; + + ret = nfs4_setup_slot_table(tbl, NFS4_MAX_SLOT_TABLE, + "NFSv4.0 transport Slot table"); + if (ret) { + nfs4_shutdown_slot_table(tbl); + kfree(tbl); + return ret; + } + + clp->cl_slot_tbl = tbl; + return 0; +} + /** * nfs40_walk_client_list - Find server that recognizes a client ID * diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 3a81a658e5d2..9d0bec3a23f3 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -483,7 +483,6 @@ extern const u32 nfs4_fsinfo_bitmap[3]; extern const u32 nfs4_fs_locations_bitmap[3]; void nfs41_shutdown_client(struct nfs_client *); -int nfs40_init_client(struct nfs_client *); int nfs41_init_client(struct nfs_client *); void nfs4_free_client(struct nfs_client *); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index d83a8a2a3c70..c376b2420b6c 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -331,33 +331,6 @@ static int nfs4_init_callback(struct nfs_client *clp) return 0; } -/** - * nfs40_init_client - nfs_client initialization tasks for NFSv4.0 - * @clp: nfs_client to initialize - * - * Returns zero on success, or a negative errno if some error occurred. - */ -int nfs40_init_client(struct nfs_client *clp) -{ - struct nfs4_slot_table *tbl; - int ret; - - tbl = kzalloc(sizeof(*tbl), GFP_NOFS); - if (tbl == NULL) - return -ENOMEM; - - ret = nfs4_setup_slot_table(tbl, NFS4_MAX_SLOT_TABLE, - "NFSv4.0 transport Slot table"); - if (ret) { - nfs4_shutdown_slot_table(tbl); - kfree(tbl); - return ret; - } - - clp->cl_slot_tbl = tbl; - return 0; -} - #if defined(CONFIG_NFS_V4_1) /** From 424a3a71b8059cdb8e755bac35c29184a4ef4482 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 14 Nov 2025 16:03:41 -0500 Subject: [PATCH 40/60] NFS: Move NFS v4.0 pathdown recovery into nfs40client.c Signed-off-by: Anna Schumaker --- fs/nfs/nfs40.h | 1 + fs/nfs/nfs40client.c | 23 +++++++++++++++++++++++ fs/nfs/nfs4state.c | 23 +---------------------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index 5a9c5d367b12..ee09aac738c8 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -6,6 +6,7 @@ /* nfs40client.c */ void nfs40_shutdown_client(struct nfs_client *); int nfs40_init_client(struct nfs_client *); +void nfs40_handle_cb_pathdown(struct nfs_client *clp); /* nfs40proc.c */ extern const struct nfs4_minor_version_ops nfs_v4_0_minor_ops; diff --git a/fs/nfs/nfs40client.c b/fs/nfs/nfs40client.c index cd105dc1cee1..fc0c6b48fc94 100644 --- a/fs/nfs/nfs40client.c +++ b/fs/nfs/nfs40client.c @@ -3,6 +3,7 @@ #include "nfs4_fs.h" #include "nfs4session.h" #include "callback.h" +#include "delegation.h" #include "internal.h" #include "netns.h" #include "nfs40.h" @@ -80,6 +81,28 @@ int nfs40_init_client(struct nfs_client *clp) return 0; } +/* + * nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN + * @clp: client to process + * + * Set the NFS4CLNT_LEASE_EXPIRED state in order to force a + * resend of the SETCLIENTID and hence re-establish the + * callback channel. Then return all existing delegations. + */ +void nfs40_handle_cb_pathdown(struct nfs_client *clp) +{ + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + nfs_expire_all_delegations(clp); + dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__, + clp->cl_hostname); +} + +void nfs4_schedule_path_down_recovery(struct nfs_client *clp) +{ + nfs40_handle_cb_pathdown(clp); + nfs4_schedule_state_manager(clp); +} + /** * nfs40_walk_client_list - Find server that recognizes a client ID * diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index ead4d1ac4d0b..f3d441bb28af 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -54,6 +54,7 @@ #include #include "nfs4_fs.h" +#include "nfs40.h" #include "callback.h" #include "delegation.h" #include "internal.h" @@ -1294,28 +1295,6 @@ int nfs4_client_recover_expired_lease(struct nfs_client *clp) return ret; } -/* - * nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN - * @clp: client to process - * - * Set the NFS4CLNT_LEASE_EXPIRED state in order to force a - * resend of the SETCLIENTID and hence re-establish the - * callback channel. Then return all existing delegations. - */ -static void nfs40_handle_cb_pathdown(struct nfs_client *clp) -{ - set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); - nfs_expire_all_delegations(clp); - dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__, - clp->cl_hostname); -} - -void nfs4_schedule_path_down_recovery(struct nfs_client *clp) -{ - nfs40_handle_cb_pathdown(clp); - nfs4_schedule_state_manager(clp); -} - static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state) { From 211891374353c91f9aa4bb8cfca5d681b990f3d6 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 20 Nov 2025 16:55:09 -0500 Subject: [PATCH 41/60] NFS: Pass a struct nfs_client to nfs4_init_sequence() No functional change in this patch. This just makes the next patch where I introduce "sequence slot operations" simpler. Signed-off-by: Anna Schumaker --- fs/nfs/nfs40proc.c | 14 ++--- fs/nfs/nfs42proc.c | 13 +++-- fs/nfs/nfs4_fs.h | 3 +- fs/nfs/nfs4proc.c | 126 +++++++++++++++++++++++++-------------------- 4 files changed, 87 insertions(+), 69 deletions(-) diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 0399e2e68c6b..9b5b57170471 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -147,11 +147,12 @@ static int _nfs40_proc_get_locations(struct nfs_server *server, struct page *page, const struct cred *cred) { struct rpc_clnt *clnt = server->client; + struct nfs_client *clp = server->nfs_client; u32 bitmask[2] = { [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, }; struct nfs4_fs_locations_arg args = { - .clientid = server->nfs_client->cl_clientid, + .clientid = clp->cl_clientid, .fh = fhandle, .page = page, .bitmask = bitmask, @@ -176,7 +177,7 @@ static int _nfs40_proc_get_locations(struct nfs_server *server, locations->server = server; locations->nlocations = 0; - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); + nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 1); status = nfs4_call_sync_sequence(clnt, server, &msg, &args.seq_args, &res.seq_res); if (status) @@ -219,7 +220,7 @@ static int _nfs40_proc_fsid_present(struct inode *inode, const struct cred *cred if (res.fh == NULL) return -ENOMEM; - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); + nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 1); status = nfs4_call_sync_sequence(clnt, server, &msg, &args.seq_args, &res.seq_res); nfs_free_fhandle(res.fh); @@ -288,11 +289,12 @@ static void nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp) { struct nfs_release_lockowner_data *data; + struct nfs_client *clp = server->nfs_client; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER], }; - if (server->nfs_client->cl_mvops->minor_version != 0) + if (clp->cl_mvops->minor_version != 0) return; data = kmalloc(sizeof(*data), GFP_KERNEL); @@ -300,13 +302,13 @@ nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp) return; data->lsp = lsp; data->server = server; - data->args.lock_owner.clientid = server->nfs_client->cl_clientid; + data->args.lock_owner.clientid = clp->cl_clientid; data->args.lock_owner.id = lsp->ls_seqid.owner_id; data->args.lock_owner.s_dev = server->s_dev; msg.rpc_argp = &data->args; msg.rpc_resp = &data->res; - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0); + nfs4_init_sequence(clp, &data->args.seq_args, &data->res.seq_res, 0, 0); rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data); } diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index c08520828708..3487948d7181 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -670,8 +670,8 @@ static int nfs42_do_offload_cancel_async(struct file *dst, msg.rpc_argp = &data->args; msg.rpc_resp = &data->res; task_setup_data.callback_data = data; - nfs4_init_sequence(&data->args.osa_seq_args, &data->res.osr_seq_res, - 1, 0); + nfs4_init_sequence(dst_server->nfs_client, &data->args.osa_seq_args, + &data->res.osr_seq_res, 1, 0); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); @@ -1072,7 +1072,8 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server, nfs42_layoutstat_release(data); return -EAGAIN; } - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0); + nfs4_init_sequence(server->nfs_client, &data->args.seq_args, + &data->res.seq_res, 0, 0); task = rpc_run_task(&task_setup); if (IS_ERR(task)) return PTR_ERR(task); @@ -1210,6 +1211,7 @@ int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg, const struct nfs42_layout_error *errors, size_t n) { struct inode *inode = lseg->pls_layout->plh_inode; + struct nfs_server *server = NFS_SERVER(inode); struct nfs42_layouterror_data *data; struct rpc_task *task; struct rpc_message msg = { @@ -1237,8 +1239,9 @@ int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg, msg.rpc_argp = &data->args; msg.rpc_resp = &data->res; task_setup.callback_data = data; - task_setup.rpc_client = NFS_SERVER(inode)->client; - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0); + task_setup.rpc_client = server->client; + nfs4_init_sequence(server->nfs_client, &data->args.seq_args, + &data->res.seq_res, 0, 0); task = rpc_run_task(&task_setup); if (IS_ERR(task)) return PTR_ERR(task); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 9d0bec3a23f3..34cdbf42b865 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -309,7 +309,8 @@ extern int nfs4_call_sync_sequence(struct rpc_clnt *clnt, struct rpc_message *msg, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res); -extern void nfs4_init_sequence(struct nfs4_sequence_args *, struct nfs4_sequence_res *, int, int); +extern void nfs4_init_sequence(struct nfs_client *clp, struct nfs4_sequence_args *, + struct nfs4_sequence_res *, int, int); extern int nfs40_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res); extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, const struct cred *, struct nfs4_setclientid_res *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, const struct cred *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 136fcc71cbe4..574ebefcb4fa 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -770,7 +770,8 @@ void renew_lease(const struct nfs_server *server, unsigned long timestamp) do_renew_lease(clp, timestamp); } -void nfs4_init_sequence(struct nfs4_sequence_args *args, +void nfs4_init_sequence(struct nfs_client *clp, + struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply, int privileged) { @@ -1228,7 +1229,7 @@ int nfs4_call_sync(struct rpc_clnt *clnt, struct nfs4_sequence_res *res, int cache_reply) { - nfs4_init_sequence(args, res, cache_reply, 0); + nfs4_init_sequence(server->nfs_client, args, res, cache_reply, 0); return nfs4_call_sync_sequence(clnt, server, msg, args, res); } @@ -2504,8 +2505,8 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data) }; int status; - nfs4_init_sequence(&data->c_arg.seq_args, &data->c_res.seq_res, 1, - data->is_recover); + nfs4_init_sequence(server->nfs_client, &data->c_arg.seq_args, + &data->c_res.seq_res, 1, data->is_recover); kref_get(&data->kref); data->rpc_done = false; data->rpc_status = 0; @@ -2652,6 +2653,7 @@ static int nfs4_run_open_task(struct nfs4_opendata *data, { struct inode *dir = d_inode(data->dir); struct nfs_server *server = NFS_SERVER(dir); + struct nfs_client *clp = server->nfs_client; struct nfs_openargs *o_arg = &data->o_arg; struct nfs_openres *o_res = &data->o_res; struct rpc_task *task; @@ -2680,11 +2682,11 @@ static int nfs4_run_open_task(struct nfs4_opendata *data, data->cancelled = false; data->is_recover = false; if (!ctx) { - nfs4_init_sequence(&o_arg->seq_args, &o_res->seq_res, 1, 1); + nfs4_init_sequence(clp, &o_arg->seq_args, &o_res->seq_res, 1, 1); data->is_recover = true; task_setup_data.flags |= RPC_TASK_TIMEOUT; } else { - nfs4_init_sequence(&o_arg->seq_args, &o_res->seq_res, 1, 0); + nfs4_init_sequence(clp, &o_arg->seq_args, &o_res->seq_res, 1, 0); pnfs_lgopen_prepare(data, ctx); } task = rpc_run_task(&task_setup_data); @@ -3807,6 +3809,7 @@ static const struct rpc_call_ops nfs4_close_ops = { int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait) { struct nfs_server *server = NFS_SERVER(state->inode); + struct nfs_client *clp = server->nfs_client; struct nfs_seqid *(*alloc_seqid)(struct nfs_seqid_counter *, gfp_t); struct nfs4_closedata *calldata; struct nfs4_state_owner *sp = state->owner; @@ -3827,20 +3830,21 @@ int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait) if (nfs_server_capable(state->inode, NFS_CAP_MOVEABLE)) task_setup_data.flags |= RPC_TASK_MOVEABLE; - nfs4_state_protect(server->nfs_client, NFS_SP4_MACH_CRED_CLEANUP, + nfs4_state_protect(clp, NFS_SP4_MACH_CRED_CLEANUP, &task_setup_data.rpc_client, &msg); calldata = kzalloc(sizeof(*calldata), gfp_mask); if (calldata == NULL) goto out; - nfs4_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 1, 0); + nfs4_init_sequence(clp, &calldata->arg.seq_args, + &calldata->res.seq_res, 1, 0); calldata->inode = state->inode; calldata->state = state; calldata->arg.fh = NFS_FH(state->inode); if (!nfs4_copy_open_stateid(&calldata->arg.stateid, state)) goto out_free_calldata; /* Serialization for the sequence id */ - alloc_seqid = server->nfs_client->cl_mvops->alloc_seqid; + alloc_seqid = clp->cl_mvops->alloc_seqid; calldata->arg.seqid = alloc_seqid(&state->owner->so_seqid, gfp_mask); if (IS_ERR(calldata->arg.seqid)) goto out_free_calldata; @@ -4498,10 +4502,11 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, .callback_ops = &nfs4_call_getattr_ops, .callback_data = &data, }; + struct nfs_client *clp = server->nfs_client; struct nfs4_gdd_res gdd_res; int status; - if (nfs4_has_session(server->nfs_client)) + if (nfs4_has_session(clp)) task_setup.flags = RPC_TASK_MOVEABLE; /* Is this is an attribute revalidation, subject to softreval? */ @@ -4514,7 +4519,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, 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); + nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 0); status = nfs4_call_sync_custom(&task_setup); @@ -4655,7 +4660,7 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, nfs_fattr_init(fattr); dprintk("NFS call lookup %pd2\n", dentry); - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0); + nfs4_init_sequence(server->nfs_client, &args.seq_args, &res.seq_res, 0, 0); status = nfs4_do_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, task_flags); dprintk("NFS reply lookup: %d\n", status); @@ -4773,7 +4778,7 @@ static int _nfs4_proc_lookupp(struct inode *inode, args.bitmask = nfs4_bitmask(server, fattr->label); nfs_fattr_init(fattr); - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0); + nfs4_init_sequence(server->nfs_client, &args.seq_args, &res.seq_res, 0, 0); dprintk("NFS call lookupp ino=0x%lx\n", inode->i_ino); status = nfs4_do_call_sync(clnt, server, &msg, &args.seq_args, @@ -5018,10 +5023,12 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, { struct nfs_removeargs *args = msg->rpc_argp; struct nfs_removeres *res = msg->rpc_resp; + struct nfs_server *server = NFS_SB(dentry->d_sb); - res->server = NFS_SB(dentry->d_sb); + res->server = server; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; - nfs4_init_sequence(&args->seq_args, &res->seq_res, 1, 0); + nfs4_init_sequence(server->nfs_client, &args->seq_args, + &res->seq_res, 1, 0); nfs_fattr_init(res->dir_attr); nfs_request_directory_delegation(d_inode(dentry->d_parent)); @@ -5062,6 +5069,7 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg, struct dentry *new_dentry, struct inode *same_parent) { + struct nfs_server *server = NFS_SB(old_dentry->d_sb); struct nfs_renameargs *arg = msg->rpc_argp; struct nfs_renameres *res = msg->rpc_resp; struct inode *old_inode = d_inode(old_dentry); @@ -5074,8 +5082,9 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg, if (same_parent) nfs_request_directory_delegation(same_parent); msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME]; - res->server = NFS_SB(old_dentry->d_sb); - nfs4_init_sequence(&arg->seq_args, &res->seq_res, 1, 0); + res->server = server, + nfs4_init_sequence(server->nfs_client, &arg->seq_args, + &res->seq_res, 1, 0); } static void nfs4_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data) @@ -5743,7 +5752,8 @@ static void nfs4_proc_read_setup(struct nfs_pgio_header *hdr, hdr->pgio_done_cb = nfs4_read_done_cb; if (!nfs42_read_plus_support(hdr, msg)) msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ]; - nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0); + nfs4_init_sequence(NFS_SERVER(hdr->inode)->nfs_client, + &hdr->args.seq_args, &hdr->res.seq_res, 0, 0); } static int nfs4_proc_pgio_rpc_prepare(struct rpc_task *task, @@ -5885,7 +5895,8 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr, hdr->timestamp = jiffies; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE]; - nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0); + nfs4_init_sequence(server->nfs_client, &hdr->args.seq_args, + &hdr->res.seq_res, 0, 0); nfs4_state_protect_write(hdr->ds_clp ? hdr->ds_clp : server->nfs_client, clnt, msg, hdr); } @@ -5926,7 +5937,8 @@ static void nfs4_proc_commit_setup(struct nfs_commit_data *data, struct rpc_mess data->commit_done_cb = nfs4_commit_done_cb; data->res.server = server; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT]; - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0); + nfs4_init_sequence(server->nfs_client, &data->args.seq_args, + &data->res.seq_res, 1, 0); nfs4_state_protect(data->ds_clp ? data->ds_clp : server->nfs_client, NFS_SP4_MACH_CRED_COMMIT, clnt, msg); } @@ -6922,12 +6934,8 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred, data->res.sattr_res = true; } - if (!data->inode) - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, - 1); - else - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, - 0); + nfs4_init_sequence(server->nfs_client, &data->args.seq_args, + &data->res.seq_res, 1, !data->inode ? 1 : 0); task_setup_data.callback_data = data; msg.rpc_argp = &data->args; @@ -7206,6 +7214,7 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, struct nfs_seqid *seqid) { struct nfs4_unlockdata *data; + struct nfs_client *clp = NFS_SERVER(lsp->ls_state->inode)->nfs_client; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU], .rpc_cred = ctx->cred, @@ -7221,8 +7230,8 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, if (nfs_server_capable(lsp->ls_state->inode, NFS_CAP_MOVEABLE)) task_setup_data.flags |= RPC_TASK_MOVEABLE; - nfs4_state_protect(NFS_SERVER(lsp->ls_state->inode)->nfs_client, - NFS_SP4_MACH_CRED_CLEANUP, &task_setup_data.rpc_client, &msg); + nfs4_state_protect(clp, NFS_SP4_MACH_CRED_CLEANUP, + &task_setup_data.rpc_client, &msg); /* Ensure this is an unlock - when canceling a lock, the * canceled lock is passed in, and it won't be an unlock. @@ -7237,7 +7246,7 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, return ERR_PTR(-ENOMEM); } - nfs4_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1, 0); + nfs4_init_sequence(clp, &data->arg.seq_args, &data->res.seq_res, 1, 0); msg.rpc_argp = &data->arg; msg.rpc_resp = &data->res; task_setup_data.callback_data = data; @@ -7487,6 +7496,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f { struct nfs4_lockdata *data; struct rpc_task *task; + struct nfs_client *clp = NFS_SERVER(state->inode)->nfs_client; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK], .rpc_cred = state->owner->so_cred, @@ -7510,7 +7520,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f return -ENOMEM; if (IS_SETLKW(cmd)) data->arg.block = 1; - nfs4_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1, + nfs4_init_sequence(clp, &data->arg.seq_args, &data->res.seq_res, 1, recovery_type > NFS_LOCK_NEW); msg.rpc_argp = &data->arg; msg.rpc_resp = &data->res; @@ -8211,6 +8221,7 @@ static int _nfs41_proc_get_locations(struct nfs_server *server, struct page *page, const struct cred *cred) { struct rpc_clnt *clnt = server->client; + struct nfs_client *clp = server->nfs_client; u32 bitmask[2] = { [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, }; @@ -8238,7 +8249,7 @@ static int _nfs41_proc_get_locations(struct nfs_server *server, struct rpc_task_setup task_setup_data = { .rpc_client = clnt, .rpc_message = &msg, - .callback_ops = server->nfs_client->cl_mvops->call_sync_ops, + .callback_ops = clp->cl_mvops->call_sync_ops, .callback_data = &data, .flags = RPC_TASK_NO_ROUND_ROBIN, }; @@ -8248,7 +8259,7 @@ static int _nfs41_proc_get_locations(struct nfs_server *server, locations->server = server; locations->nlocations = 0; - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); + nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 1); status = nfs4_call_sync_custom(&task_setup_data); if (status == NFS4_OK && res.seq_res.sr_status_flags & SEQ4_STATUS_LEASE_MOVED) @@ -8335,7 +8346,7 @@ static int _nfs41_proc_fsid_present(struct inode *inode, const struct cred *cred if (res.fh == NULL) return -ENOMEM; - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); + nfs4_init_sequence(server->nfs_client, &args.seq_args, &res.seq_res, 0, 1); status = nfs4_call_sync_sequence(clnt, server, &msg, &args.seq_args, &res.seq_res); nfs_free_fhandle(res.fh); @@ -8436,7 +8447,7 @@ static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct dprintk("NFS call secinfo %s\n", name->name); nfs4_state_protect(clp, NFS_SP4_MACH_CRED_SECINFO, &clnt, &msg); - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0); + nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 0); status = nfs4_call_sync_custom(&task_setup); dprintk("NFS reply secinfo: %d\n", status); @@ -9162,7 +9173,7 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) .flags = RPC_TASK_TIMEOUT, }; - nfs4_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0, 1); + nfs4_init_sequence(clp, &args.la_seq_args, &res.lr_seq_res, 0, 1); return nfs4_call_sync_custom(&task_setup); } @@ -9501,7 +9512,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, calldata = kzalloc(sizeof(*calldata), GFP_KERNEL); if (calldata == NULL) goto out_put_clp; - nfs4_init_sequence(&calldata->args, &calldata->res, 0, is_privileged); + nfs4_init_sequence(clp, &calldata->args, &calldata->res, 0, is_privileged); nfs4_sequence_attach_slot(&calldata->args, &calldata->res, slot); msg.rpc_argp = &calldata->args; msg.rpc_resp = &calldata->res; @@ -9651,7 +9662,7 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp, calldata->clp = clp; calldata->arg.one_fs = 0; - nfs4_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 0, 1); + nfs4_init_sequence(clp, &calldata->arg.seq_args, &calldata->res.seq_res, 0, 1); msg.rpc_argp = &calldata->arg; msg.rpc_resp = &calldata->res; task_setup_data.callback_data = calldata; @@ -9815,7 +9826,8 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, struct pnfs_layout_segment *lseg = NULL; int status = 0; - nfs4_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0, 0); + nfs4_init_sequence(server->nfs_client, &lgp->args.seq_args, + &lgp->res.seq_res, 0, 0); exception->retry = 0; task = rpc_run_task(&task_setup_data); @@ -9950,6 +9962,7 @@ static const struct rpc_call_ops nfs4_layoutreturn_call_ops = { int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, unsigned int flags) { + struct nfs_client *clp = NFS_SERVER(lrp->args.inode)->nfs_client; struct rpc_task *task; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN], @@ -9966,9 +9979,8 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, unsigned int flags) }; int status = 0; - nfs4_state_protect(NFS_SERVER(lrp->args.inode)->nfs_client, - NFS_SP4_MACH_CRED_PNFS_CLEANUP, - &task_setup_data.rpc_client, &msg); + nfs4_state_protect(clp, NFS_SP4_MACH_CRED_PNFS_CLEANUP, + &task_setup_data.rpc_client, &msg); lrp->inode = nfs_igrab_and_active(lrp->args.inode); if (flags & PNFS_FL_LAYOUTRETURN_ASYNC) { @@ -9980,12 +9992,9 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, unsigned int flags) } if (!lrp->inode) flags |= PNFS_FL_LAYOUTRETURN_PRIVILEGED; - if (flags & PNFS_FL_LAYOUTRETURN_PRIVILEGED) - nfs4_init_sequence(&lrp->args.seq_args, &lrp->res.seq_res, 1, - 1); - else - nfs4_init_sequence(&lrp->args.seq_args, &lrp->res.seq_res, 1, - 0); + + nfs4_init_sequence(clp, &lrp->args.seq_args, &lrp->res.seq_res, 1, + flags & PNFS_FL_LAYOUTRETURN_PRIVILEGED ? 1 : 0); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); @@ -10135,7 +10144,8 @@ nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync) } task_setup_data.flags = RPC_TASK_ASYNC; } - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0); + nfs4_init_sequence(NFS_SERVER(data->args.inode)->nfs_client, + &data->args.seq_args, &data->res.seq_res, 1, 0); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); @@ -10156,6 +10166,7 @@ static int _nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs4_secinfo_flavors *flavors, bool use_integrity) { + struct nfs_client *clp = server->nfs_client; struct nfs41_secinfo_no_name_args args = { .style = SECINFO_STYLE_CURRENT_FH, }; @@ -10175,7 +10186,7 @@ static int _nfs41_proc_secinfo_no_name(struct nfs_server *server, struct rpc_task_setup task_setup = { .rpc_client = server->client, .rpc_message = &msg, - .callback_ops = server->nfs_client->cl_mvops->call_sync_ops, + .callback_ops = clp->cl_mvops->call_sync_ops, .callback_data = &data, .flags = RPC_TASK_NO_ROUND_ROBIN, }; @@ -10183,13 +10194,13 @@ static int _nfs41_proc_secinfo_no_name(struct nfs_server *server, int status; if (use_integrity) { - task_setup.rpc_client = server->nfs_client->cl_rpcclient; + task_setup.rpc_client = clp->cl_rpcclient; - cred = nfs4_get_clid_cred(server->nfs_client); + cred = nfs4_get_clid_cred(clp); msg.rpc_cred = cred; } - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0); + nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 0); status = nfs4_call_sync_custom(&task_setup); dprintk("<-- %s status=%d\n", __func__, status); @@ -10321,12 +10332,12 @@ static int _nfs41_test_stateid(struct nfs_server *server, .rpc_cred = cred, }; struct rpc_clnt *rpc_client = server->client; + struct nfs_client *clp = server->nfs_client; - nfs4_state_protect(server->nfs_client, NFS_SP4_MACH_CRED_STATEID, - &rpc_client, &msg); + nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID, &rpc_client, &msg); dprintk("NFS call test_stateid %p\n", stateid); - nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1); + nfs4_init_sequence(clp, &args.seq_args, &res.seq_res, 0, 1); status = nfs4_call_sync_sequence(rpc_client, server, &msg, &args.seq_args, &res.seq_res); if (status != NFS_OK) { @@ -10456,7 +10467,7 @@ static int nfs41_free_stateid(struct nfs_server *server, if (!refcount_inc_not_zero(&clp->cl_count)) return -EIO; - nfs4_state_protect(server->nfs_client, NFS_SP4_MACH_CRED_STATEID, + nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID, &task_setup.rpc_client, &msg); dprintk("NFS call free_stateid %p\n", stateid); @@ -10470,7 +10481,8 @@ static int nfs41_free_stateid(struct nfs_server *server, msg.rpc_argp = &data->args; msg.rpc_resp = &data->res; - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, privileged); + nfs4_init_sequence(clp, &data->args.seq_args, &data->res.seq_res, 1, + privileged); task = rpc_run_task(&task_setup); if (IS_ERR(task)) return PTR_ERR(task); From 9c54afc10611638cca9997f472283985b9ca8e42 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 20 Nov 2025 15:14:30 -0500 Subject: [PATCH 42/60] NFS: Move sequence slot operations into minorversion operations At the same time, I move the NFS v4.0 functions into nfs40proc.c to keep v4.0 features together in their own files. Signed-off-by: Anna Schumaker --- fs/nfs/nfs40proc.c | 30 ++++++++++++ fs/nfs/nfs4_fs.h | 8 +++- fs/nfs/nfs4proc.c | 103 +++++++++++++--------------------------- include/linux/nfs_xdr.h | 1 + 4 files changed, 70 insertions(+), 72 deletions(-) diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 9b5b57170471..32a2a1a2b216 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -6,6 +6,7 @@ #include "internal.h" #include "nfs4_fs.h" #include "nfs40.h" +#include "nfs4session.h" #include "nfs4trace.h" static void nfs40_call_sync_prepare(struct rpc_task *task, void *calldata) @@ -21,6 +22,28 @@ static void nfs40_call_sync_done(struct rpc_task *task, void *calldata) nfs4_sequence_done(task, data->seq_res); } +static void nfs40_sequence_free_slot(struct nfs4_sequence_res *res) +{ + struct nfs4_slot *slot = res->sr_slot; + struct nfs4_slot_table *tbl; + + tbl = slot->table; + spin_lock(&tbl->slot_tbl_lock); + if (!nfs41_wake_and_assign_slot(tbl, slot)) + nfs4_free_slot(tbl, slot); + spin_unlock(&tbl->slot_tbl_lock); + + res->sr_slot = NULL; +} + +static int nfs40_sequence_done(struct rpc_task *task, + struct nfs4_sequence_res *res) +{ + if (res->sr_slot != NULL) + nfs40_sequence_free_slot(res); + return 1; +} + static void nfs40_clear_delegation_stateid(struct nfs4_state *state) { if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL) @@ -317,6 +340,12 @@ static const struct rpc_call_ops nfs40_call_sync_ops = { .rpc_call_done = nfs40_call_sync_done, }; +static const struct nfs4_sequence_slot_ops nfs40_sequence_slot_ops = { + .process = nfs40_sequence_done, + .done = nfs40_sequence_done, + .free_slot = nfs40_sequence_free_slot, +}; + static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, @@ -358,6 +387,7 @@ const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { .test_and_free_expired = nfs40_test_and_free_expired_stateid, .alloc_seqid = nfs_alloc_seqid, .call_sync_ops = &nfs40_call_sync_ops, + .sequence_slot_ops = &nfs40_sequence_slot_ops, .reboot_recovery_ops = &nfs40_reboot_recovery_ops, .nograce_recovery_ops = &nfs40_nograce_recovery_ops, .state_renewal_ops = &nfs40_state_renewal_ops, diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 34cdbf42b865..a5dd4a837769 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -73,6 +73,7 @@ struct nfs4_minor_version_ops { void (*session_trunk)(struct rpc_clnt *clnt, struct rpc_xprt *xprt, void *data); const struct rpc_call_ops *call_sync_ops; + const struct nfs4_sequence_slot_ops *sequence_slot_ops; const struct nfs4_state_recovery_ops *reboot_recovery_ops; const struct nfs4_state_recovery_ops *nograce_recovery_ops; const struct nfs4_state_maintenance_ops *state_renewal_ops; @@ -256,6 +257,12 @@ struct nfs4_add_xprt_data { const struct cred *cred; }; +struct nfs4_sequence_slot_ops { + int (*process)(struct rpc_task *, struct nfs4_sequence_res *); + int (*done)(struct rpc_task *, struct nfs4_sequence_res *); + void (*free_slot)(struct nfs4_sequence_res *); +}; + struct nfs4_state_maintenance_ops { int (*sched_state_renewal)(struct nfs_client *, const struct cred *, unsigned); const struct cred * (*get_state_renewal_cred)(struct nfs_client *); @@ -311,7 +318,6 @@ extern int nfs4_call_sync_sequence(struct rpc_clnt *clnt, struct nfs4_sequence_res *res); extern void nfs4_init_sequence(struct nfs_client *clp, struct nfs4_sequence_args *, struct nfs4_sequence_res *, int, int); -extern int nfs40_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res); extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, const struct cred *, struct nfs4_setclientid_res *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, const struct cred *); extern void renew_lease(const struct nfs_server *server, unsigned long timestamp); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 574ebefcb4fa..c621294e3f0f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -780,28 +780,7 @@ void nfs4_init_sequence(struct nfs_client *clp, args->sa_privileged = privileged; res->sr_slot = NULL; -} - -static void nfs40_sequence_free_slot(struct nfs4_sequence_res *res) -{ - struct nfs4_slot *slot = res->sr_slot; - struct nfs4_slot_table *tbl; - - tbl = slot->table; - spin_lock(&tbl->slot_tbl_lock); - if (!nfs41_wake_and_assign_slot(tbl, slot)) - nfs4_free_slot(tbl, slot); - spin_unlock(&tbl->slot_tbl_lock); - - res->sr_slot = NULL; -} - -int nfs40_sequence_done(struct rpc_task *task, - struct nfs4_sequence_res *res) -{ - if (res->sr_slot != NULL) - nfs40_sequence_free_slot(res); - return 1; + res->sr_slot_ops = clp->cl_mvops->sequence_slot_ops; } #if defined(CONFIG_NFS_V4_1) @@ -1020,35 +999,6 @@ int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) } EXPORT_SYMBOL_GPL(nfs41_sequence_done); -static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res) -{ - if (res->sr_slot == NULL) - return 1; - if (res->sr_slot->table->session != NULL) - return nfs41_sequence_process(task, res); - return nfs40_sequence_done(task, res); -} - -static void nfs4_sequence_free_slot(struct nfs4_sequence_res *res) -{ - if (res->sr_slot != NULL) { - if (res->sr_slot->table->session != NULL) - nfs41_sequence_free_slot(res); - else - nfs40_sequence_free_slot(res); - } -} - -int nfs4_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) -{ - if (res->sr_slot == NULL) - return 1; - if (!res->sr_slot->table->session) - return nfs40_sequence_done(task, res); - return nfs41_sequence_done(task, res); -} -EXPORT_SYMBOL_GPL(nfs4_sequence_done); - static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) { struct nfs4_call_sync_data *data = calldata; @@ -1071,25 +1021,6 @@ static const struct rpc_call_ops nfs41_call_sync_ops = { .rpc_call_done = nfs41_call_sync_done, }; -#else /* !CONFIG_NFS_V4_1 */ - -static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res) -{ - return nfs40_sequence_done(task, res); -} - -static void nfs4_sequence_free_slot(struct nfs4_sequence_res *res) -{ - if (res->sr_slot != NULL) - nfs40_sequence_free_slot(res); -} - -int nfs4_sequence_done(struct rpc_task *task, - struct nfs4_sequence_res *res) -{ - return nfs40_sequence_done(task, res); -} -EXPORT_SYMBOL_GPL(nfs4_sequence_done); #endif /* !CONFIG_NFS_V4_1 */ @@ -1113,6 +1044,28 @@ void nfs4_sequence_attach_slot(struct nfs4_sequence_args *args, res->sr_slot = slot; } +static void nfs4_sequence_free_slot(struct nfs4_sequence_res *res) +{ + if (res->sr_slot != NULL) + res->sr_slot_ops->free_slot(res); +} + +static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res) +{ + if (res->sr_slot == NULL) + return 1; + return res->sr_slot_ops->process(task, res); +} + +int nfs4_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) +{ + if (res->sr_slot == NULL) + return 1; + return res->sr_slot_ops->done(task, res); +} +EXPORT_SYMBOL_GPL(nfs4_sequence_done); + + int nfs4_setup_sequence(struct nfs_client *client, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, @@ -2447,7 +2400,7 @@ static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata) { struct nfs4_opendata *data = calldata; - nfs40_sequence_done(task, &data->c_res.seq_res); + data->c_res.seq_res.sr_slot_ops->done(task, &data->c_res.seq_res); data->rpc_status = task->tk_status; if (data->rpc_status == 0) { @@ -10529,6 +10482,12 @@ bool nfs4_match_stateid(const nfs4_stateid *s1, #if defined(CONFIG_NFS_V4_1) +static const struct nfs4_sequence_slot_ops nfs41_sequence_slot_ops = { + .process = nfs41_sequence_process, + .done = nfs41_sequence_done, + .free_slot = nfs41_sequence_free_slot, +}; + static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, @@ -10583,6 +10542,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { .alloc_seqid = nfs_alloc_no_seqid, .session_trunk = nfs4_test_session_trunk, .call_sync_ops = &nfs41_call_sync_ops, + .sequence_slot_ops = &nfs41_sequence_slot_ops, .reboot_recovery_ops = &nfs41_reboot_recovery_ops, .nograce_recovery_ops = &nfs41_nograce_recovery_ops, .state_renewal_ops = &nfs41_state_renewal_ops, @@ -10619,6 +10579,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = { .find_root_sec = nfs41_find_root_sec, .free_lock_state = nfs41_free_lock_state, .call_sync_ops = &nfs41_call_sync_ops, + .sequence_slot_ops = &nfs41_sequence_slot_ops, .test_and_free_expired = nfs41_test_and_free_expired_stateid, .alloc_seqid = nfs_alloc_no_seqid, .session_trunk = nfs4_test_session_trunk, diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 79fe2dfb470f..2aa4e38af57a 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -209,6 +209,7 @@ struct nfs4_sequence_args { }; struct nfs4_sequence_res { + const struct nfs4_sequence_slot_ops *sr_slot_ops; struct nfs4_slot *sr_slot; /* slot used to send request */ unsigned long sr_timestamp; int sr_status; /* sequence operation status */ From 4e0269352534715915aae3fb84dd4d3bb3ff3738 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 21 Nov 2025 14:53:50 -0500 Subject: [PATCH 43/60] NFS: Add a way to disable NFS v4.0 via KConfig I introduce NFS4_MIN_MINOR_VERSION as a parallel to NFS4_MAX_MINOR_VERSION to check if NFS v4.0 has been compiled in and return an appropriate error if not. Signed-off-by: Anna Schumaker --- fs/nfs/Kconfig | 9 +++++++++ fs/nfs/Makefile | 3 ++- fs/nfs/fs_context.c | 3 ++- fs/nfs/nfs4_fs.h | 6 ++++++ fs/nfs/nfs4client.c | 3 ++- fs/nfs/nfs4proc.c | 2 ++ fs/nfs/nfs4state.c | 2 ++ fs/nfs/nfs4xdr.c | 24 ++++++++++++++++++++++-- 8 files changed, 47 insertions(+), 5 deletions(-) diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 07932ce9246c..058ed67b98cc 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -96,6 +96,15 @@ config NFS_SWAP help This option enables swapon to work on files located on NFS mounts. +config NFS_V4_0 + bool "NFS client support for NFSv4.0" + depends on NFS_V4 + help + This option enables support for minor version 0 of the NFSv4 protocol + (RFC 3530) in the kernel's NFS client. + + If unsure, say N. + config NFS_V4_1 bool "NFS client support for NFSv4.1" depends on NFS_V4 diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index d05e69c00fe1..6a9aaf2f913b 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -27,9 +27,10 @@ CFLAGS_nfs4trace.o += -I$(src) nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ delegation.o nfs4idmap.o callback.o callback_xdr.o callback_proc.o \ nfs4namespace.o nfs4getroot.o nfs4client.o nfs4session.o \ - dns_resolve.o nfs4trace.o nfs40proc.o nfs40client.o + dns_resolve.o nfs4trace.o nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o +nfsv4-$(CONFIG_NFS_V4_0) += nfs40client.o nfs40proc.o nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o pnfs_nfs.o nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o nfs42xattr.o diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c index b4679b7161b0..86750f110053 100644 --- a/fs/nfs/fs_context.c +++ b/fs/nfs/fs_context.c @@ -806,7 +806,8 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, ctx->mount_server.version = result.uint_32; break; case Opt_minorversion: - if (result.uint_32 > NFS4_MAX_MINOR_VERSION) + if (result.uint_32 < NFS4_MIN_MINOR_VERSION || + result.uint_32 > NFS4_MAX_MINOR_VERSION) goto out_of_bounds; ctx->minorversion = result.uint_32; break; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index a5dd4a837769..5a6728acb589 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -10,6 +10,12 @@ #ifndef __LINUX_FS_NFS_NFS4_FS_H #define __LINUX_FS_NFS_NFS4_FS_H +#if defined(CONFIG_NFS_V4_0) +#define NFS4_MIN_MINOR_VERSION 0 +#else +#define NFS4_MIN_MINOR_VERSION 1 +#endif + #if defined(CONFIG_NFS_V4_2) #define NFS4_MAX_MINOR_VERSION 2 #elif defined(CONFIG_NFS_V4_1) diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index c376b2420b6c..00b57e55aba8 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -203,7 +203,8 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) if (err) goto error; - if (cl_init->minorversion > NFS4_MAX_MINOR_VERSION) { + if (cl_init->minorversion < NFS4_MIN_MINOR_VERSION || + cl_init->minorversion > NFS4_MAX_MINOR_VERSION) { err = -EINVAL; goto error; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index c621294e3f0f..3ee5394aca72 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -10591,7 +10591,9 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = { #endif const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { +#if defined(CONFIG_NFS_V4_0) [0] = &nfs_v4_0_minor_ops, +#endif /* CONFIG_NFS_V4_0 */ #if defined(CONFIG_NFS_V4_1) [1] = &nfs_v4_1_minor_ops, #endif diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index f3d441bb28af..3330a188e1a5 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1804,9 +1804,11 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) switch (error) { case 0: break; +#if IS_ENABLED(CONFIG_NFS_V4_0) case -NFS4ERR_CB_PATH_DOWN: nfs40_handle_cb_pathdown(clp); break; +#endif /* CONFIG_NFS_V4_0 */ case -NFS4ERR_NO_GRACE: nfs4_state_end_reclaim_reboot(clp); break; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index b6fe30577fab..85ddcee51162 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1399,11 +1399,13 @@ static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *ar xdr_encode_hyper(p, nfs4_lock_length(args->fl)); } +#if defined(CONFIG_NFS_V4_0) static void encode_release_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lowner, struct compound_hdr *hdr) { encode_op_hdr(xdr, OP_RELEASE_LOCKOWNER, decode_release_lockowner_maxsz, hdr); encode_lockowner(xdr, lowner); } +#endif /* CONFIG_NFS_V4_0 */ static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) { @@ -2583,6 +2585,7 @@ static void nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr, encode_nops(&hdr); } +#if defined(CONFIG_NFS_V4_0) static void nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) @@ -2596,6 +2599,7 @@ static void nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req, encode_release_lockowner(xdr, &args->lock_owner, &hdr); encode_nops(&hdr); } +#endif /* CONFIG_NFS_V4_0 */ /* * Encode a READLINK request @@ -2825,6 +2829,7 @@ static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req, /* * a RENEW request */ +#if defined(CONFIG_NFS_V4_0) static void nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) @@ -2838,6 +2843,7 @@ static void nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr, encode_renew(xdr, clp->cl_clientid, &hdr); encode_nops(&hdr); } +#endif /* CONFIG_NFS_V4_0 */ /* * a SETCLIENTID request @@ -5224,10 +5230,12 @@ static int decode_locku(struct xdr_stream *xdr, struct nfs_locku_res *res) return status; } +#if defined(CONFIG_NFS_V4_0) static int decode_release_lockowner(struct xdr_stream *xdr) { return decode_op_hdr(xdr, OP_RELEASE_LOCKOWNER); } +#endif /* CONFIG_NFS_V4_0 */ static int decode_lookup(struct xdr_stream *xdr) { @@ -6930,6 +6938,7 @@ static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, struct xdr_stream *xdr, return status; } +#if defined(CONFIG_NFS_V4_0) static int nfs4_xdr_dec_release_lockowner(struct rpc_rqst *rqstp, struct xdr_stream *xdr, void *dummy) { @@ -6941,6 +6950,7 @@ static int nfs4_xdr_dec_release_lockowner(struct rpc_rqst *rqstp, status = decode_release_lockowner(xdr); return status; } +#endif /* CONFIG_NFS_V4_0 */ /* * Decode READLINK response @@ -7162,6 +7172,7 @@ static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req, /* * Decode RENEW response */ +#if defined(CONFIG_NFS_V4_0) static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, struct xdr_stream *xdr, void *__unused) { @@ -7173,6 +7184,7 @@ static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, struct xdr_stream *xdr, status = decode_renew(xdr); return status; } +#endif /* CONFIG_NFS_V4_0 */ /* * Decode SETCLIENTID response @@ -7754,6 +7766,14 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, .p_name = #proc, \ } +#if defined(CONFIG_NFS_V4_0) +#define PROC40(proc, argtype, restype) \ + PROC(proc, argtype, restype) +#else +#define PROC40(proc, argtype, restype) \ + STUB(proc) +#endif /* CONFIG_NFS_V4_0 */ + #if defined(CONFIG_NFS_V4_1) #define PROC41(proc, argtype, restype) \ PROC(proc, argtype, restype) @@ -7781,7 +7801,7 @@ const struct rpc_procinfo nfs4_procedures[] = { PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), PROC(FSINFO, enc_fsinfo, dec_fsinfo), - PROC(RENEW, enc_renew, dec_renew), + PROC40(RENEW, enc_renew, dec_renew), PROC(SETCLIENTID, enc_setclientid, dec_setclientid), PROC(SETCLIENTID_CONFIRM, enc_setclientid_confirm, dec_setclientid_confirm), PROC(LOCK, enc_lock, dec_lock), @@ -7805,7 +7825,7 @@ const struct rpc_procinfo nfs4_procedures[] = { PROC(GETACL, enc_getacl, dec_getacl), PROC(SETACL, enc_setacl, dec_setacl), PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations), - PROC(RELEASE_LOCKOWNER, enc_release_lockowner, dec_release_lockowner), + PROC40(RELEASE_LOCKOWNER, enc_release_lockowner, dec_release_lockowner), PROC(SECINFO, enc_secinfo, dec_secinfo), PROC(FSID_PRESENT, enc_fsid_present, dec_fsid_present), PROC41(EXCHANGE_ID, enc_exchange_id, dec_exchange_id), From 7537db24806fdc3d3ec4fef53babdc22c9219e75 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 4 Dec 2025 15:44:33 -0500 Subject: [PATCH 44/60] NFS: Merge CONFIG_NFS_V4_1 with CONFIG_NFS_V4 Compiling the NFSv4 module without any minorversion support doesn't make much sense, so this patch sets NFS v4.1 as the default, always enabled NFS version allowing us to replace all the CONFIG_NFS_V4_1s scattered throughout the code with CONFIG_NFS_V4. Signed-off-by: Anna Schumaker --- fs/nfs/Kconfig | 27 ++++-------- fs/nfs/Makefile | 3 +- fs/nfs/callback.c | 13 +----- fs/nfs/callback.h | 3 -- fs/nfs/callback_proc.c | 3 -- fs/nfs/callback_xdr.c | 21 ---------- fs/nfs/client.c | 8 ++-- fs/nfs/internal.h | 12 ++---- fs/nfs/netns.h | 4 +- fs/nfs/nfs4_fs.h | 36 +--------------- fs/nfs/nfs4client.c | 23 ----------- fs/nfs/nfs4proc.c | 55 ------------------------- fs/nfs/nfs4session.c | 4 -- fs/nfs/nfs4session.h | 23 ----------- fs/nfs/nfs4state.c | 17 -------- fs/nfs/nfs4trace.c | 2 - fs/nfs/nfs4trace.h | 16 ------- fs/nfs/nfs4xdr.c | 87 --------------------------------------- fs/nfs/pnfs.h | 6 +-- fs/nfs/read.c | 4 +- fs/nfs/super.c | 16 ++----- fs/nfs/sysfs.c | 10 ++--- fs/nfs/write.c | 2 +- include/linux/nfs_fs_sb.h | 2 - include/linux/nfs_xdr.h | 6 +-- 25 files changed, 35 insertions(+), 368 deletions(-) diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 058ed67b98cc..12cb0ca738af 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -78,9 +78,10 @@ config NFS_V4 tristate "NFS client support for NFS version 4" depends on NFS_FS select KEYS + select SUNRPC_BACKCHANNEL help - This option enables support for version 4 of the NFS protocol - (RFC 3530) in the kernel's NFS client. + This option enables support for version 4.1 of the NFS protocol + (RFC 5661) in the kernel's NFS client. To mount NFS servers using NFSv4, you also need to install user space programs which can be found in the Linux nfs-utils package, @@ -105,19 +106,9 @@ config NFS_V4_0 If unsure, say N. -config NFS_V4_1 - bool "NFS client support for NFSv4.1" - depends on NFS_V4 - select SUNRPC_BACKCHANNEL - help - This option enables support for minor version 1 of the NFSv4 protocol - (RFC 5661) in the kernel's NFS client. - - If unsure, say N. - config NFS_V4_2 bool "NFS client support for NFSv4.2" - depends on NFS_V4_1 + depends on NFS_V4 help This option enables support for minor version 2 of the NFSv4 protocol in the kernel's NFS client. @@ -126,22 +117,22 @@ config NFS_V4_2 config PNFS_FILE_LAYOUT tristate - depends on NFS_V4_1 + depends on NFS_V4 default NFS_V4 config PNFS_BLOCK tristate - depends on NFS_V4_1 && BLK_DEV_DM + depends on NFS_V4 && BLK_DEV_DM default NFS_V4 config PNFS_FLEXFILE_LAYOUT tristate - depends on NFS_V4_1 + depends on NFS_V4 default NFS_V4 config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN string "NFSv4.1 Implementation ID Domain" - depends on NFS_V4_1 + depends on NFS_V4 default "kernel.org" help This option defines the domain portion of the implementation ID that @@ -153,7 +144,7 @@ config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN config NFS_V4_1_MIGRATION bool "NFSv4.1 client support for migration" - depends on NFS_V4_1 + depends on NFS_V4 default n help This option makes the NFS client advertise to NFSv4.1 servers that diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 6a9aaf2f913b..c895521f27f3 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -27,11 +27,10 @@ CFLAGS_nfs4trace.o += -I$(src) nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ delegation.o nfs4idmap.o callback.o callback_xdr.o callback_proc.o \ nfs4namespace.o nfs4getroot.o nfs4client.o nfs4session.o \ - dns_resolve.o nfs4trace.o + dns_resolve.o nfs4trace.o pnfs.o pnfs_dev.o pnfs_nfs.o nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o nfsv4-$(CONFIG_NFS_V4_0) += nfs40client.o nfs40proc.o -nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o pnfs_nfs.o nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o nfs42xattr.o obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/ diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index fabda0f6ec1a..6af67bdf0e40 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -87,7 +87,6 @@ nfs4_callback_svc(void *vrqstp) return 0; } -#if defined(CONFIG_NFS_V4_1) static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, struct svc_serv *serv) { @@ -98,12 +97,6 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, */ xprt->bc_serv = serv; } -#else -static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, - struct svc_serv *serv) -{ -} -#endif /* CONFIG_NFS_V4_1 */ static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt, struct svc_serv *serv) @@ -157,7 +150,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, } ret = 0; - if (!IS_ENABLED(CONFIG_NFS_V4_1) || minorversion == 0) + if (minorversion == 0) ret = nfs4_callback_up_net(serv, net); else if (xprt->ops->bc_setup) set_bc_enabled(serv); @@ -198,10 +191,6 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion) cb_info->users); threadfn = nfs4_callback_svc; -#if !defined(CONFIG_NFS_V4_1) - if (minorversion) - return ERR_PTR(-ENOTSUPP); -#endif serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, threadfn); if (!serv) { diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 8809f93d82c0..2a721c422d48 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -65,8 +65,6 @@ struct cb_recallargs { uint32_t truncate; }; -#if defined(CONFIG_NFS_V4_1) - struct referring_call { uint32_t rc_sequenceid; uint32_t rc_slotid; @@ -168,7 +166,6 @@ struct cb_notify_lock_args { extern __be32 nfs4_callback_notify_lock(void *argp, void *resp, struct cb_process_state *cps); -#endif /* CONFIG_NFS_V4_1 */ #ifdef CONFIG_NFS_V4_2 struct cb_offloadargs { struct nfs_fh coa_fh; diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 57550020c819..805eb3764186 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -126,8 +126,6 @@ __be32 nfs4_callback_recall(void *argp, void *resp, return res; } -#if defined(CONFIG_NFS_V4_1) - /* * Lookup a layout inode by stateid * @@ -698,7 +696,6 @@ __be32 nfs4_callback_notify_lock(void *argp, void *resp, return htonl(NFS4_OK); } -#endif /* CONFIG_NFS_V4_1 */ #ifdef CONFIG_NFS_V4_2 static void nfs4_copy_cb_args(struct nfs4_copy_state *cp_state, struct cb_offloadargs *args) diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 4254ba3ee7c5..c2fa4a91db26 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -30,7 +30,6 @@ (2 + 2 + 3 + 3 + 3 + 3 + 3) * 4) #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) -#if defined(CONFIG_NFS_V4_1) #define CB_OP_LAYOUTRECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #define CB_OP_DEVICENOTIFY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ @@ -39,7 +38,6 @@ #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #define CB_OP_RECALLSLOT_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #define CB_OP_NOTIFY_LOCK_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) -#endif /* CONFIG_NFS_V4_1 */ #ifdef CONFIG_NFS_V4_2 #define CB_OP_OFFLOAD_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) #endif /* CONFIG_NFS_V4_2 */ @@ -205,7 +203,6 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, return decode_fh(xdr, &args->fh); } -#if defined(CONFIG_NFS_V4_1) static __be32 decode_layout_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) { stateid->type = NFS4_LAYOUT_STATEID_TYPE; @@ -521,7 +518,6 @@ static __be32 decode_notify_lock_args(struct svc_rqst *rqstp, return decode_lockowner(xdr, args); } -#endif /* CONFIG_NFS_V4_1 */ #ifdef CONFIG_NFS_V4_2 static __be32 decode_write_response(struct xdr_stream *xdr, struct cb_offloadargs *args) @@ -747,8 +743,6 @@ static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, return status; } -#if defined(CONFIG_NFS_V4_1) - static __be32 encode_sessionid(struct xdr_stream *xdr, const struct nfs4_sessionid *sid) { @@ -846,19 +840,6 @@ static void nfs4_cb_free_slot(struct cb_process_state *cps) } } -#else /* CONFIG_NFS_V4_1 */ - -static __be32 -preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) -{ - return htonl(NFS4ERR_MINOR_VERS_MISMATCH); -} - -static void nfs4_cb_free_slot(struct cb_process_state *cps) -{ -} -#endif /* CONFIG_NFS_V4_1 */ - #ifdef CONFIG_NFS_V4_2 static __be32 preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op) @@ -1051,7 +1032,6 @@ static struct callback_op callback_ops[] = { .decode_args = decode_recall_args, .res_maxsize = CB_OP_RECALL_RES_MAXSZ, }, -#if defined(CONFIG_NFS_V4_1) [OP_CB_LAYOUTRECALL] = { .process_op = nfs4_callback_layoutrecall, .decode_args = decode_layoutrecall_args, @@ -1083,7 +1063,6 @@ static struct callback_op callback_ops[] = { .decode_args = decode_notify_lock_args, .res_maxsize = CB_OP_NOTIFY_LOCK_RES_MAXSZ, }, -#endif /* CONFIG_NFS_V4_1 */ #ifdef CONFIG_NFS_V4_2 [OP_CB_OFFLOAD] = { .process_op = nfs4_callback_offload, diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 62aece00f810..6b9a65615a51 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1266,11 +1266,9 @@ void nfs_clients_init(struct net *net) INIT_LIST_HEAD(&nn->nfs_volume_list); #if IS_ENABLED(CONFIG_NFS_V4) idr_init(&nn->cb_ident_idr); -#endif -#if IS_ENABLED(CONFIG_NFS_V4_1) INIT_LIST_HEAD(&nn->nfs4_data_server_cache); spin_lock_init(&nn->nfs4_data_server_lock); -#endif +#endif /* CONFIG_NFS_V4 */ spin_lock_init(&nn->nfs_client_lock); nn->boot_time = ktime_get_real(); memset(&nn->rpcstats, 0, sizeof(nn->rpcstats)); @@ -1287,9 +1285,9 @@ void nfs_clients_exit(struct net *net) nfs_cleanup_cb_ident_idr(net); WARN_ON_ONCE(!list_empty(&nn->nfs_client_list)); WARN_ON_ONCE(!list_empty(&nn->nfs_volume_list)); -#if IS_ENABLED(CONFIG_NFS_V4_1) +#if IS_ENABLED(CONFIG_NFS_V4) WARN_ON_ONCE(!list_empty(&nn->nfs4_data_server_cache)); -#endif +#endif /* CONFIG_NFS_V4 */ } #ifdef CONFIG_PROC_FS diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index e99998e515c0..63e09dfc27a8 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -334,17 +334,13 @@ extern int nfs3_decode_dirent(struct xdr_stream *, #if IS_ENABLED(CONFIG_NFS_V4) extern int nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, bool); -#endif -#ifdef CONFIG_NFS_V4_1 extern const u32 nfs41_maxread_overhead; extern const u32 nfs41_maxwrite_overhead; extern const u32 nfs41_maxgetdevinfo_overhead; -#endif /* nfs4proc.c */ -#if IS_ENABLED(CONFIG_NFS_V4) extern const struct rpc_procinfo nfs4_procedures[]; -#endif +#endif /* CONFIG_NFS_V4 */ #ifdef CONFIG_NFS_V4_SECURITY_LABEL extern struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags); @@ -639,7 +635,7 @@ void nfs_pageio_stop_mirroring(struct nfs_pageio_descriptor *pgio); int nfs_filemap_write_and_wait_range(struct address_space *mapping, loff_t lstart, loff_t lend); -#ifdef CONFIG_NFS_V4_1 +#ifdef CONFIG_NFS_V4 static inline void pnfs_bucket_clear_pnfs_ds_commit_verifiers(struct pnfs_commit_bucket *buckets, unsigned int nbuckets) @@ -660,12 +656,12 @@ void nfs_clear_pnfs_ds_commit_verifiers(struct pnfs_ds_commit_info *cinfo) array->nbuckets); rcu_read_unlock(); } -#else +#else /* CONFIG_NFS_V4 */ static inline void nfs_clear_pnfs_ds_commit_verifiers(struct pnfs_ds_commit_info *cinfo) { } -#endif +#endif /* CONFIG_NFS_V4 */ #ifdef CONFIG_MIGRATION int nfs_migrate_folio(struct address_space *, struct folio *dst, diff --git a/fs/nfs/netns.h b/fs/nfs/netns.h index 6ba3ea39e928..36658579100d 100644 --- a/fs/nfs/netns.h +++ b/fs/nfs/netns.h @@ -31,11 +31,9 @@ struct nfs_net { unsigned short nfs_callback_tcpport; unsigned short nfs_callback_tcpport6; int cb_users[NFS4_MAX_MINOR_VERSION + 1]; -#endif /* CONFIG_NFS_V4 */ -#if IS_ENABLED(CONFIG_NFS_V4_1) struct list_head nfs4_data_server_cache; spinlock_t nfs4_data_server_lock; -#endif /* CONFIG_NFS_V4_1 */ +#endif /* CONFIG_NFS_V4 */ struct nfs_netns_client *nfs_client; spinlock_t nfs_client_lock; ktime_t boot_time; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 5a6728acb589..783df6901b84 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -18,10 +18,8 @@ #if defined(CONFIG_NFS_V4_2) #define NFS4_MAX_MINOR_VERSION 2 -#elif defined(CONFIG_NFS_V4_1) -#define NFS4_MAX_MINOR_VERSION 1 #else -#define NFS4_MAX_MINOR_VERSION 0 +#define NFS4_MAX_MINOR_VERSION 1 #endif #if IS_ENABLED(CONFIG_NFS_V4) @@ -383,7 +381,6 @@ extern bool nfs4_match_stateid(const nfs4_stateid *s1, const nfs4_stateid *s2); extern int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr); -#if defined(CONFIG_NFS_V4_1) extern int nfs41_sequence_done(struct rpc_task *, struct nfs4_sequence_res *); extern int nfs4_proc_create_session(struct nfs_client *, const struct cred *); extern int nfs4_proc_destroy_session(struct nfs4_session *, const struct cred *); @@ -461,31 +458,6 @@ nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp, !test_bit(NFS_SP4_MACH_CRED_COMMIT, &clp->cl_sp4_flags)) hdr->args.stable = NFS_FILE_SYNC; } -#else /* CONFIG_NFS_v4_1 */ -static inline bool -is_ds_only_client(struct nfs_client *clp) -{ - return false; -} - -static inline bool -is_ds_client(struct nfs_client *clp) -{ - return false; -} - -static inline void -nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_flags, - struct rpc_clnt **clntp, struct rpc_message *msg) -{ -} - -static inline void -nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp, - struct rpc_message *msg, struct nfs_pgio_header *hdr) -{ -} -#endif /* CONFIG_NFS_V4_1 */ extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; @@ -518,18 +490,12 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, struct nfs_client **); int nfs40_discover_server_trunking(struct nfs_client *clp, struct nfs_client **, const struct cred *); -#if defined(CONFIG_NFS_V4_1) int nfs41_discover_server_trunking(struct nfs_client *clp, struct nfs_client **, const struct cred *); extern void nfs4_schedule_session_recovery(struct nfs4_session *, int); extern void nfs41_notify_server(struct nfs_client *); bool nfs4_check_serverowner_major_id(struct nfs41_server_owner *o1, struct nfs41_server_owner *o2); -#else -static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) -{ -} -#endif /* CONFIG_NFS_V4_1 */ extern struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *, const struct cred *, gfp_t); extern void nfs4_put_state_owner(struct nfs4_state_owner *); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 00b57e55aba8..51cf4a37d652 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -44,7 +44,6 @@ static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion) return ret < 0 ? ret : 0; } -#ifdef CONFIG_NFS_V4_1 /* * Per auth flavor data server rpc clients */ @@ -187,7 +186,6 @@ void nfs41_shutdown_client(struct nfs_client *clp) } } -#endif /* CONFIG_NFS_V4_1 */ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) { @@ -217,9 +215,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; clp->cl_mig_gen = 1; clp->cl_last_renewal = jiffies; -#if IS_ENABLED(CONFIG_NFS_V4_1) init_waitqueue_head(&clp->cl_lock_waitq); -#endif INIT_LIST_HEAD(&clp->pending_cb_stateids); if (cl_init->minorversion != 0) @@ -332,8 +328,6 @@ static int nfs4_init_callback(struct nfs_client *clp) return 0; } -#if defined(CONFIG_NFS_V4_1) - /** * nfs41_init_client - nfs_client initialization tasks for NFSv4.1+ * @clp: nfs_client to initialize @@ -365,8 +359,6 @@ int nfs41_init_client(struct nfs_client *clp) return 0; } -#endif /* CONFIG_NFS_V4_1 */ - /* * Initialize the minor version specific parts of an NFS4 client record */ @@ -508,7 +500,6 @@ int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, return 0; } -#ifdef CONFIG_NFS_V4_1 /* * Returns true if the server major ids match */ @@ -637,7 +628,6 @@ int nfs41_walk_client_list(struct nfs_client *new, nfs_put_client(prev); return status; } -#endif /* CONFIG_NFS_V4_1 */ static void nfs4_destroy_server(struct nfs_server *server) { @@ -669,7 +659,6 @@ nfs4_find_client_ident(struct net *net, int cb_ident) return clp; } -#if defined(CONFIG_NFS_V4_1) /* Common match routine for v4.0 and v4.1 callback services */ static bool nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp, u32 minorversion) @@ -727,16 +716,6 @@ nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, return NULL; } -#else /* CONFIG_NFS_V4_1 */ - -struct nfs_client * -nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, - struct nfs4_sessionid *sid, u32 minorversion) -{ - return NULL; -} -#endif /* CONFIG_NFS_V4_1 */ - /* * Set up an NFS4 client */ @@ -878,7 +857,6 @@ EXPORT_SYMBOL_GPL(nfs4_set_ds_client); */ static void nfs4_session_limit_rwsize(struct nfs_server *server) { -#ifdef CONFIG_NFS_V4_1 struct nfs4_session *sess; u32 server_resp_sz; u32 server_rqst_sz; @@ -895,7 +873,6 @@ static void nfs4_session_limit_rwsize(struct nfs_server *server) server->rsize = server_resp_sz; if (server->wsize > server_rqst_sz) server->wsize = server_rqst_sz; -#endif /* CONFIG_NFS_V4_1 */ } /* diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 3ee5394aca72..405f3fdbc1f0 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -99,7 +99,6 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, static int nfs4_do_setattr(struct inode *inode, const struct cred *cred, struct nfs_fattr *fattr, struct iattr *sattr, struct nfs_open_context *ctx, struct nfs4_label *ilabel); -#ifdef CONFIG_NFS_V4_1 static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, const struct cred *cred, struct nfs4_slot *slot, @@ -108,7 +107,6 @@ static int nfs41_test_stateid(struct nfs_server *, const nfs4_stateid *, const struct cred *); static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *, const struct cred *, bool); -#endif #ifdef CONFIG_NFS_V4_SECURITY_LABEL static inline struct nfs4_label * @@ -569,7 +567,6 @@ static int nfs4_do_handle_exception(struct nfs_server *server, case -NFS4ERR_LEASE_MOVED: nfs4_schedule_lease_moved_recovery(clp); goto wait_on_recovery; -#if defined(CONFIG_NFS_V4_1) case -NFS4ERR_BADSESSION: case -NFS4ERR_BADSLOT: case -NFS4ERR_BAD_HIGH_SLOT: @@ -579,7 +576,6 @@ static int nfs4_do_handle_exception(struct nfs_server *server, case -NFS4ERR_SEQ_MISORDERED: /* Handled in nfs41_sequence_process() */ goto wait_on_recovery; -#endif /* defined(CONFIG_NFS_V4_1) */ case -NFS4ERR_FILE_OPEN: if (exception->timeout > HZ) { /* We have retried a decent amount, time to @@ -783,8 +779,6 @@ void nfs4_init_sequence(struct nfs_client *clp, res->sr_slot_ops = clp->cl_mvops->sequence_slot_ops; } -#if defined(CONFIG_NFS_V4_1) - static void nfs41_release_slot(struct nfs4_slot *slot) { struct nfs4_session *session; @@ -1022,8 +1016,6 @@ static const struct rpc_call_ops nfs41_call_sync_ops = { }; -#endif /* !CONFIG_NFS_V4_1 */ - static void nfs41_sequence_res_init(struct nfs4_sequence_res *res) { res->sr_timestamp = jiffies; @@ -1589,7 +1581,6 @@ static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode) nfs4_state_set_mode_locked(state, state->state | fmode); } -#ifdef CONFIG_NFS_V4_1 static bool nfs_open_stateid_recover_openmode(struct nfs4_state *state) { if (state->n_rdonly && !test_bit(NFS_O_RDONLY_STATE, &state->flags)) @@ -1600,7 +1591,6 @@ static bool nfs_open_stateid_recover_openmode(struct nfs4_state *state) return true; return false; } -#endif /* CONFIG_NFS_V4_1 */ static void nfs_state_log_update_open_stateid(struct nfs4_state *state) { @@ -2837,7 +2827,6 @@ void nfs_finish_clear_delegation_stateid(struct nfs4_state *state, nfs_state_clear_delegation(state); } -#if defined(CONFIG_NFS_V4_1) static int nfs41_test_and_free_expired_stateid(struct nfs_server *server, nfs4_stateid *stateid, const struct cred *cred) { @@ -3022,7 +3011,6 @@ static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st status = nfs4_open_expired(sp, state); return status; } -#endif /* * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-* @@ -4384,7 +4372,6 @@ static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir, return status; } -#if IS_ENABLED(CONFIG_NFS_V4_1) static bool should_request_dir_deleg(struct inode *inode) { if (!directory_delegations) @@ -4401,12 +4388,6 @@ static bool should_request_dir_deleg(struct inode *inode) return false; return true; } -#else -static bool should_request_dir_deleg(struct inode *inode) -{ - return false; -} -#endif /* CONFIG_NFS_V4_1 */ static void nfs4_call_getattr_prepare(struct rpc_task *task, void *calldata) { @@ -7552,7 +7533,6 @@ int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request) return err; } -#if defined(CONFIG_NFS_V4_1) static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request) { struct nfs4_lock_state *lsp; @@ -7567,7 +7547,6 @@ static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *reques return 0; return nfs4_lock_expired(state, request); } -#endif static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) { @@ -7641,7 +7620,6 @@ nfs4_retry_setlk_simple(struct nfs4_state *state, int cmd, return status; } -#ifdef CONFIG_NFS_V4_1 struct nfs4_lock_waiter { struct inode *inode; struct nfs_lowner owner; @@ -7709,13 +7687,6 @@ nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) return status; } -#else /* !CONFIG_NFS_V4_1 */ -static inline int -nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) -{ - return nfs4_retry_setlk_simple(state, cmd, request); -} -#endif static int nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) @@ -7848,7 +7819,6 @@ static bool nfs4_xattr_list_nfs4_acl(struct dentry *dentry) return nfs4_server_supports_acls(NFS_SB(dentry->d_sb), NFS4ACL_ACL); } -#if defined(CONFIG_NFS_V4_1) #define XATTR_NAME_NFSV4_DACL "system.nfs4_dacl" static int nfs4_xattr_set_nfs4_dacl(const struct xattr_handler *handler, @@ -7895,8 +7865,6 @@ static bool nfs4_xattr_list_nfs4_sacl(struct dentry *dentry) return nfs4_server_supports_acls(NFS_SB(dentry->d_sb), NFS4ACL_SACL); } -#endif - #ifdef CONFIG_NFS_V4_SECURITY_LABEL static int nfs4_xattr_set_nfs4_label(const struct xattr_handler *handler, @@ -8156,8 +8124,6 @@ int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, return err; } -#ifdef CONFIG_NFS_V4_1 - /* * This operation also signals the server that this client is * performing migration recovery. The server can stop asserting @@ -8220,8 +8186,6 @@ static int _nfs41_proc_get_locations(struct nfs_server *server, return status; } -#endif /* CONFIG_NFS_V4_1 */ - /** * nfs4_proc_get_locations - discover locations for a migrated FSID * @server: pointer to nfs_server to process @@ -8269,8 +8233,6 @@ int nfs4_proc_get_locations(struct nfs_server *server, return status; } -#ifdef CONFIG_NFS_V4_1 - /* * This operation also signals the server that this client is * performing "lease moved" recovery. The server can stop asserting @@ -8309,8 +8271,6 @@ static int _nfs41_proc_fsid_present(struct inode *inode, const struct cred *cred return status; } -#endif /* CONFIG_NFS_V4_1 */ - /** * nfs4_proc_fsid_present - Is this FSID present or absent on server? * @inode: inode on FSID to check @@ -8439,7 +8399,6 @@ int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, return err; } -#ifdef CONFIG_NFS_V4_1 /* * Check the exchange flags returned by the server for invalid flags, having * both PNFS and NON_PNFS flags set, and not having one of NON_PNFS, PNFS, or @@ -9052,8 +9011,6 @@ int nfs4_destroy_clientid(struct nfs_client *clp) return ret; } -#endif /* CONFIG_NFS_V4_1 */ - struct nfs4_get_lease_time_data { struct nfs4_get_lease_time_args *args; struct nfs4_get_lease_time_res *res; @@ -9130,8 +9087,6 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return nfs4_call_sync_custom(&task_setup); } -#ifdef CONFIG_NFS_V4_1 - /* * Initialize the values to be used by the client in CREATE_SESSION * If nfs4_init_session set the fore channel request and response sizes, @@ -10470,8 +10425,6 @@ static bool nfs41_match_stateid(const nfs4_stateid *s1, return s1->seqid == 0 || s2->seqid == 0; } -#endif /* CONFIG_NFS_V4_1 */ - bool nfs4_match_stateid(const nfs4_stateid *s1, const nfs4_stateid *s2) { @@ -10481,7 +10434,6 @@ bool nfs4_match_stateid(const nfs4_stateid *s1, } -#if defined(CONFIG_NFS_V4_1) static const struct nfs4_sequence_slot_ops nfs41_sequence_slot_ops = { .process = nfs41_sequence_process, .done = nfs41_sequence_done, @@ -10548,7 +10500,6 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { .state_renewal_ops = &nfs41_state_renewal_ops, .mig_recovery_ops = &nfs41_mig_recovery_ops, }; -#endif #if defined(CONFIG_NFS_V4_2) static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = { @@ -10594,9 +10545,7 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { #if defined(CONFIG_NFS_V4_0) [0] = &nfs_v4_0_minor_ops, #endif /* CONFIG_NFS_V4_0 */ -#if defined(CONFIG_NFS_V4_1) [1] = &nfs_v4_1_minor_ops, -#endif #if defined(CONFIG_NFS_V4_2) [2] = &nfs_v4_2_minor_ops, #endif @@ -10775,7 +10724,6 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { .set = nfs4_xattr_set_nfs4_acl, }; -#if defined(CONFIG_NFS_V4_1) static const struct xattr_handler nfs4_xattr_nfs4_dacl_handler = { .name = XATTR_NAME_NFSV4_DACL, .list = nfs4_xattr_list_nfs4_dacl, @@ -10789,7 +10737,6 @@ static const struct xattr_handler nfs4_xattr_nfs4_sacl_handler = { .get = nfs4_xattr_get_nfs4_sacl, .set = nfs4_xattr_set_nfs4_sacl, }; -#endif #ifdef CONFIG_NFS_V4_2 static const struct xattr_handler nfs4_xattr_nfs4_user_handler = { @@ -10801,10 +10748,8 @@ static const struct xattr_handler nfs4_xattr_nfs4_user_handler = { const struct xattr_handler * const nfs4_xattr_handlers[] = { &nfs4_xattr_nfs4_acl_handler, -#if defined(CONFIG_NFS_V4_1) &nfs4_xattr_nfs4_dacl_handler, &nfs4_xattr_nfs4_sacl_handler, -#endif #ifdef CONFIG_NFS_V4_SECURITY_LABEL &nfs4_xattr_nfs4_label_handler, #endif diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index 5db460476bf2..a2fdd4b80dc4 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -408,8 +408,6 @@ void nfs41_wake_slot_table(struct nfs4_slot_table *tbl) } } -#if defined(CONFIG_NFS_V4_1) - static void nfs41_set_max_slotid_locked(struct nfs4_slot_table *tbl, u32 target_highest_slotid) { @@ -653,5 +651,3 @@ int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time) return 0; } EXPORT_SYMBOL_GPL(nfs4_init_ds_session); - -#endif /* defined(CONFIG_NFS_V4_1) */ diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index f9c291e2165c..d2569f599977 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -111,7 +111,6 @@ static inline struct nfs4_session *nfs4_get_session(const struct nfs_client *clp return clp->cl_session; } -#if defined(CONFIG_NFS_V4_1) extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, u32 target_highest_slotid); extern void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, @@ -154,28 +153,6 @@ static inline void nfs4_copy_sessionid(struct nfs4_sessionid *dst, */ #define nfs_session_id_hash(sess_id) \ (~crc32_le(0xFFFFFFFF, &(sess_id)->data[0], sizeof((sess_id)->data))) -#else /* defined(CONFIG_NFS_V4_1) */ -static inline int nfs4_init_session(struct nfs_client *clp) -{ - return 0; -} - -/* - * Determine if sessions are in use. - */ -static inline int nfs4_has_session(const struct nfs_client *clp) -{ - return 0; -} - -static inline int nfs4_has_persistent_session(const struct nfs_client *clp) -{ - return 0; -} - -#define nfs_session_id_hash(session) (0) - -#endif /* defined(CONFIG_NFS_V4_1) */ #endif /* IS_ENABLED(CONFIG_NFS_V4) */ #endif /* __LINUX_FS_NFS_NFS4SESSION_H */ diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 3330a188e1a5..3de1fb242c15 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -259,8 +259,6 @@ static int nfs4_begin_drain_session(struct nfs_client *clp) return nfs4_drain_slot_tbl(&ses->fc_slot_table); } -#if defined(CONFIG_NFS_V4_1) - static void nfs41_finish_session_reset(struct nfs_client *clp) { clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); @@ -339,8 +337,6 @@ int nfs41_discover_server_trunking(struct nfs_client *clp, return status; } -#endif /* CONFIG_NFS_V4_1 */ - /** * nfs4_get_clid_cred - Acquire credential for a setclientid operation * @clp: client state handle @@ -2310,7 +2306,6 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, return status; } -#ifdef CONFIG_NFS_V4_1 void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) { struct nfs_client *clp = session->clp; @@ -2517,18 +2512,6 @@ static void nfs4_layoutreturn_any_run(struct nfs_client *clp) set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state); } } -#else /* CONFIG_NFS_V4_1 */ -static int nfs4_reset_session(struct nfs_client *clp) { return 0; } - -static int nfs4_bind_conn_to_session(struct nfs_client *clp) -{ - return 0; -} - -static void nfs4_layoutreturn_any_run(struct nfs_client *clp) -{ -} -#endif /* CONFIG_NFS_V4_1 */ static void nfs4_state_manager(struct nfs_client *clp) { diff --git a/fs/nfs/nfs4trace.c b/fs/nfs/nfs4trace.c index 987c92d6364b..3fdc013f56d8 100644 --- a/fs/nfs/nfs4trace.c +++ b/fs/nfs/nfs4trace.c @@ -14,7 +14,6 @@ #define CREATE_TRACE_POINTS #include "nfs4trace.h" -#ifdef CONFIG_NFS_V4_1 EXPORT_TRACEPOINT_SYMBOL_GPL(nfs4_pnfs_read); EXPORT_TRACEPOINT_SYMBOL_GPL(nfs4_pnfs_write); EXPORT_TRACEPOINT_SYMBOL_GPL(nfs4_pnfs_commit_ds); @@ -39,4 +38,3 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(bl_pr_key_unreg); EXPORT_TRACEPOINT_SYMBOL_GPL(bl_pr_key_unreg_err); EXPORT_TRACEPOINT_SYMBOL_GPL(fl_getdevinfo); -#endif diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h index 8ff6396bc206..a598d94d4536 100644 --- a/fs/nfs/nfs4trace.h +++ b/fs/nfs/nfs4trace.h @@ -71,7 +71,6 @@ DEFINE_NFS4_CLIENTID_EVENT(nfs4_setclientid); DEFINE_NFS4_CLIENTID_EVENT(nfs4_setclientid_confirm); DEFINE_NFS4_CLIENTID_EVENT(nfs4_renew); DEFINE_NFS4_CLIENTID_EVENT(nfs4_renew_async); -#ifdef CONFIG_NFS_V4_1 DEFINE_NFS4_CLIENTID_EVENT(nfs4_exchange_id); DEFINE_NFS4_CLIENTID_EVENT(nfs4_create_session); DEFINE_NFS4_CLIENTID_EVENT(nfs4_destroy_session); @@ -302,8 +301,6 @@ TRACE_EVENT(pnfs_ds_connect, ) ); -#endif /* CONFIG_NFS_V4_1 */ - TRACE_EVENT(nfs4_setup_sequence, TP_PROTO( const struct nfs4_session *session, @@ -1068,7 +1065,6 @@ TRACE_EVENT(nfs4_delegreturn_exit, ) ); -#ifdef CONFIG_NFS_V4_1 DECLARE_EVENT_CLASS(nfs4_test_stateid_event, TP_PROTO( const struct nfs4_state *state, @@ -1123,7 +1119,6 @@ DECLARE_EVENT_CLASS(nfs4_test_stateid_event, DEFINE_NFS4_TEST_STATEID_EVENT(nfs4_test_delegation_stateid); DEFINE_NFS4_TEST_STATEID_EVENT(nfs4_test_open_stateid); DEFINE_NFS4_TEST_STATEID_EVENT(nfs4_test_lock_stateid); -#endif /* CONFIG_NFS_V4_1 */ DECLARE_EVENT_CLASS(nfs4_lookup_event, TP_PROTO( @@ -1626,12 +1621,8 @@ DEFINE_NFS4_IDMAP_EVENT(nfs4_map_group_to_gid); DEFINE_NFS4_IDMAP_EVENT(nfs4_map_uid_to_name); DEFINE_NFS4_IDMAP_EVENT(nfs4_map_gid_to_group); -#ifdef CONFIG_NFS_V4_1 #define NFS4_LSEG_LAYOUT_STATEID_HASH(lseg) \ (lseg ? nfs_stateid_hash(&lseg->pls_layout->plh_stateid) : 0) -#else -#define NFS4_LSEG_LAYOUT_STATEID_HASH(lseg) (0) -#endif DECLARE_EVENT_CLASS(nfs4_read_event, TP_PROTO( @@ -1703,9 +1694,7 @@ DECLARE_EVENT_CLASS(nfs4_read_event, ), \ TP_ARGS(hdr, error)) DEFINE_NFS4_READ_EVENT(nfs4_read); -#ifdef CONFIG_NFS_V4_1 DEFINE_NFS4_READ_EVENT(nfs4_pnfs_read); -#endif /* CONFIG_NFS_V4_1 */ DECLARE_EVENT_CLASS(nfs4_write_event, TP_PROTO( @@ -1778,9 +1767,7 @@ DECLARE_EVENT_CLASS(nfs4_write_event, ), \ TP_ARGS(hdr, error)) DEFINE_NFS4_WRITE_EVENT(nfs4_write); -#ifdef CONFIG_NFS_V4_1 DEFINE_NFS4_WRITE_EVENT(nfs4_pnfs_write); -#endif /* CONFIG_NFS_V4_1 */ DECLARE_EVENT_CLASS(nfs4_commit_event, TP_PROTO( @@ -1840,7 +1827,6 @@ DECLARE_EVENT_CLASS(nfs4_commit_event, ), \ TP_ARGS(data, error)) DEFINE_NFS4_COMMIT_EVENT(nfs4_commit); -#ifdef CONFIG_NFS_V4_1 DEFINE_NFS4_COMMIT_EVENT(nfs4_pnfs_commit_ds); TRACE_EVENT(nfs4_layoutget, @@ -2874,8 +2860,6 @@ DEFINE_NFS4_XATTR_EVENT(nfs4_removexattr); DEFINE_NFS4_INODE_EVENT(nfs4_listxattr); #endif /* CONFIG_NFS_V4_2 */ -#endif /* CONFIG_NFS_V4_1 */ - #endif /* _TRACE_NFS4_H */ #undef TRACE_INCLUDE_PATH diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 85ddcee51162..c23c2eee1b5c 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -308,7 +308,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, #define encode_secinfo_maxsz (op_encode_hdr_maxsz + nfs4_name_maxsz) #define decode_secinfo_maxsz (op_decode_hdr_maxsz + 1 + ((NFS_MAX_SECFLAVORS * (16 + GSS_OID_MAX_LEN)) / 4)) -#if defined(CONFIG_NFS_V4_1) #define NFS4_MAX_MACHINE_NAME_LEN (64) #define IMPL_NAME_LIMIT (sizeof(utsname()->sysname) + sizeof(utsname()->release) + \ sizeof(utsname()->version) + sizeof(utsname()->machine) + 8) @@ -455,16 +454,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, #define encode_free_stateid_maxsz (op_encode_hdr_maxsz + 1 + \ XDR_QUADLEN(NFS4_STATEID_SIZE)) #define decode_free_stateid_maxsz (op_decode_hdr_maxsz) -#else /* CONFIG_NFS_V4_1 */ -#define encode_sequence_maxsz 0 -#define decode_sequence_maxsz 0 -#define encode_get_dir_deleg_maxsz 0 -#define decode_get_dir_deleg_maxsz 0 -#define encode_layoutreturn_maxsz 0 -#define decode_layoutreturn_maxsz 0 -#define encode_layoutget_maxsz 0 -#define decode_layoutget_maxsz 0 -#endif /* CONFIG_NFS_V4_1 */ #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ #define NFS4_dec_compound_sz (1024) /* XXX: large enough? */ @@ -838,7 +827,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, decode_putfh_maxsz + \ decode_getfh_maxsz + \ decode_renew_maxsz) -#if defined(CONFIG_NFS_V4_1) #define NFS4_enc_bind_conn_to_session_sz \ (compound_encode_hdr_maxsz + \ encode_bind_conn_to_session_maxsz) @@ -871,7 +859,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, #define NFS4_dec_sequence_sz \ (compound_decode_hdr_maxsz + \ decode_sequence_maxsz) -#endif #define NFS4_enc_get_lease_time_sz (compound_encode_hdr_maxsz + \ encode_sequence_maxsz + \ encode_putrootfh_maxsz + \ @@ -880,7 +867,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, decode_sequence_maxsz + \ decode_putrootfh_maxsz + \ decode_fsinfo_maxsz) -#if defined(CONFIG_NFS_V4_1) #define NFS4_enc_reclaim_complete_sz (compound_encode_hdr_maxsz + \ encode_sequence_maxsz + \ encode_reclaim_complete_maxsz) @@ -958,7 +944,6 @@ const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH + decode_sequence_maxsz) * XDR_UNIT); EXPORT_SYMBOL_GPL(nfs41_maxgetdevinfo_overhead); -#endif /* CONFIG_NFS_V4_1 */ static const umode_t nfs_type2fmt[] = { [NF4BAD] = 0, @@ -1834,7 +1819,6 @@ static void encode_secinfo(struct xdr_stream *xdr, const struct qstr *name, stru encode_string(xdr, name->len, name->name); } -#if defined(CONFIG_NFS_V4_1) /* NFSv4.1 operations */ static void encode_bind_conn_to_session(struct xdr_stream *xdr, const struct nfs41_bind_conn_to_session_args *args, @@ -1986,13 +1970,11 @@ static void encode_reclaim_complete(struct xdr_stream *xdr, encode_op_hdr(xdr, OP_RECLAIM_COMPLETE, decode_reclaim_complete_maxsz, hdr); encode_uint32(xdr, args->one_fs); } -#endif /* CONFIG_NFS_V4_1 */ static void encode_sequence(struct xdr_stream *xdr, const struct nfs4_sequence_args *args, struct compound_hdr *hdr) { -#if defined(CONFIG_NFS_V4_1) struct nfs4_session *session; struct nfs4_slot_table *tp; struct nfs4_slot *slot = args->sa_slot; @@ -2023,10 +2005,8 @@ static void encode_sequence(struct xdr_stream *xdr, *p++ = cpu_to_be32(slot->slot_nr); *p++ = cpu_to_be32(tp->highest_used_slotid); *p = cpu_to_be32(args->sa_cache_this); -#endif /* CONFIG_NFS_V4_1 */ } -#ifdef CONFIG_NFS_V4_1 static void encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr) { @@ -2188,26 +2168,6 @@ static void encode_free_stateid(struct xdr_stream *xdr, encode_op_hdr(xdr, OP_FREE_STATEID, decode_free_stateid_maxsz, hdr); encode_nfs4_stateid(xdr, &args->stateid); } -#else -static inline void -encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr) -{ -} - -static inline void -encode_layoutreturn(struct xdr_stream *xdr, - const struct nfs4_layoutreturn_args *args, - struct compound_hdr *hdr) -{ -} - -static void -encode_layoutget(struct xdr_stream *xdr, - const struct nfs4_layoutget_args *args, - struct compound_hdr *hdr) -{ -} -#endif /* CONFIG_NFS_V4_1 */ /* * END OF "GENERIC" ENCODE ROUTINES. @@ -2215,11 +2175,9 @@ encode_layoutget(struct xdr_stream *xdr, static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args) { -#if defined(CONFIG_NFS_V4_1) struct nfs4_session *session = args->sa_slot->table->session; if (session) return session->clp->cl_mvops->minor_version; -#endif /* CONFIG_NFS_V4_1 */ return 0; } @@ -2977,7 +2935,6 @@ static void nfs4_xdr_enc_fsid_present(struct rpc_rqst *req, encode_nops(&hdr); } -#if defined(CONFIG_NFS_V4_1) /* * BIND_CONN_TO_SESSION request */ @@ -3079,8 +3036,6 @@ static void nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr, encode_nops(&hdr); } -#endif - /* * a GET_LEASE_TIME request */ @@ -3101,8 +3056,6 @@ static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req, encode_nops(&hdr); } -#ifdef CONFIG_NFS_V4_1 - /* * a RECLAIM_COMPLETE request */ @@ -3265,7 +3218,6 @@ static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req, encode_free_stateid(xdr, args, &hdr); encode_nops(&hdr); } -#endif /* CONFIG_NFS_V4_1 */ static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char **string) { @@ -5764,7 +5716,6 @@ static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) return decode_secinfo_common(xdr, res); } -#if defined(CONFIG_NFS_V4_1) static int decode_secinfo_no_name(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) { int status = decode_op_hdr(xdr, OP_SECINFO_NO_NAME); @@ -5976,13 +5927,11 @@ static int decode_reclaim_complete(struct xdr_stream *xdr, void *dummy) { return decode_op_hdr(xdr, OP_RECLAIM_COMPLETE); } -#endif /* CONFIG_NFS_V4_1 */ static int decode_sequence(struct xdr_stream *xdr, struct nfs4_sequence_res *res, struct rpc_rqst *rqstp) { -#if defined(CONFIG_NFS_V4_1) struct nfs4_session *session; struct nfs4_sessionid id; u32 dummy; @@ -6042,12 +5991,8 @@ static int decode_sequence(struct xdr_stream *xdr, out_overflow: status = -EIO; goto out_err; -#else /* CONFIG_NFS_V4_1 */ - return 0; -#endif /* CONFIG_NFS_V4_1 */ } -#if defined(CONFIG_NFS_V4_1) static int decode_layout_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) { stateid->type = NFS4_LAYOUT_STATEID_TYPE; @@ -6310,27 +6255,6 @@ static int decode_free_stateid(struct xdr_stream *xdr, res->status = decode_op_hdr(xdr, OP_FREE_STATEID); return res->status; } -#else -static int decode_get_dir_delegation(struct xdr_stream *xdr, - struct nfs4_getattr_res *res) -{ - return 0; -} - -static inline -int decode_layoutreturn(struct xdr_stream *xdr, - struct nfs4_layoutreturn_res *res) -{ - return 0; -} - -static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, - struct nfs4_layoutget_res *res) -{ - return 0; -} - -#endif /* CONFIG_NFS_V4_1 */ /* * END OF "GENERIC" DECODE ROUTINES. @@ -7359,7 +7283,6 @@ static int nfs4_xdr_dec_fsid_present(struct rpc_rqst *rqstp, return status; } -#if defined(CONFIG_NFS_V4_1) /* * Decode BIND_CONN_TO_SESSION response */ @@ -7456,8 +7379,6 @@ static int nfs4_xdr_dec_sequence(struct rpc_rqst *rqstp, return status; } -#endif - /* * Decode GET_LEASE_TIME response */ @@ -7479,8 +7400,6 @@ static int nfs4_xdr_dec_get_lease_time(struct rpc_rqst *rqstp, return status; } -#ifdef CONFIG_NFS_V4_1 - /* * Decode RECLAIM_COMPLETE response */ @@ -7668,7 +7587,6 @@ static int nfs4_xdr_dec_free_stateid(struct rpc_rqst *rqstp, out: return status; } -#endif /* CONFIG_NFS_V4_1 */ /** * nfs4_decode_dirent - Decode a single NFSv4 directory entry stored in @@ -7774,13 +7692,8 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, STUB(proc) #endif /* CONFIG_NFS_V4_0 */ -#if defined(CONFIG_NFS_V4_1) #define PROC41(proc, argtype, restype) \ PROC(proc, argtype, restype) -#else -#define PROC41(proc, argtype, restype) \ - STUB(proc) -#endif #if defined(CONFIG_NFS_V4_2) #define PROC42(proc, argtype, restype) \ diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 3db8f13d8fe4..eb39859c216c 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -84,7 +84,7 @@ enum pnfs_try_status { PNFS_TRY_AGAIN = 2, }; -#ifdef CONFIG_NFS_V4_1 +#if IS_ENABLED(CONFIG_NFS_V4) #define LAYOUT_NFSV4_1_MODULE_PREFIX "nfs-layouttype4" @@ -704,7 +704,7 @@ static inline void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id) } #endif /* NFS_DEBUG */ -#else /* CONFIG_NFS_V4_1 */ +#else /* CONFIG_NFS_V4 */ static inline bool nfs_have_layout(struct inode *inode) { @@ -913,7 +913,7 @@ static inline bool pnfs_layout_is_valid(const struct pnfs_layout_hdr *lo) return false; } -#endif /* CONFIG_NFS_V4_1 */ +#endif /* CONFIG_NFS_V4 */ #if IS_ENABLED(CONFIG_NFS_V4_2) int pnfs_report_layoutstat(struct inode *inode, gfp_t gfp_flags); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 3c1fa320b3f1..e1fe78d7b8d0 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -68,10 +68,10 @@ void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_server *server = NFS_SERVER(inode); const struct nfs_pageio_ops *pg_ops = &nfs_pgio_rw_ops; -#ifdef CONFIG_NFS_V4_1 +#if IS_ENABLED(CONFIG_NFS_V4) if (server->pnfs_curr_ld && !force_mds) pg_ops = server->pnfs_curr_ld->pg_read_ops; -#endif +#endif /* CONFIG_NFS_V4 */ nfs_pageio_init(pgio, inode, pg_ops, compl_ops, &nfs_rw_read_ops, server->rsize, 0); } diff --git a/fs/nfs/super.c b/fs/nfs/super.c index e74164d9c081..7a318581f85b 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -589,18 +589,13 @@ static void show_lease(struct seq_file *m, struct nfs_server *server) seq_printf(m, ",lease_expired=%ld", time_after(expire, jiffies) ? 0 : (jiffies - expire) / HZ); } -#ifdef CONFIG_NFS_V4_1 + static void show_sessions(struct seq_file *m, struct nfs_server *server) { if (nfs4_has_session(server->nfs_client)) seq_puts(m, ",sessions"); } -#else -static void show_sessions(struct seq_file *m, struct nfs_server *server) {} -#endif -#endif -#ifdef CONFIG_NFS_V4_1 static void show_pnfs(struct seq_file *m, struct nfs_server *server) { seq_printf(m, ",pnfs="); @@ -620,16 +615,11 @@ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss) impl_id->date.seconds, impl_id->date.nseconds); } } -#else -#if IS_ENABLED(CONFIG_NFS_V4) -static void show_pnfs(struct seq_file *m, struct nfs_server *server) -{ -} -#endif +#else /* CONFIG_NFS_V4 */ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss) { } -#endif +#endif /* CONFIG_NFS_V4 */ int nfs_show_devname(struct seq_file *m, struct dentry *root) { diff --git a/fs/nfs/sysfs.c b/fs/nfs/sysfs.c index ea6e6168092b..7bf650fda1cb 100644 --- a/fs/nfs/sysfs.c +++ b/fs/nfs/sysfs.c @@ -293,7 +293,7 @@ shutdown_store(struct kobject *kobj, struct kobj_attribute *attr, static struct kobj_attribute nfs_sysfs_attr_shutdown = __ATTR_RW(shutdown); -#if IS_ENABLED(CONFIG_NFS_V4_1) +#if IS_ENABLED(CONFIG_NFS_V4) static ssize_t implid_domain_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -323,7 +323,7 @@ implid_name_show(struct kobject *kobj, struct kobj_attribute *attr, static struct kobj_attribute nfs_sysfs_attr_implid_name = __ATTR_RO(implid_name); -#endif /* IS_ENABLED(CONFIG_NFS_V4_1) */ +#endif /* IS_ENABLED(CONFIG_NFS_V4) */ #define RPC_CLIENT_NAME_SIZE 64 @@ -362,7 +362,7 @@ static struct kobj_type nfs_sb_ktype = { .child_ns_type = nfs_netns_object_child_ns_type, }; -#if IS_ENABLED(CONFIG_NFS_V4_1) +#if IS_ENABLED(CONFIG_NFS_V4) static void nfs_sysfs_add_nfsv41_server(struct nfs_server *server) { int ret; @@ -382,11 +382,11 @@ static void nfs_sysfs_add_nfsv41_server(struct nfs_server *server) pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n", server->s_sysfs_id, ret); } -#else /* CONFIG_NFS_V4_1 */ +#else /* CONFIG_NFS_V4 */ static inline void nfs_sysfs_add_nfsv41_server(struct nfs_server *server) { } -#endif /* CONFIG_NFS_V4_1 */ +#endif /* CONFIG_NFS_V4 */ #if IS_ENABLED(CONFIG_NFS_LOCALIO) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index bf412455e8ed..2d0e4a765aeb 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1402,7 +1402,7 @@ void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_server *server = NFS_SERVER(inode); const struct nfs_pageio_ops *pg_ops = &nfs_pgio_rw_ops; -#ifdef CONFIG_NFS_V4_1 +#if IS_ENABLED(CONFIG_NFS_V4) if (server->pnfs_curr_ld && !force_mds) pg_ops = server->pnfs_curr_ld->pg_write_ops; #endif diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index bb13a294b69e..89826c3e15a2 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -115,9 +115,7 @@ struct nfs_client { #define NFS_SP4_MACH_CRED_WRITE 5 /* WRITE */ #define NFS_SP4_MACH_CRED_COMMIT 6 /* COMMIT */ #define NFS_SP4_MACH_CRED_PNFS_CLEANUP 7 /* LAYOUTRETURN */ -#if IS_ENABLED(CONFIG_NFS_V4_1) wait_queue_head_t cl_lock_waitq; -#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4 */ /* Our own IP address, as a null-terminated string. diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 2aa4e38af57a..437e6f4af7e0 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1324,10 +1324,6 @@ struct nfs4_fsid_present_res { unsigned char renew:1; }; -#endif /* CONFIG_NFS_V4 */ - -#ifdef CONFIG_NFS_V4_1 - struct pnfs_commit_bucket { struct list_head written; struct list_head committing; @@ -1467,7 +1463,7 @@ struct nfs41_free_stateid_res { struct pnfs_ds_commit_info { }; -#endif /* CONFIG_NFS_V4_1 */ +#endif /* CONFIG_NFS_V4 */ #ifdef CONFIG_NFS_V4_2 struct nfs42_falloc_args { From d37272c62a08b5fc74bedf7b560f794a84b7cdf1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 28 Jan 2026 05:46:03 +0100 Subject: [PATCH 45/60] NFS: return void from nfs4_inode_make_writeable None of the callers checks the return value, so drop it. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 10 +++------- fs/nfs/delegation.h | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 4d5f1f3162b0..c77c7b2d5877 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -912,23 +912,19 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) * @inode: pointer to inode * * Make the inode writeable by returning the delegation if necessary - * - * Returns zero on success, or a negative errno value. */ -int nfs4_inode_make_writeable(struct inode *inode) +void nfs4_inode_make_writeable(struct inode *inode) { struct nfs_delegation *delegation; - int error = 0; delegation = nfs4_get_valid_delegation(inode); if (!delegation) - return 0; + return; if (!nfs4_has_session(NFS_SERVER(inode)->nfs_client) || !(delegation->type & FMODE_WRITE)) - error = nfs4_inode_return_delegation(inode); + nfs4_inode_return_delegation(inode); nfs_put_delegation(delegation); - return error; } static void diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index a6733f034442..d30f19a28077 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -87,7 +87,7 @@ int nfs4_check_delegation(struct inode *inode, fmode_t type); bool nfs4_delegation_flush_on_close(const struct inode *inode); void nfs_inode_find_delegation_state_and_recover(struct inode *inode, const nfs4_stateid *stateid); -int nfs4_inode_make_writeable(struct inode *inode); +void nfs4_inode_make_writeable(struct inode *inode); #endif From b1cb730e847d9766b7fb416e623454a5cba57dc4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 28 Jan 2026 05:46:04 +0100 Subject: [PATCH 46/60] NFS: return void from ->return_delegation The caller doesn't check the return value, so drop it. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 8 +++----- fs/nfs/delegation.h | 2 +- fs/nfs/nfs3proc.c | 3 +-- fs/nfs/proc.c | 3 +-- include/linux/nfs_xdr.h | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index c77c7b2d5877..fe1f57ec326c 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -814,23 +814,21 @@ void nfs_inode_evict_delegation(struct inode *inode) * * Returns zero on success, or a negative errno value. */ -int nfs4_inode_return_delegation(struct inode *inode) +void nfs4_inode_return_delegation(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs_delegation *delegation; - int err; delegation = nfs_start_delegation_return(nfsi); if (!delegation) - return 0; + return; /* Synchronous recall of any application leases */ break_lease(inode, O_WRONLY | O_RDWR); if (S_ISREG(inode->i_mode)) nfs_wb_all(inode); - err = nfs_end_delegation_return(inode, delegation, 1); + nfs_end_delegation_return(inode, delegation, 1); nfs_put_delegation(delegation); - return err; } /** diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index d30f19a28077..eda39fcb032b 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -47,7 +47,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit, u32 deleg_type); -int nfs4_inode_return_delegation(struct inode *inode); +void nfs4_inode_return_delegation(struct inode *inode); void nfs4_inode_return_delegation_on_close(struct inode *inode); void nfs4_inode_set_return_delegation_on_close(struct inode *inode); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 1181f9cc6dbd..d3d2fbeba89d 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -1027,11 +1027,10 @@ static int nfs3_have_delegation(struct inode *inode, fmode_t type, int flags) return 0; } -static int nfs3_return_delegation(struct inode *inode) +static void nfs3_return_delegation(struct inode *inode) { if (S_ISREG(inode->i_mode)) nfs_wb_all(inode); - return 0; } static const struct inode_operations nfs3_dir_inode_operations = { diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 39df80e4ae6f..0e440ebf5335 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -697,11 +697,10 @@ static int nfs_have_delegation(struct inode *inode, fmode_t type, int flags) return 0; } -static int nfs_return_delegation(struct inode *inode) +static void nfs_return_delegation(struct inode *inode) { if (S_ISREG(inode->i_mode)) nfs_wb_all(inode); - return 0; } static const struct inode_operations nfs_dir_inode_operations = { diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 437e6f4af7e0..ff1f12aa73d2 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1846,7 +1846,7 @@ struct nfs_rpc_ops { struct iattr *iattr, int *); int (*have_delegation)(struct inode *, fmode_t, int); - int (*return_delegation)(struct inode *); + void (*return_delegation)(struct inode *); struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *); struct nfs_client *(*init_client) (struct nfs_client *, const struct nfs_client_initdata *); From 2bd7ebcf9bff7ece7a0c3cfa38191d77b4383658 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 28 Jan 2026 05:46:05 +0100 Subject: [PATCH 47/60] NFS: use bool for the issync argument to nfs_end_delegation_return Replace the integer used as boolean with a bool type, and tidy up the prototype and top of function comment. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index fe1f57ec326c..d95a6e9876f1 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -560,9 +560,12 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, } /* - * Basic procedure for returning a delegation to the server + * Basic procedure for returning a delegation to the server. + * If @issync is set, wait until state recovery has finished. Otherwise + * return -EAGAIN to the caller if we need more time. */ -static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) +static int nfs_end_delegation_return(struct inode *inode, + struct nfs_delegation *delegation, bool issync) { struct nfs_server *server = NFS_SERVER(inode); unsigned int mode = O_WRONLY | O_RDWR; @@ -635,7 +638,7 @@ static int nfs_return_one_delegation(struct nfs_server *server) nfs_clear_verifier_delegated(inode); - err = nfs_end_delegation_return(inode, delegation, 0); + err = nfs_end_delegation_return(inode, delegation, false); if (err) { nfs_mark_return_delegation(server, delegation); goto out_put_inode; @@ -827,7 +830,7 @@ void nfs4_inode_return_delegation(struct inode *inode) break_lease(inode, O_WRONLY | O_RDWR); if (S_ISREG(inode->i_mode)) nfs_wb_all(inode); - nfs_end_delegation_return(inode, delegation, 1); + nfs_end_delegation_return(inode, delegation, true); nfs_put_delegation(delegation); } @@ -863,7 +866,7 @@ void nfs4_inode_set_return_delegation_on_close(struct inode *inode) spin_unlock(&delegation->lock); if (return_now) { nfs_clear_verifier_delegated(inode); - nfs_end_delegation_return(inode, delegation, 0); + nfs_end_delegation_return(inode, delegation, false); } nfs_put_delegation(delegation); } @@ -898,7 +901,7 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) if (return_now) { nfs_clear_verifier_delegated(inode); - nfs_end_delegation_return(inode, delegation, 0); + nfs_end_delegation_return(inode, delegation, false); } else { nfs_delegation_add_lru(server, delegation); } From 438c3e47c2e5edfde3ab01443345b4c5e5f2fb55 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 28 Jan 2026 05:46:06 +0100 Subject: [PATCH 48/60] NFS: remove the delegation == NULL check in nfs_end_delegation_return All callers now pass a non-NULL delegation. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index d95a6e9876f1..32803963b5d7 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -571,9 +571,6 @@ static int nfs_end_delegation_return(struct inode *inode, unsigned int mode = O_WRONLY | O_RDWR; int err = 0; - if (delegation == NULL) - return 0; - /* Directory delegations don't require any state recovery */ if (!S_ISREG(inode->i_mode)) goto out_return; From f7550318b29f754dabfb2f1db535ffe77e6d4446 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 28 Jan 2026 05:46:07 +0100 Subject: [PATCH 49/60] NFS: fold nfs_abort_delegation_return into nfs_end_delegation_return This will allow to simplify the error handling flow. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 32803963b5d7..cf90aa7f922a 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -350,21 +350,6 @@ nfs_start_delegation_return(struct nfs_inode *nfsi) return delegation; } -static void nfs_abort_delegation_return(struct nfs_delegation *delegation, - struct nfs_server *server, int err) -{ - spin_lock(&delegation->lock); - clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); - if (err == -EAGAIN) { - set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); - set_bit(NFS4SERV_DELEGRETURN_DELAYED, - &server->delegation_flags); - set_bit(NFS4CLNT_DELEGRETURN_DELAYED, - &server->nfs_client->cl_state); - } - spin_unlock(&delegation->lock); -} - static bool nfs_detach_delegations_locked(struct nfs_inode *nfsi, struct nfs_delegation *delegation, @@ -593,13 +578,23 @@ static int nfs_end_delegation_return(struct inode *inode, err = nfs4_wait_clnt_recover(server->nfs_client); } - if (err) { - nfs_abort_delegation_return(delegation, server, err); - return err; - } + if (err) + goto abort; out_return: return nfs_do_return_delegation(inode, delegation, issync); +abort: + spin_lock(&delegation->lock); + clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); + if (err == -EAGAIN) { + set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); + set_bit(NFS4SERV_DELEGRETURN_DELAYED, + &server->delegation_flags); + set_bit(NFS4CLNT_DELEGRETURN_DELAYED, + &server->nfs_client->cl_state); + } + spin_unlock(&delegation->lock); + return err; } static int nfs_return_one_delegation(struct nfs_server *server) From 94b88865109c0ea10e5ba562a793673b4fb37113 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 28 Jan 2026 05:46:08 +0100 Subject: [PATCH 50/60] NFS: simplify error handling in nfs_end_delegation_return Drop the pointless delegation->lock held over setting multiple atomic bits in different structures, and use separate labels for the delay vs abort cases. Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index cf90aa7f922a..cff49a934c9e 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -570,30 +570,27 @@ static int nfs_end_delegation_return(struct inode *inode, break; err = nfs_delegation_claim_opens(inode, &delegation->stateid, delegation->type); - if (!issync || err != -EAGAIN) + if (!err) break; + if (err != -EAGAIN) + goto abort; + if (!issync) + goto delay; + /* * Guard against state recovery */ err = nfs4_wait_clnt_recover(server->nfs_client); } - if (err) - goto abort; - out_return: return nfs_do_return_delegation(inode, delegation, issync); +delay: + set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); + set_bit(NFS4SERV_DELEGRETURN_DELAYED, &server->delegation_flags); + set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &server->nfs_client->cl_state); abort: - spin_lock(&delegation->lock); clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); - if (err == -EAGAIN) { - set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); - set_bit(NFS4SERV_DELEGRETURN_DELAYED, - &server->delegation_flags); - set_bit(NFS4CLNT_DELEGRETURN_DELAYED, - &server->nfs_client->cl_state); - } - spin_unlock(&delegation->lock); return err; } From 4039fbedcbcb022704ff45533aa7860ce036ee6b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 28 Jan 2026 05:46:09 +0100 Subject: [PATCH 51/60] NFS: fix delayed delegation return handling Rework this code that was totally busted at least as of my most recent changes. Introduce a separate list for delayed delegations so that they can't get lost and don't clutter up the returns list. Add a missing spin_unlock in the helper marking it as a regular pending return. Fixes: 0ebe655bd033 ("NFS: add a separate delegation return list") Reported-by: Chris Mason Signed-off-by: Christoph Hellwig Signed-off-by: Anna Schumaker --- fs/nfs/client.c | 1 + fs/nfs/delegation.c | 30 ++++++++++++------------------ fs/nfs/delegation.h | 1 - fs/nfs/nfs4trace.h | 3 +-- include/linux/nfs_fs_sb.h | 2 +- 5 files changed, 15 insertions(+), 22 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 6b9a65615a51..fd15731cf361 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1063,6 +1063,7 @@ struct nfs_server *nfs_alloc_server(void) spin_lock_init(&server->delegations_lock); INIT_LIST_HEAD(&server->delegations_return); INIT_LIST_HEAD(&server->delegations_lru); + INIT_LIST_HEAD(&server->delegations_delayed); INIT_LIST_HEAD(&server->layouts); INIT_LIST_HEAD(&server->state_owners_lru); INIT_LIST_HEAD(&server->ss_copies); diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index cff49a934c9e..94103f8d3f21 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -336,10 +336,8 @@ nfs_start_delegation_return(struct nfs_inode *nfsi) spin_lock(&delegation->lock); if (delegation->inode && - !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { - clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); + !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) return_now = true; - } spin_unlock(&delegation->lock); if (!return_now) { @@ -586,8 +584,11 @@ static int nfs_end_delegation_return(struct inode *inode, out_return: return nfs_do_return_delegation(inode, delegation, issync); delay: - set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); - set_bit(NFS4SERV_DELEGRETURN_DELAYED, &server->delegation_flags); + spin_lock(&server->delegations_lock); + if (list_empty(&delegation->entry)) + refcount_inc(&delegation->refcount); + list_move_tail(&delegation->entry, &server->delegations_return); + spin_unlock(&server->delegations_lock); set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &server->nfs_client->cl_state); abort: clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); @@ -616,22 +617,16 @@ static int nfs_return_one_delegation(struct nfs_server *server) spin_unlock(&delegation->lock); goto out_put_delegation; } - if (test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || - test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) || + if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) || test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { spin_unlock(&delegation->lock); goto out_put_inode; } - clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); spin_unlock(&delegation->lock); nfs_clear_verifier_delegated(inode); err = nfs_end_delegation_return(inode, delegation, false); - if (err) { - nfs_mark_return_delegation(server, delegation); - goto out_put_inode; - } out_put_inode: iput(inode); @@ -708,19 +703,18 @@ static void nfs_delegation_add_lru(struct nfs_server *server, static bool nfs_server_clear_delayed_delegations(struct nfs_server *server) { - struct nfs_delegation *d; bool ret = false; - if (!test_and_clear_bit(NFS4SERV_DELEGRETURN_DELAYED, - &server->delegation_flags)) + if (list_empty_careful(&server->delegations_delayed)) return false; spin_lock(&server->delegations_lock); - list_for_each_entry_rcu(d, &server->delegations_return, entry) { - if (test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags)) - clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags); + if (!list_empty(&server->delegations_delayed)) { + list_splice_tail_init(&server->delegations_delayed, + &server->delegations_return); ret = true; } + spin_unlock(&server->delegations_lock); return ret; } diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index eda39fcb032b..fba4699952b8 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -37,7 +37,6 @@ enum { NFS_DELEGATION_RETURNING, NFS_DELEGATION_REVOKED, NFS_DELEGATION_TEST_EXPIRED, - NFS_DELEGATION_RETURN_DELAYED, NFS_DELEGATION_DELEGTIME, }; diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h index a598d94d4536..c939533b9881 100644 --- a/fs/nfs/nfs4trace.h +++ b/fs/nfs/nfs4trace.h @@ -991,8 +991,7 @@ DEFINE_NFS4_SET_DELEGATION_EVENT(nfs4_detach_delegation); { BIT(NFS_DELEGATION_REFERENCED), "REFERENCED" }, \ { BIT(NFS_DELEGATION_RETURNING), "RETURNING" }, \ { BIT(NFS_DELEGATION_REVOKED), "REVOKED" }, \ - { BIT(NFS_DELEGATION_TEST_EXPIRED), "TEST_EXPIRED" }, \ - { BIT(NFS_DELEGATION_RETURN_DELAYED), "RETURN_DELAYED" }) + { BIT(NFS_DELEGATION_TEST_EXPIRED), "TEST_EXPIRED" }) DECLARE_EVENT_CLASS(nfs4_delegation_event, TP_PROTO( diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 89826c3e15a2..4daee27fa5eb 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -260,6 +260,7 @@ struct nfs_server { spinlock_t delegations_lock; struct list_head delegations_return; struct list_head delegations_lru; + struct list_head delegations_delayed; atomic_long_t nr_active_delegations; unsigned int delegation_hash_mask; struct hlist_head *delegation_hash_table; @@ -268,7 +269,6 @@ struct nfs_server { unsigned long delegation_flags; #define NFS4SERV_DELEGATION_EXPIRED (1) -#define NFS4SERV_DELEGRETURN_DELAYED (2) unsigned long delegation_gen; unsigned long mig_gen; unsigned long mig_status; From 5248d8474e594d156bee1ed10339cc16e207a28b Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Mon, 26 Jan 2026 14:15:39 -0500 Subject: [PATCH 52/60] pNFS: fix a missing wake up while waiting on NFS_LAYOUT_DRAIN It is possible to have a task get stuck on waiting on the NFS_LAYOUT_DRAIN in the following scenario 1. cpu a: waiter test NFS_LAYOUT_DRAIN (1) and plh_outstanding (1) 2. cpu b: atomic_dec_and_test() -> clear bit -> wake up 3. cpu c: sets NFS_LAYOUT_DRAIN again 4. cpu a: calls wait_on_bit() sleeps forever. To expand on this we have say 2 outstanding pnfs write IO that get ESTALE which causes both to call pnfs_destroy_layout() and set the NFS_LAYOUT_DRAIN bit but the 1st one doesn't call the pnfs_put_layout_hdr() yet (as that would prevent the 2nd ESTALE write from trying to call pnfs_destroy_layout()). If the 1st ESTALE write is the one that initially sets the NFS_LAYOUT_DRAIN so that new IO on this file initiates new LAYOUTGET. Another new write would find NFS_LAYOUT_DRAIN set and phl_outstanding>0 (step 1) and would wait_on_bit(). LAYOUTGET completes doing step 2. Now, the 2nd of ESTALE writes is calling pnfs_destory_layout() and set the NFS_LAYOUT_DRAIN bit (step 3). Finally, the waiting write wakes up to check the bit and goes back to sleep. The problem revolves around the fact that if NFS_LAYOUT_INVALID_STID was already set, it should not do the work of pnfs_mark_layout_stateid_invalid(), thus NFS_LAYOUT_DRAIN will not be set more than once for an invalid layout. Suggested-by: Trond Myklebust Fixes: 880265c77ac4 ("pNFS: Avoid a live lock condition in pnfs_update_layout()") Signed-off-by: Olga Kornievskaia Signed-off-by: Anna Schumaker --- fs/nfs/pnfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index cff225721d1c..ff8483d3373a 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -463,7 +463,8 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo, }; struct pnfs_layout_segment *lseg, *next; - set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags); + if (test_and_set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags)) + return !list_empty(&lo->plh_segs); clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(lo->plh_inode)->flags); list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list) pnfs_clear_lseg_state(lseg, lseg_list); From 42e7c876b182da65723700f6bc507a8aecb10d3b Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Sat, 27 Dec 2025 12:46:29 +0200 Subject: [PATCH 53/60] fs/nfs: Fix readdir slow-start regression Commit 580f236737d1 ("NFS: Adjust the amount of readahead performed by NFS readdir") reduces the amount of readahead names caching done by the client. The downside of this approach is READDIR now may suffer from a slow-start issue, where initially it will fetch names that fit in a single page, then in 2, 4, 8 until the maximum supported transfer size (usually 1M). This patch tries to take a balanced approach between mitigating the slow-start issue still maintaining some efficiency gains. Fixes: 580f236737d1 ("NFS: Adjust the amount of readahead performed by NFS readdir") Signed-off-by: Sagi Grimberg Signed-off-by: Anna Schumaker --- fs/nfs/dir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 8f9ea79b7882..b3f5c9461204 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -72,7 +72,7 @@ const struct address_space_operations nfs_dir_aops = { .free_folio = nfs_readdir_clear_array, }; -#define NFS_INIT_DTSIZE PAGE_SIZE +#define NFS_INIT_DTSIZE SZ_64K static struct nfs_open_dir_context * alloc_nfs_open_dir_context(struct inode *dir) @@ -83,7 +83,7 @@ alloc_nfs_open_dir_context(struct inode *dir) ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT); if (ctx != NULL) { ctx->attr_gencount = nfsi->attr_gencount; - ctx->dtsize = NFS_INIT_DTSIZE; + ctx->dtsize = min(NFS_SERVER(dir)->dtsize, NFS_INIT_DTSIZE); spin_lock(&dir->i_lock); if (list_empty(&nfsi->open_files) && (nfsi->cache_validity & NFS_INO_DATA_INVAL_DEFER)) From fdc0396b3cc05dc9b678627af23c3fdc7dbe930e Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 3 Dec 2025 14:57:28 -0500 Subject: [PATCH 54/60] nfs: unify security_inode_listsecurity() calls commit 243fea134633 ("NFSv4.2: fix listxattr to return selinux security label") introduced a direct call to security_inode_listsecurity() in nfs4_listxattr(). However, nfs4_listxattr() already indirectly called security_inode_listsecurity() via nfs4_listxattr_nfs4_label() if CONFIG_NFS_V4_SECURITY_LABEL is enabled and the server has the NFS_CAP_SECURITY_LABEL capability enabled. This duplication was fixed by commit 9acb237deff7 ("NFSv4.2: another fix for listxattr") by making the second call conditional on NFS_CAP_SECURITY_LABEL not being set by the server. However, the combination of the two changes effectively makes one call to security_inode_listsecurity() in every case - which is the desired behavior since getxattr() always returns a security xattr even if it has to synthesize one. Further, the two different calls produce different xattr name ordering between security.* and user.* xattr names. Unify the two separate calls into a single call and get rid of nfs4_listxattr_nfs4_label() altogether. Link: https://lore.kernel.org/selinux/CAEjxPJ6e8z__=MP5NfdUxkOMQ=EnUFSjWFofP4YPwHqK=Ki5nw@mail.gmail.com/ Signed-off-by: Stephen Smalley Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 38 +++----------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 405f3fdbc1f0..7c9cf0983366 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -7888,33 +7888,12 @@ static int nfs4_xattr_get_nfs4_label(const struct xattr_handler *handler, return -EOPNOTSUPP; } -static ssize_t -nfs4_listxattr_nfs4_label(struct inode *inode, char *list, size_t list_len) -{ - int len = 0; - - if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL)) { - len = security_inode_listsecurity(inode, list, list_len); - if (len >= 0 && list_len && len > list_len) - return -ERANGE; - } - return len; -} - static const struct xattr_handler nfs4_xattr_nfs4_label_handler = { .prefix = XATTR_SECURITY_PREFIX, .get = nfs4_xattr_get_nfs4_label, .set = nfs4_xattr_set_nfs4_label, }; -#else - -static ssize_t -nfs4_listxattr_nfs4_label(struct inode *inode, char *list, size_t list_len) -{ - return 0; -} - #endif #ifdef CONFIG_NFS_V4_2 @@ -10553,7 +10532,7 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size) { - ssize_t error, error2, error3, error4 = 0; + ssize_t error, error2, error3; size_t left = size; error = generic_listxattr(dentry, list, left); @@ -10564,10 +10543,9 @@ static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size) left -= error; } - error2 = nfs4_listxattr_nfs4_label(d_inode(dentry), list, left); + error2 = security_inode_listsecurity(d_inode(dentry), list, left); if (error2 < 0) return error2; - if (list) { list += error2; left -= error2; @@ -10576,18 +10554,8 @@ static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size) error3 = nfs4_listxattr_nfs4_user(d_inode(dentry), list, left); if (error3 < 0) return error3; - if (list) { - list += error3; - left -= error3; - } - if (!nfs_server_capable(d_inode(dentry), NFS_CAP_SECURITY_LABEL)) { - error4 = security_inode_listsecurity(d_inode(dentry), list, left); - if (error4 < 0) - return error4; - } - - error += error2 + error3 + error4; + error += error2 + error3; if (size && error > size) return -ERANGE; return error; From 3d57c44e918012db1f901d50bc9195a8812ad602 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Mon, 8 Dec 2025 23:15:03 +0300 Subject: [PATCH 55/60] NFSv4: pass lease period in seconds to nfs4_set_lease_period() There's no need to multiply the lease period by HZ at all the call sites of nfs4_set_lease_period() -- it makes more sense to do that only once, inside that function, by passing to it lease period as 32-bit # of seconds instead of 32/64-bit *unsigned long* # of jiffies... Signed-off-by: Sergey Shtylyov Signed-off-by: Anna Schumaker --- fs/nfs/nfs4_fs.h | 3 +-- fs/nfs/nfs4proc.c | 2 +- fs/nfs/nfs4renewd.c | 7 ++++--- fs/nfs/nfs4state.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 783df6901b84..b48e5b87cb2a 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -477,8 +477,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *); extern void nfs4_schedule_state_renewal(struct nfs_client *); extern void nfs4_kill_renewd(struct nfs_client *); extern void nfs4_renew_state(struct work_struct *); -extern void nfs4_set_lease_period(struct nfs_client *clp, unsigned long lease); - +extern void nfs4_set_lease_period(struct nfs_client *clp, u32 period); /* nfs4state.c */ extern const nfs4_stateid current_stateid; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7c9cf0983366..9c2ffe72cf33 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5487,7 +5487,7 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, str err = _nfs4_do_fsinfo(server, fhandle, fsinfo); trace_nfs4_fsinfo(server, fhandle, fsinfo->fattr, err); if (err == 0) { - nfs4_set_lease_period(server->nfs_client, fsinfo->lease_time * HZ); + nfs4_set_lease_period(server->nfs_client, fsinfo->lease_time); break; } err = nfs4_handle_exception(server, err, &exception); diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index 18ae614e5a6c..043b2de8d416 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -137,11 +137,12 @@ nfs4_kill_renewd(struct nfs_client *clp) * nfs4_set_lease_period - Sets the lease period on a nfs_client * * @clp: pointer to nfs_client - * @lease: new value for lease period + * @period: new value for lease period (in seconds) */ -void nfs4_set_lease_period(struct nfs_client *clp, - unsigned long lease) +void nfs4_set_lease_period(struct nfs_client *clp, u32 period) { + unsigned long lease = period * HZ; + spin_lock(&clp->cl_lock); clp->cl_lease_time = lease; spin_unlock(&clp->cl_lock); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 3de1fb242c15..963719f35467 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -104,7 +104,7 @@ static int nfs4_setup_state_renewal(struct nfs_client *clp) status = nfs4_proc_get_lease_time(clp, &fsinfo); if (status == 0) { - nfs4_set_lease_period(clp, fsinfo.lease_time * HZ); + nfs4_set_lease_period(clp, fsinfo.lease_time); nfs4_schedule_state_renewal(clp); } From e29a3e61eef6b6c2e60bc1872e9da3bcdbc46c17 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Mon, 8 Dec 2025 23:15:04 +0300 Subject: [PATCH 56/60] NFSv4: limit lease period in nfs4_set_lease_period() In nfs4_set_lease_period(), the passed 32-bit lease period in seconds is multiplied by HZ -- that might overflow before being implicitly cast to *unsigned long* (32/64-bit type), while initializing the lease variable. Cap the lease period at MAX_LEASE_PERIOD (#define'd to 1 hour for now), before multipying to avoid such overflow... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Signed-off-by: Sergey Shtylyov Suggested-by: Trond Myklebust Signed-off-by: Anna Schumaker --- fs/nfs/nfs4renewd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index 043b2de8d416..30065df1482e 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -133,6 +133,8 @@ nfs4_kill_renewd(struct nfs_client *clp) cancel_delayed_work_sync(&clp->cl_renewd); } +#define MAX_LEASE_PERIOD (60 * 60) /* 1 hour */ + /** * nfs4_set_lease_period - Sets the lease period on a nfs_client * @@ -141,7 +143,13 @@ nfs4_kill_renewd(struct nfs_client *clp) */ void nfs4_set_lease_period(struct nfs_client *clp, u32 period) { - unsigned long lease = period * HZ; + unsigned long lease; + + /* Limit the lease period */ + if (period < MAX_LEASE_PERIOD) + lease = period * HZ; + else + lease = MAX_LEASE_PERIOD * HZ; spin_lock(&clp->cl_lock); clp->cl_lease_time = lease; From 728bea264883031c377fcc9c465b650dbfd1bbf5 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 6 Jan 2026 17:37:45 +0000 Subject: [PATCH 57/60] sunrpc: rpc_debug and others are defined even if CONFIG_SUNRPC_DEBUG unset The rpc_debug, nfs_debug, nfsd_debug and nlm_debug are exported even if CONFIG_SUNRPC_DEBUG is not set. This means that the debug header should also define these to remove the following sparse warnings: net/sunrpc/sysctl.c:29:17: warning: symbol 'rpc_debug' was not declared. Should it be static? net/sunrpc/sysctl.c:32:17: warning: symbol 'nfs_debug' was not declared. Should it be static? net/sunrpc/sysctl.c:35:17: warning: symbol 'nfsd_debug' was not declared. Should it be static? net/sunrpc/sysctl.c:38:17: warning: symbol 'nlm_debug' was not declared. Should it be static? Signed-off-by: Ben Dooks Signed-off-by: Anna Schumaker --- include/linux/sunrpc/debug.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index 891f6173c951..eb4bd62df319 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -14,12 +14,10 @@ /* * Debugging macros etc */ -#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) extern unsigned int rpc_debug; extern unsigned int nfs_debug; extern unsigned int nfsd_debug; extern unsigned int nlm_debug; -#endif #define dprintk(fmt, ...) \ dfprintk(FACILITY, fmt, ##__VA_ARGS__) From afb24505ff6583eb5150b4a54086188494d25c28 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Mon, 8 Dec 2025 16:53:48 +0800 Subject: [PATCH 58/60] SUNRPC: Change list definition method The LIST_HEAD macro can both define a linked list and initialize it in one step. To simplify code, we replace the separate operations of linked list definition and manual initialization with the LIST_HEAD macro. Signed-off-by: Chenguang Zhao Reviewed-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: Anna Schumaker --- net/sunrpc/backchannel_rqst.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 68b1fcdea8f0..6b9dee4119d5 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -147,7 +147,7 @@ EXPORT_SYMBOL_GPL(xprt_setup_backchannel); int xprt_setup_bc(struct rpc_xprt *xprt, unsigned int min_reqs) { struct rpc_rqst *req; - struct list_head tmp_list; + LIST_HEAD(tmp_list); int i; dprintk("RPC: setup backchannel transport\n"); @@ -163,7 +163,6 @@ int xprt_setup_bc(struct rpc_xprt *xprt, unsigned int min_reqs) * lock is held on the rpc_xprt struct. It also makes cleanup * easier in case of memory allocation errors. */ - INIT_LIST_HEAD(&tmp_list); for (i = 0; i < min_reqs; i++) { /* Pre-allocate one backchannel rpc_rqst */ req = xprt_alloc_bc_req(xprt); From 1075e8e826b27aac92925c44d6d3c794e4d2ce0b Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 3 Feb 2026 09:09:40 +0800 Subject: [PATCH 59/60] nfs: nfs4proc: Convert comma to semicolon Replace comma between expressions with semicolons. Using a ',' in place of a ';' can have unintended side effects. Although that is not the case here, it is seems best to use ';' unless ',' is intended. Found by inspection. No functional change intended. Compile tested only. Signed-off-by: Chen Ni Reviewed-by: Jeff Layton Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 9c2ffe72cf33..180229320731 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5016,7 +5016,7 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg, if (same_parent) nfs_request_directory_delegation(same_parent); msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME]; - res->server = server, + res->server = server; nfs4_init_sequence(server->nfs_client, &arg->seq_args, &res->seq_res, 1, 0); } From dd2fdc3504592d85e549c523b054898a036a6afe Mon Sep 17 00:00:00 2001 From: Daniel Hodges Date: Fri, 6 Feb 2026 15:41:46 -0500 Subject: [PATCH 60/60] SUNRPC: fix gss_auth kref leak in gss_alloc_msg error path Commit 5940d1cf9f42 ("SUNRPC: Rebalance a kref in auth_gss.c") added a kref_get(&gss_auth->kref) call to balance the gss_put_auth() done in gss_release_msg(), but forgot to add a corresponding kref_put() on the error path when kstrdup_const() fails. If service_name is non-NULL and kstrdup_const() fails, the function jumps to err_put_pipe_version which calls put_pipe_version() and kfree(gss_msg), but never releases the gss_auth reference. This leads to a kref leak where the gss_auth structure is never freed. Add a forward declaration for gss_free_callback() and call kref_put() in the err_put_pipe_version error path to properly release the reference taken earlier. Fixes: 5940d1cf9f42 ("SUNRPC: Rebalance a kref in auth_gss.c") Cc: stable@vger.kernel.org Signed-off-by: Daniel Hodges Signed-off-by: Anna Schumaker --- net/sunrpc/auth_gss/auth_gss.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 5c095cb8cb20..bb3c3db2713b 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -39,6 +39,8 @@ static const struct rpc_authops authgss_ops; static const struct rpc_credops gss_credops; static const struct rpc_credops gss_nullops; +static void gss_free_callback(struct kref *kref); + #define GSS_RETRY_EXPIRED 5 static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; @@ -551,6 +553,7 @@ gss_alloc_msg(struct gss_auth *gss_auth, } return gss_msg; err_put_pipe_version: + kref_put(&gss_auth->kref, gss_free_callback); put_pipe_version(gss_auth->net); err_free_msg: kfree(gss_msg);