FAILED: patch "[PATCH] nfsd: dedup nfs4_client_to_reclaim inserts" failed to apply to 6.1-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.1-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.1.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 '2026090327-fruit-impish-82f7@gregkh' --subject-prefix 'PATCH 6.1.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/2 NFSD: net ref data still needs to be freed even if net hasn't startup
From: Edward Adam Davis <[email protected]>
[ Upstream commit 0b88bfa42e5468baff71909c2f324a495318532b ]
When the NFSD instance doesn't to startup, the net ref data memory is not properly reclaimed, which triggers the memory leak issue reported by syzbot [1].
To avoid the problem reported in [1], the net ref data memory reclamation action is moved outside of nfsd_net_up when the net is shutdown.
[1]
unreferenced object 0xffff88812a39dfc0 (size 64):
backtrace (crc a2262fc6):
percpu_ref_init+0x94/0x1e0 lib/percpu-refcount.c:76
nfsd_create_serv+0xbe/0x260 fs/nfsd/nfssvc.c:605
nfsd_nl_listener_set_doit+0x62/0xb00 fs/nfsd/nfsctl.c:1882
genl_family_rcv_msg_doit+0x11e/0x190 net/netlink/genetlink.c:1115
genl_family_rcv_msg net/netlink/genetlink.c:1195 [inline]
genl_rcv_msg+0x2fd/0x440 net/netlink/genetlink.c:1210
BUG: memory leakStable-dep-of: 9b57793863f6 ("nfsd: dedup nfs4_client_to_reclaim inserts")
--- fs/nfsd/Kconfig | 2 +- fs/nfsd/netns.h | 19 +++++---- fs/nfsd/nfs4proc.c | 2 +- fs/nfsd/nfs4recover.c | 94 ++++++++++++++++--------------------------- fs/nfsd/nfs4state.c | 41 +++++++++++-------- fs/nfsd/nfsctl.c | 2 +- fs/nfsd/nfssvc.c | 20 ++++----- 7 files changed, 83 insertions(+), 97 deletions(-)
fs/nfsd/Kconfig +1 -1
@@ -77,8 +77,8 @@ config NFSD_V4 select FS_POSIX_ACL select SUNRPC_GSS select CRYPTO + select CRYPTO_LIB_SHA256 select CRYPTO_MD5 - select CRYPTO_SHA256 select GRACE_PERIOD select NFS_V4_2_SSC_HELPER if NFS_V4_2 help
fs/nfsd/netns.h +11 -8
@@ -25,6 +25,16 @@struct cld_net ;struct nfsd4_client_tracking_ops ; +enum nfsd_net_flag { +NFSD_NET_GRACE_ENDED , +NFSD_NET_GRACE_END_FORCED , +NFSD_NET_IN_GRACE , +NFSD_NET_SOMEBODY_RECLAIMED , +NFSD_NET_TRACK_RECLAIM_COMPLETES , +NFSD_NET_UP , +NFSD_NET_LOCKD_UP , +}; +enum {/* cache misses due only to checksum comparison failures */ NFSD_STATS_PAYLOAD_MISSES ,
@@ -63,8 +73,7 @@ struct nfsd_net {struct cache_detail *nametoid_cache;struct lock_manager nfsd4_manager; -bool grace_ended; -bool grace_end_forced; +unsigned long flags;bool client_tracking_active;time64_t boot_time;
@@ -112,19 +121,13 @@ struct nfsd_net {spinlock_t blocked_locks_lock;struct file *rec_file; -bool in_grace;const struct nfsd4_client_tracking_ops *client_tracking_ops;time64_t nfsd4_lease;time64_t nfsd4_grace; -bool somebody_reclaimed; -bool track_reclaim_completes;atomic_t nr_reclaim_complete; -bool nfsd_net_up; -bool lockd_up; -seqlock_t writeverf_lock;unsigned char writeverf[8 ];
fs/nfsd/nfs4proc.c +1 -1
@@ -592,7 +592,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,pr_warn ("nfsd4_process_open2 failed to open newly-created file: status=%u\n" ,be32_to_cpu (status));if (reclaim && !status) - nn->somebody_reclaimed = true; +set_bit (NFSD_NET_SOMEBODY_RECLAIMED , &nn->flags); out:if (open->op_filp) {fput (open->op_filp);
fs/nfsd/nfs4recover.c +34 -60
230 lines not on this page · raw message
fs/nfsd/nfs4state.c +25 -16
100 lines not on this page · raw message
fs/nfsd/nfsctl.c +1 -1
@@ -1124,7 +1124,7 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size) }return scnprintf (buf,SIMPLE_TRANSACTION_LIMIT ,"%c\n" , - nn->grace_ended ?'Y' :'N' ); +test_bit (NFSD_NET_GRACE_ENDED , &nn->flags) ?'Y' :'N' ); }#endif
fs/nfsd/nfssvc.c +10 -10
@@ -402,7 +402,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)struct nfsd_net *nn =net_generic (net, nfsd_net_id);int ret; -if (nn->nfsd_net_up) +if (test_bit (NFSD_NET_UP , &nn->flags))return 0 ; ret =nfsd_startup_generic ();
@@ -412,11 +412,11 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)if (ret) goto out_socks; -if (nfsd_needs_lockd (nn) && !nn->lockd_up) { +if (nfsd_needs_lockd (nn) && !test_bit (NFSD_NET_LOCKD_UP , &nn->flags)) { ret =lockd_up (net, cred);if (ret) goto out_socks; - nn->lockd_up = true; +set_bit (NFSD_NET_LOCKD_UP , &nn->flags); } ret =nfsd_file_cache_start_net (net);
@@ -434,7 +434,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)if (ret) goto out_reply_cache; - nn->nfsd_net_up = true; +set_bit (NFSD_NET_UP , &nn->flags);return 0 ; out_reply_cache:
@@ -442,9 +442,9 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred) out_filecache:nfsd_file_cache_shutdown_net (net); out_lockd: -if (nn->lockd_up) { +if (test_bit (NFSD_NET_LOCKD_UP , &nn->flags)) {lockd_down (net); - nn->lockd_up = false; +clear_bit (NFSD_NET_LOCKD_UP , &nn->flags); } out_socks:nfsd_shutdown_generic ();
@@ -458,11 +458,11 @@ static void nfsd_shutdown_net(struct net *net)nfs4_state_shutdown_net (net);nfsd_reply_cache_shutdown (nn);nfsd_file_cache_shutdown_net (net); -if (nn->lockd_up) { +if (test_bit (NFSD_NET_LOCKD_UP , &nn->flags)) {lockd_down (net); - nn->lockd_up = false; +clear_bit (NFSD_NET_LOCKD_UP , &nn->flags); } - nn->nfsd_net_up = false; +clear_bit (NFSD_NET_UP , &nn->flags);nfsd_shutdown_generic (); }
@@ -558,7 +558,7 @@ void nfsd_last_thread(struct net *net)* other initialization has been done except the rpcb information. */ svc_rpcb_cleanup (serv, net); -if (!nn->nfsd_net_up) +if (!test_bit (NFSD_NET_UP , &nn->flags))return ;nfsd_shutdown_net (net);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 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 | 36 ++++++++++++++++++++----- fs/nfsd/nfs4state.c | 61 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 89 insertions(+), 14 deletions(-)
fs/nfsd/netns.h +5 -1
@@ -91,6 +91,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;
@@ -103,7 +104,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
@@ -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 ;
@@ -1263,6 +1269,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;
@@ -1282,6 +1289,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 ;
@@ -1292,9 +1300,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 ; }
@@ -1306,6 +1316,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? */
@@ -1313,6 +1324,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;
@@ -1331,6 +1343,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 ;
@@ -1341,23 +1354,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 ; }
@@ -1457,6 +1478,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
Signature · 1 line
2.53.0