FAILED: patch "[PATCH] nfsd: dedup nfs4_client_to_reclaim inserts" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 9b57793863f69c608691c98e57c2f9be2db9dfed
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090325-triage-suitor-da06@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 9b57793863f69c608691c98e57c2f9be2db9dfed Mon Sep 17 00:00:00 2001
From: Jeff Layton <[email protected]>
Date: Sat, 30 May 2026 09:19:20 -0400
Subject: [PATCH] nfsd: dedup nfs4_client_to_reclaim inserts
nfs4_client_to_reclaim() unconditionally allocates a new nfs4_client_reclaim, prepends it to reclaim_str_hashtbl[], and bumps reclaim_str_hashtbl_size with no check for an existing entry for the same client name. After a reboot with a populated recovery directory that inflates the counter by one for every client that reclaims:
boot: load_recdir()
nfs4_client_to_reclaim(name) /* entry #1, size++ */
grace: RECLAIM_COMPLETE
__nfsd4_create_reclaim_record_grace()
nfs4_client_to_reclaim(name) /* entry #2, size++ */inc_reclaim_complete() ends the grace period early only when
atomic_inc_return(&nn->nr_reclaim_complete) ==
nn->reclaim_str_hashtbl_sizeWith reclaim_str_hashtbl_size at 2N and nr_reclaim_complete capped at N, the equality never holds and the fast end-of-grace path is dead. The grace period always runs out the full 90-second laundromat timer, and the shadow entry left in the hash table carries a dangling cr_clp for any reader that walks it.
Fix nfs4_client_to_reclaim() to look the name up with nfsd4_find_reclaim_client() first and, on a hit, fold the new princhash into the existing record (if it lacks one) and return that record without allocating or touching reclaim_str_hashtbl_size. On kmemdup() failure during the fold-in, return NULL so __cld_pipe_inprogress_downcall() surfaces -EFAULT to nfsdcld, matching the miss-path contract.
Add an rw_semaphore (reclaim_str_hashtbl_lock) to struct nfsd_net that serialises all access to reclaim_str_hashtbl[] and reclaim_str_hashtbl_size. Writers (nfs4_client_to_reclaim, nfs4_remove_reclaim_record callers) hold the write side; readers (nfsd4_cld_check*, inc_reclaim_complete, clients_still_reclaiming, nfs4_has_reclaimed_state, nfsd4_check_legacy_client) hold the read side. All call sites are in sleepable context, and none is a hot path, so the rwsem cost is negligible.
fs/nfsd/netns.h +5 -1
@@ -93,6 +93,7 @@ struct nfsd_net {*/ struct list_head *reclaim_str_hashtbl;int reclaim_str_hashtbl_size; +struct rw_semaphore reclaim_str_hashtbl_lock;struct list_head *conf_id_hashtbl;struct rb_root conf_name_tree;struct list_head *unconf_id_hashtbl;
@@ -105,7 +106,10 @@ struct nfsd_net {* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time * for last close replay. * -* All of the above fields are protected by the client_mutex. +* reclaim_str_hashtbl[], reclaim_str_hashtbl_size are protected by +* reclaim_str_hashtbl_lock. +* +* All of the remaining fields are protected by the client_lock. */ struct list_head client_lru;struct list_head close_lru;
fs/nfsd/nfs4recover.c +29 -7
@@ -285,10 +285,12 @@ __nfsd4_remove_reclaim_record_grace(const char *dname, int len,return ; } name.len = len; +down_write (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (name, nn); -kfree (name.data);if (crp)nfs4_remove_reclaim_record (crp, nn); +up_write (&nn->reclaim_str_hashtbl_lock); +kfree (name.data); } static void
@@ -484,6 +486,7 @@ nfs4_legacy_state_init(struct net *net)for (i =0 ; i <CLIENT_HASH_SIZE ; i++)INIT_LIST_HEAD (&nn->reclaim_str_hashtbl[i]); nn->reclaim_str_hashtbl_size =0 ; +init_rwsem (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -598,13 +601,16 @@ nfsd4_check_legacy_client(struct nfs4_client *clp) goto out_enoent; } name.len =HEXDIR_LEN ; +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (name, nn); -kfree (name.data);if (crp) {set_bit (NFSD4_CLIENT_STABLE , &clp->cl_flags); crp->cr_clp = clp; -return 0 ; } +up_read (&nn->reclaim_str_hashtbl_lock); +kfree (name.data); +if (crp) +return 0 ; out_enoent:return -ENOENT ;
@@ -1176,6 +1182,7 @@ nfsd4_cld_check(struct nfs4_client *clp)return 0 ;/* look for it in the reclaim hashtable otherwise */ +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (clp->cl_name, nn);if (crp) goto found;
@@ -1191,6 +1198,7 @@ nfsd4_cld_check(struct nfs4_client *clp)if (!name.data) {dprintk ("%s: failed to allocate memory for name.data!\n" , __func__); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; } name.len =HEXDIR_LEN ;
@@ -1201,9 +1209,11 @@ nfsd4_cld_check(struct nfs4_client *clp) }#endif +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; found: crp->cr_clp = clp; +up_read (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -1215,6 +1225,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)struct cld_net *cn = nn->cld_net;#endif struct nfs4_client_reclaim *crp; +unsigned int princhashlen;char *principal =NULL ;/* did we already find that this client is stable? */
@@ -1222,6 +1233,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)return 0 ;/* look for it in the reclaim hashtable otherwise */ +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (clp->cl_name, nn);if (crp) goto found;
@@ -1237,6 +1249,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)if (!name.data) {dprintk ("%s: failed to allocate memory for name.data\n" , __func__); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; } name.len =HEXDIR_LEN ;
@@ -1247,23 +1260,31 @@ nfsd4_cld_check_v2(struct nfs4_client *clp) }#endif +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; found: -if (crp->cr_princhash.len) { + princhashlen = crp->cr_princhash.len; +if (princhashlen) {u8 digest[SHA256_DIGEST_SIZE ]; +u8 *pdata;if (clp->cl_cred.cr_raw_principal) principal = clp->cl_cred.cr_raw_principal;else if (clp->cl_cred.cr_principal) principal = clp->cl_cred.cr_principal; -if (principal ==NULL ) +if (principal ==NULL ) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + }sha256 (principal,strlen (principal), digest); -if (memcmp (crp->cr_princhash.data, digest, - crp->cr_princhash.len)) + pdata = crp->cr_princhash.data; +if (memcmp (pdata, digest, princhashlen)) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } } crp->cr_clp = clp; +up_read (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -1362,6 +1383,7 @@ nfs4_cld_state_init(struct net *net)for (i =0 ; i <CLIENT_HASH_SIZE ; i++)INIT_LIST_HEAD (&nn->reclaim_str_hashtbl[i]); nn->reclaim_str_hashtbl_size =0 ; +init_rwsem (&nn->reclaim_str_hashtbl_lock);set_bit (NFSD_NET_TRACK_RECLAIM_COMPLETES , &nn->flags);atomic_set (&nn->nr_reclaim_complete,0 );
fs/nfsd/nfs4state.c +55 -6
122 lines not on this page · raw message
Sasha Levin
maintainerPATCH 1/1 nfsd: dedup nfs4_client_to_reclaim inserts
From: Jeff Layton <[email protected]>
[ Upstream commit 9b57793863f69c608691c98e57c2f9be2db9dfed ]
nfs4_client_to_reclaim() unconditionally allocates a new nfs4_client_reclaim, prepends it to reclaim_str_hashtbl[], and bumps reclaim_str_hashtbl_size with no check for an existing entry for the same client name. After a reboot with a populated recovery directory that inflates the counter by one for every client that reclaims:
boot: load_recdir()
nfs4_client_to_reclaim(name) /* entry #1, size++ */
grace: RECLAIM_COMPLETE
__nfsd4_create_reclaim_record_grace()
nfs4_client_to_reclaim(name) /* entry #2, size++ */inc_reclaim_complete() ends the grace period early only when
atomic_inc_return(&nn->nr_reclaim_complete) ==
nn->reclaim_str_hashtbl_sizeWith reclaim_str_hashtbl_size at 2N and nr_reclaim_complete capped at N, the equality never holds and the fast end-of-grace path is dead. The grace period always runs out the full 90-second laundromat timer, and the shadow entry left in the hash table carries a dangling cr_clp for any reader that walks it.
Fix nfs4_client_to_reclaim() to look the name up with nfsd4_find_reclaim_client() first and, on a hit, fold the new princhash into the existing record (if it lacks one) and return that record without allocating or touching reclaim_str_hashtbl_size. On kmemdup() failure during the fold-in, return NULL so __cld_pipe_inprogress_downcall() surfaces -EFAULT to nfsdcld, matching the miss-path contract.
Add an rw_semaphore (reclaim_str_hashtbl_lock) to struct nfsd_net that serialises all access to reclaim_str_hashtbl[] and reclaim_str_hashtbl_size. Writers (nfs4_client_to_reclaim, nfs4_remove_reclaim_record callers) hold the write side; readers (nfsd4_cld_check*, inc_reclaim_complete, clients_still_reclaiming, nfs4_has_reclaimed_state, nfsd4_check_legacy_client) hold the read side. All call sites are in sleepable context, and none is a hot path, so the rwsem cost is negligible.
--- fs/nfsd/netns.h | 6 ++++- fs/nfsd/nfs4recover.c | 39 ++++++++++++++++++++++----- fs/nfsd/nfs4state.c | 61 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 92 insertions(+), 14 deletions(-)
fs/nfsd/netns.h +5 -1
@@ -84,6 +84,7 @@ struct nfsd_net {*/ struct list_head *reclaim_str_hashtbl;int reclaim_str_hashtbl_size; +struct rw_semaphore reclaim_str_hashtbl_lock;struct list_head *conf_id_hashtbl;struct rb_root conf_name_tree;struct list_head *unconf_id_hashtbl;
@@ -96,7 +97,10 @@ struct nfsd_net {* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time * for last close replay. * -* All of the above fields are protected by the client_mutex. +* reclaim_str_hashtbl[], reclaim_str_hashtbl_size are protected by +* reclaim_str_hashtbl_lock. +* +* All of the remaining fields are protected by the client_lock. */ struct list_head client_lru;struct list_head close_lru;
fs/nfsd/nfs4recover.c +32 -7
@@ -355,10 +355,12 @@ __nfsd4_remove_reclaim_record_grace(const char *dname, int len,return ; } name.len = len; +down_write (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (name, nn); -kfree (name.data);if (crp)nfs4_remove_reclaim_record (crp, nn); +up_write (&nn->reclaim_str_hashtbl_lock); +kfree (name.data); } static void
@@ -559,6 +561,7 @@ nfs4_legacy_state_init(struct net *net)for (i =0 ; i <CLIENT_HASH_SIZE ; i++)INIT_LIST_HEAD (&nn->reclaim_str_hashtbl[i]); nn->reclaim_str_hashtbl_size =0 ; +init_rwsem (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -678,13 +681,16 @@ nfsd4_check_legacy_client(struct nfs4_client *clp) goto out_enoent; } name.len =HEXDIR_LEN ; +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (name, nn); -kfree (name.data);if (crp) {set_bit (NFSD4_CLIENT_STABLE , &clp->cl_flags); crp->cr_clp = clp; -return 0 ; } +up_read (&nn->reclaim_str_hashtbl_lock); +kfree (name.data); +if (crp) +return 0 ; out_enoent:return -ENOENT ;
@@ -1285,6 +1291,7 @@ nfsd4_cld_check(struct nfs4_client *clp)return 0 ;/* look for it in the reclaim hashtable otherwise */ +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (clp->cl_name, nn);if (crp) goto found;
@@ -1296,13 +1303,16 @@ nfsd4_cld_check(struct nfs4_client *clp)struct xdr_netobj name; status =nfs4_make_rec_clidname (dname, &clp->cl_name); -if (status) +if (status) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } name.data =kmemdup (dname,HEXDIR_LEN ,GFP_KERNEL );if (!name.data) {dprintk ("%s: failed to allocate memory for name.data!\n" , __func__); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; } name.len =HEXDIR_LEN ;
@@ -1313,9 +1323,11 @@ nfsd4_cld_check(struct nfs4_client *clp) }#endif +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; found: crp->cr_clp = clp; +up_read (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -1335,6 +1347,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)return 0 ;/* look for it in the reclaim hashtable otherwise */ +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (clp->cl_name, nn);if (crp) goto found;
@@ -1345,13 +1358,16 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)char dname[HEXDIR_LEN ]; status =nfs4_make_rec_clidname (dname, &clp->cl_name); -if (status) +if (status) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } name.data =kmemdup (dname,HEXDIR_LEN ,GFP_KERNEL );if (!name.data) {dprintk ("%s: failed to allocate memory for name.data\n" , __func__); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; } name.len =HEXDIR_LEN ;
@@ -1362,6 +1378,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp) }#endif +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; found:if (crp->cr_princhash.len) {
@@ -1369,26 +1386,33 @@ nfsd4_cld_check_v2(struct nfs4_client *clp) principal = clp->cl_cred.cr_raw_principal; else if (clp->cl_cred.cr_principal) principal = clp->cl_cred.cr_principal; -if (principal ==NULL ) +if (principal ==NULL ) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } cksum.len =crypto_shash_digestsize (tfm); cksum.data =kmalloc (cksum.len,GFP_KERNEL ); -if (cksum.data ==NULL ) +if (cksum.data ==NULL ) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } status =crypto_shash_tfm_digest (tfm, principal,strlen (principal), cksum.data);if (status) {kfree (cksum.data); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; }if (memcmp (crp->cr_princhash.data, cksum.data, crp->cr_princhash.len)) {kfree (cksum.data); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; }kfree (cksum.data); } crp->cr_clp = clp; +up_read (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -1488,6 +1512,7 @@ nfs4_cld_state_init(struct net *net)for (i =0 ; i <CLIENT_HASH_SIZE ; i++)INIT_LIST_HEAD (&nn->reclaim_str_hashtbl[i]); nn->reclaim_str_hashtbl_size =0 ; +init_rwsem (&nn->reclaim_str_hashtbl_lock); nn->track_reclaim_completes = true;atomic_set (&nn->nr_reclaim_complete,0 );
fs/nfsd/nfs4state.c +55 -6
122 lines not on this page · raw message
Signature · 1 line
2.53.0