From: Sasha Levin Date: Wed, 8 Jul 2026 12:04:39 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e9db4b48651cc1839d555af84c8a8b7552d980c1;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch b/queue-5.10/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch new file mode 100644 index 0000000000..04b211e51c --- /dev/null +++ b/queue-5.10/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch @@ -0,0 +1,218 @@ +From d13e59aca2e1a167bd79c398ca888bf2d0b432da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Sep 2025 11:38:33 +1000 +Subject: nfsd: change nfs4_client_to_reclaim() to allocate data + +From: NeilBrown + +[ Upstream commit 4552f4e3f2c96597914f07b060d5c5db84420ddd ] + +The calling convention for nfs4_client_to_reclaim() is clumsy in that +the caller needs to free memory if the function fails. It is much +cleaner if the function frees its own memory. + +This patch changes nfs4_client_to_reclaim() to re-allocate the .data +fields to be stored in the newly allocated struct nfs4_client_reclaim, +and to free everything on failure. + +__cld_pipe_inprogress_downcall() needs to allocate the data anyway to +copy it from user-space, so now that data is allocated twice. I think +that is a small price to pay for a cleaner interface. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 67 +++++++++++++++---------------------------- + fs/nfsd/nfs4state.c | 22 ++++++++++++-- + 2 files changed, 42 insertions(+), 47 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index c74c4023770694..8ff61f6184e24d 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -167,24 +167,13 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error) + + static void + __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, +- const char *dname, int len, struct nfsd_net *nn) ++ char *dname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = strlen(dname), .data = dname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + struct nfs4_client_reclaim *crp; + +- name.data = kmemdup(dname, len, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- return; +- } +- name.len = len; + crp = nfs4_client_to_reclaim(name, princhash, nn); +- if (!crp) { +- kfree(name.data); +- return; +- } + crp->cr_clp = clp; + } + +@@ -240,8 +229,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + inode_unlock(d_inode(dir)); + if (status == 0) { + if (nn->in_grace) +- __nfsd4_create_reclaim_record_grace(clp, dname, +- HEXDIR_LEN, nn); ++ __nfsd4_create_reclaim_record_grace(clp, dname, nn); + vfs_fsync(nn->rec_file, 0); + } else { + printk(KERN_ERR "NFSD: failed to write recovery record" +@@ -478,7 +466,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + static int + load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + + if (strlen(cname) != HEXDIR_LEN - 1) { +@@ -487,16 +475,7 @@ load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kstrdup(cname, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- goto out; +- } +- name.len = HEXDIR_LEN; +- if (!nfs4_client_to_reclaim(name, princhash, nn)) +- kfree(name.data); +-out: ++ nfs4_client_to_reclaim(name, princhash, nn); + return 0; + } + +@@ -792,6 +771,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + { + uint8_t cmd, princhashlen; + struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; ++ char *namecopy __free(kfree) = NULL; ++ char *princhashcopy __free(kfree) = NULL; + uint16_t namelen; + struct cld_net *cn = nn->cld_net; + +@@ -810,20 +791,20 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: namelen should not be zero", __func__); + return -EINVAL; + } +- name.data = memdup_user(&ci->cc_name.cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&ci->cc_name.cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; + if (princhashlen > 0) { +- princhash.data = memdup_user( +- &ci->cc_princhash.cp_data, +- princhashlen); +- if (IS_ERR(princhash.data)) { +- kfree(name.data); +- return PTR_ERR(princhash.data); +- } ++ princhashcopy = memdup_user( ++ &ci->cc_princhash.cp_data, ++ princhashlen); ++ if (IS_ERR(princhashcopy)) ++ return PTR_ERR(princhashcopy); ++ princhash.data = princhashcopy; + princhash.len = princhashlen; + } else + princhash.len = 0; +@@ -837,23 +818,21 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: namelen should not be zero", __func__); + return -EINVAL; + } +- name.data = memdup_user(&cnm->cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&cnm->cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + } + #ifdef CONFIG_NFSD_V4 + if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { + name.len = name.len - 5; +- memmove(name.data, name.data + 5, name.len); ++ name.data = name.data + 5; + cn->cn_has_legacy = true; + } + #endif +- if (!nfs4_client_to_reclaim(name, princhash, nn)) { +- kfree(name.data); +- kfree(princhash.data); ++ if (!nfs4_client_to_reclaim(name, princhash, nn)) + return -EFAULT; +- } + return nn->client_tracking_ops->msglen; + } + return -EFAULT; +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index 6b34bfac6194bb..c61e1a4ab620cc 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -8037,9 +8037,6 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn) + + /* + * failure => all reset bets are off, nfserr_no_grace... +- * +- * The caller is responsible for freeing name.data if NULL is returned (it +- * will be freed in nfs4_remove_reclaim_record in the normal case). + */ + struct nfs4_client_reclaim * + nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, +@@ -8048,6 +8045,22 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + unsigned int strhashval; + struct nfs4_client_reclaim *crp; + ++ name.data = kmemdup(name.data, name.len, GFP_KERNEL); ++ if (!name.data) { ++ dprintk("%s: failed to allocate memory for name.data!\n", ++ __func__); ++ return NULL; ++ } ++ if (princhash.len) { ++ princhash.data = kmemdup(princhash.data, princhash.len, GFP_KERNEL); ++ if (!princhash.data) { ++ dprintk("%s: failed to allocate memory for princhash.data!\n", ++ __func__); ++ kfree(name.data); ++ return NULL; ++ } ++ } else ++ princhash.data = NULL; + crp = alloc_reclaim(); + if (crp) { + strhashval = clientstr_hashval(name); +@@ -8059,6 +8072,9 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + crp->cr_princhash.len = princhash.len; + crp->cr_clp = NULL; + nn->reclaim_str_hashtbl_size++; ++ } else { ++ kfree(name.data); ++ kfree(princhash.data); + } + return crp; + } +-- +2.53.0 + diff --git a/queue-5.10/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch b/queue-5.10/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch new file mode 100644 index 0000000000..85d3cd5b52 --- /dev/null +++ b/queue-5.10/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch @@ -0,0 +1,154 @@ +From 3c83fd41ed6deb900cfcad7444540c105c0e3c80 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 15 Sep 2025 12:55:13 +1000 +Subject: nfsd: move name lookup out of nfsd4_list_rec_dir() + +From: NeilBrown + +[ Upstream commit 89bd77cf436bf25e448817a662ebf76515f22863 ] + +nfsd4_list_rec_dir() is called with two different callbacks. +One of the callbacks uses vfs_rmdir() to remove the directory. +The other doesn't use the dentry at all, just the name. + +As only one callback needs the dentry, this patch moves the lookup into +that callback. This prepares of changes to how directory operations +are locked. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 55 ++++++++++++++++++++++--------------------- + 1 file changed, 28 insertions(+), 27 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 8ea55b2621ee41..c74c4023770694 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -254,7 +254,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + nfs4_reset_creds(original_cred); + } + +-typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); ++typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *); + + struct name_list { + char name[HEXDIR_LEN]; +@@ -308,23 +308,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) + } + + status = iterate_dir(nn->rec_file, &ctx.ctx); +- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +- if (!status) { +- struct dentry *dentry; +- dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1); +- if (IS_ERR(dentry)) { +- status = PTR_ERR(dentry); +- break; +- } +- status = f(dir, dentry, nn); +- dput(dentry); +- } ++ if (!status) ++ status = f(dir, entry->name, nn); ++ + list_del(&entry->list); + kfree(entry); + } +- inode_unlock(d_inode(dir)); + nfs4_reset_creds(original_cred); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +@@ -422,18 +413,19 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) + } + + static int +-purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + int status; ++ struct dentry *child; + struct xdr_netobj name; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -443,10 +435,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) + if (nfs4_has_reclaimed_state(name, nn)) + goto out_free; + +- status = vfs_rmdir(d_inode(parent), child); +- if (status) +- printk("failed to remove client recovery directory %pd\n", +- child); ++ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); ++ child = lookup_one_len(cname, parent, HEXDIR_LEN - 1); ++ if (!IS_ERR(child)) { ++ status = vfs_rmdir(d_inode(parent), child); ++ if (status) ++ printk("failed to remove client recovery directory %pd\n", ++ child); ++ dput(child); ++ } ++ inode_unlock(d_inode(parent)); ++ + out_free: + kfree(name.data); + out: +@@ -477,18 +476,18 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + } + + static int +-load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + struct xdr_netobj name; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -843,11 +842,13 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + return PTR_ERR(name.data); + name.len = namelen; + } ++#ifdef CONFIG_NFSD_V4 + if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { + name.len = name.len - 5; + memmove(name.data, name.data + 5, name.len); + cn->cn_has_legacy = true; + } ++#endif + if (!nfs4_client_to_reclaim(name, princhash, nn)) { + kfree(name.data); + kfree(princhash.data); +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index c6c484d9c3..990bb0dcb2 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -3,3 +3,5 @@ nfsd-release-layout-stid-on-setlease-failure.patch nfsd-reset-write-verifier-on-deferred-writeback-erro.patch userfaultfd-gate-must_wait-writability-check-on-pte_.patch clk-imx-add-check-for-kcalloc.patch +nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch +nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch diff --git a/queue-5.15/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch b/queue-5.15/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch new file mode 100644 index 0000000000..aa013665a3 --- /dev/null +++ b/queue-5.15/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch @@ -0,0 +1,218 @@ +From 2d47b2334b3baecf9a04ccb8563696184d8e22da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Sep 2025 11:38:33 +1000 +Subject: nfsd: change nfs4_client_to_reclaim() to allocate data + +From: NeilBrown + +[ Upstream commit 4552f4e3f2c96597914f07b060d5c5db84420ddd ] + +The calling convention for nfs4_client_to_reclaim() is clumsy in that +the caller needs to free memory if the function fails. It is much +cleaner if the function frees its own memory. + +This patch changes nfs4_client_to_reclaim() to re-allocate the .data +fields to be stored in the newly allocated struct nfs4_client_reclaim, +and to free everything on failure. + +__cld_pipe_inprogress_downcall() needs to allocate the data anyway to +copy it from user-space, so now that data is allocated twice. I think +that is a small price to pay for a cleaner interface. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 67 +++++++++++++++---------------------------- + fs/nfsd/nfs4state.c | 22 ++++++++++++-- + 2 files changed, 42 insertions(+), 47 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 52984348ba3995..d13e512149eede 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -167,24 +167,13 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error) + + static void + __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, +- const char *dname, int len, struct nfsd_net *nn) ++ char *dname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = strlen(dname), .data = dname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + struct nfs4_client_reclaim *crp; + +- name.data = kmemdup(dname, len, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- return; +- } +- name.len = len; + crp = nfs4_client_to_reclaim(name, princhash, nn); +- if (!crp) { +- kfree(name.data); +- return; +- } + crp->cr_clp = clp; + } + +@@ -240,8 +229,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + inode_unlock(d_inode(dir)); + if (status == 0) { + if (nn->in_grace) +- __nfsd4_create_reclaim_record_grace(clp, dname, +- HEXDIR_LEN, nn); ++ __nfsd4_create_reclaim_record_grace(clp, dname, nn); + vfs_fsync(nn->rec_file, 0); + } else { + printk(KERN_ERR "NFSD: failed to write recovery record" +@@ -478,7 +466,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + static int + load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + + if (strlen(cname) != HEXDIR_LEN - 1) { +@@ -487,16 +475,7 @@ load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kstrdup(cname, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- goto out; +- } +- name.len = HEXDIR_LEN; +- if (!nfs4_client_to_reclaim(name, princhash, nn)) +- kfree(name.data); +-out: ++ nfs4_client_to_reclaim(name, princhash, nn); + return 0; + } + +@@ -792,6 +771,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + { + uint8_t cmd, princhashlen; + struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; ++ char *namecopy __free(kfree) = NULL; ++ char *princhashcopy __free(kfree) = NULL; + uint16_t namelen; + struct cld_net *cn = nn->cld_net; + +@@ -810,20 +791,20 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: namelen should not be zero", __func__); + return -EINVAL; + } +- name.data = memdup_user(&ci->cc_name.cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&ci->cc_name.cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; + if (princhashlen > 0) { +- princhash.data = memdup_user( +- &ci->cc_princhash.cp_data, +- princhashlen); +- if (IS_ERR(princhash.data)) { +- kfree(name.data); +- return PTR_ERR(princhash.data); +- } ++ princhashcopy = memdup_user( ++ &ci->cc_princhash.cp_data, ++ princhashlen); ++ if (IS_ERR(princhashcopy)) ++ return PTR_ERR(princhashcopy); ++ princhash.data = princhashcopy; + princhash.len = princhashlen; + } else + princhash.len = 0; +@@ -837,23 +818,21 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: namelen should not be zero", __func__); + return -EINVAL; + } +- name.data = memdup_user(&cnm->cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&cnm->cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + } + #ifdef CONFIG_NFSD_V4 + if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { + name.len = name.len - 5; +- memmove(name.data, name.data + 5, name.len); ++ name.data = name.data + 5; + cn->cn_has_legacy = true; + } + #endif +- if (!nfs4_client_to_reclaim(name, princhash, nn)) { +- kfree(name.data); +- kfree(princhash.data); ++ if (!nfs4_client_to_reclaim(name, princhash, nn)) + return -EFAULT; +- } + return nn->client_tracking_ops->msglen; + } + return -EFAULT; +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index 86e3bf53440e38..e6c0b1520973e9 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -8028,9 +8028,6 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn) + + /* + * failure => all reset bets are off, nfserr_no_grace... +- * +- * The caller is responsible for freeing name.data if NULL is returned (it +- * will be freed in nfs4_remove_reclaim_record in the normal case). + */ + struct nfs4_client_reclaim * + nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, +@@ -8039,6 +8036,22 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + unsigned int strhashval; + struct nfs4_client_reclaim *crp; + ++ name.data = kmemdup(name.data, name.len, GFP_KERNEL); ++ if (!name.data) { ++ dprintk("%s: failed to allocate memory for name.data!\n", ++ __func__); ++ return NULL; ++ } ++ if (princhash.len) { ++ princhash.data = kmemdup(princhash.data, princhash.len, GFP_KERNEL); ++ if (!princhash.data) { ++ dprintk("%s: failed to allocate memory for princhash.data!\n", ++ __func__); ++ kfree(name.data); ++ return NULL; ++ } ++ } else ++ princhash.data = NULL; + crp = alloc_reclaim(); + if (crp) { + strhashval = clientstr_hashval(name); +@@ -8050,6 +8063,9 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + crp->cr_princhash.len = princhash.len; + crp->cr_clp = NULL; + nn->reclaim_str_hashtbl_size++; ++ } else { ++ kfree(name.data); ++ kfree(princhash.data); + } + return crp; + } +-- +2.53.0 + diff --git a/queue-5.15/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch b/queue-5.15/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch new file mode 100644 index 0000000000..b1bad55bd8 --- /dev/null +++ b/queue-5.15/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch @@ -0,0 +1,154 @@ +From 49e851bd264042ac2500fe199aac08233f65e463 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 15 Sep 2025 12:55:13 +1000 +Subject: nfsd: move name lookup out of nfsd4_list_rec_dir() + +From: NeilBrown + +[ Upstream commit 89bd77cf436bf25e448817a662ebf76515f22863 ] + +nfsd4_list_rec_dir() is called with two different callbacks. +One of the callbacks uses vfs_rmdir() to remove the directory. +The other doesn't use the dentry at all, just the name. + +As only one callback needs the dentry, this patch moves the lookup into +that callback. This prepares of changes to how directory operations +are locked. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 55 ++++++++++++++++++++++--------------------- + 1 file changed, 28 insertions(+), 27 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index b8ea2bdfbcfbf5..52984348ba3995 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -254,7 +254,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + nfs4_reset_creds(original_cred); + } + +-typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); ++typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *); + + struct name_list { + char name[HEXDIR_LEN]; +@@ -308,23 +308,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) + } + + status = iterate_dir(nn->rec_file, &ctx.ctx); +- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +- if (!status) { +- struct dentry *dentry; +- dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1); +- if (IS_ERR(dentry)) { +- status = PTR_ERR(dentry); +- break; +- } +- status = f(dir, dentry, nn); +- dput(dentry); +- } ++ if (!status) ++ status = f(dir, entry->name, nn); ++ + list_del(&entry->list); + kfree(entry); + } +- inode_unlock(d_inode(dir)); + nfs4_reset_creds(original_cred); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +@@ -422,18 +413,19 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) + } + + static int +-purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + int status; ++ struct dentry *child; + struct xdr_netobj name; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -443,10 +435,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) + if (nfs4_has_reclaimed_state(name, nn)) + goto out_free; + +- status = vfs_rmdir(&init_user_ns, d_inode(parent), child); +- if (status) +- printk("failed to remove client recovery directory %pd\n", +- child); ++ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); ++ child = lookup_one_len(cname, parent, HEXDIR_LEN - 1); ++ if (!IS_ERR(child)) { ++ status = vfs_rmdir(&init_user_ns, d_inode(parent), child); ++ if (status) ++ printk("failed to remove client recovery directory %pd\n", ++ child); ++ dput(child); ++ } ++ inode_unlock(d_inode(parent)); ++ + out_free: + kfree(name.data); + out: +@@ -477,18 +476,18 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + } + + static int +-load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + struct xdr_netobj name; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -843,11 +842,13 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + return PTR_ERR(name.data); + name.len = namelen; + } ++#ifdef CONFIG_NFSD_V4 + if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { + name.len = name.len - 5; + memmove(name.data, name.data + 5, name.len); + cn->cn_has_legacy = true; + } ++#endif + if (!nfs4_client_to_reclaim(name, princhash, nn)) { + kfree(name.data); + kfree(princhash.data); +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index 9c069ffbd9..f06ab4c903 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -11,3 +11,5 @@ slimbus-qcom-ngd-ctrl-fix-probe-error-path-ordering.patch slimbus-qcom-ngd-ctrl-correct-pdr-and-ssr-cleanup-ow.patch slimbus-convert-to-platform-remove-callback-returnin.patch slimbus-qcom-ngd-ctrl-register-callbacks-after-creat.patch +nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch +nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch diff --git a/queue-6.1/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch b/queue-6.1/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch new file mode 100644 index 0000000000..0bb578e34e --- /dev/null +++ b/queue-6.1/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch @@ -0,0 +1,218 @@ +From 0e6ced058fed27806e5dc705143487c2949129cc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Sep 2025 11:38:33 +1000 +Subject: nfsd: change nfs4_client_to_reclaim() to allocate data + +From: NeilBrown + +[ Upstream commit 4552f4e3f2c96597914f07b060d5c5db84420ddd ] + +The calling convention for nfs4_client_to_reclaim() is clumsy in that +the caller needs to free memory if the function fails. It is much +cleaner if the function frees its own memory. + +This patch changes nfs4_client_to_reclaim() to re-allocate the .data +fields to be stored in the newly allocated struct nfs4_client_reclaim, +and to free everything on failure. + +__cld_pipe_inprogress_downcall() needs to allocate the data anyway to +copy it from user-space, so now that data is allocated twice. I think +that is a small price to pay for a cleaner interface. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 67 +++++++++++++++---------------------------- + fs/nfsd/nfs4state.c | 22 ++++++++++++-- + 2 files changed, 42 insertions(+), 47 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 3ad9da783eb686..4f1f4d2c6bed1f 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -167,24 +167,13 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error) + + static void + __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, +- const char *dname, int len, struct nfsd_net *nn) ++ char *dname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = strlen(dname), .data = dname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + struct nfs4_client_reclaim *crp; + +- name.data = kmemdup(dname, len, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- return; +- } +- name.len = len; + crp = nfs4_client_to_reclaim(name, princhash, nn); +- if (!crp) { +- kfree(name.data); +- return; +- } + crp->cr_clp = clp; + } + +@@ -240,8 +229,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + inode_unlock(d_inode(dir)); + if (status == 0) { + if (nn->in_grace) +- __nfsd4_create_reclaim_record_grace(clp, dname, +- HEXDIR_LEN, nn); ++ __nfsd4_create_reclaim_record_grace(clp, dname, nn); + vfs_fsync(nn->rec_file, 0); + } else { + printk(KERN_ERR "NFSD: failed to write recovery record" +@@ -478,7 +466,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + static int + load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + + if (strlen(cname) != HEXDIR_LEN - 1) { +@@ -487,16 +475,7 @@ load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kstrdup(cname, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- goto out; +- } +- name.len = HEXDIR_LEN; +- if (!nfs4_client_to_reclaim(name, princhash, nn)) +- kfree(name.data); +-out: ++ nfs4_client_to_reclaim(name, princhash, nn); + return 0; + } + +@@ -792,6 +771,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + { + uint8_t cmd, princhashlen; + struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; ++ char *namecopy __free(kfree) = NULL; ++ char *princhashcopy __free(kfree) = NULL; + uint16_t namelen; + struct cld_net *cn = nn->cld_net; + +@@ -810,20 +791,20 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: namelen should not be zero", __func__); + return -EINVAL; + } +- name.data = memdup_user(&ci->cc_name.cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&ci->cc_name.cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; + if (princhashlen > 0) { +- princhash.data = memdup_user( +- &ci->cc_princhash.cp_data, +- princhashlen); +- if (IS_ERR(princhash.data)) { +- kfree(name.data); +- return PTR_ERR(princhash.data); +- } ++ princhashcopy = memdup_user( ++ &ci->cc_princhash.cp_data, ++ princhashlen); ++ if (IS_ERR(princhashcopy)) ++ return PTR_ERR(princhashcopy); ++ princhash.data = princhashcopy; + princhash.len = princhashlen; + } else + princhash.len = 0; +@@ -837,23 +818,21 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: namelen should not be zero", __func__); + return -EINVAL; + } +- name.data = memdup_user(&cnm->cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&cnm->cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + } + #ifdef CONFIG_NFSD_V4 + if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { + name.len = name.len - 5; +- memmove(name.data, name.data + 5, name.len); ++ name.data = name.data + 5; + cn->cn_has_legacy = true; + } + #endif +- if (!nfs4_client_to_reclaim(name, princhash, nn)) { +- kfree(name.data); +- kfree(princhash.data); ++ if (!nfs4_client_to_reclaim(name, princhash, nn)) + return -EFAULT; +- } + return nn->client_tracking_ops->msglen; + } + return -EFAULT; +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index ed5cc5b2330aef..f390716ce3ffe7 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -8027,9 +8027,6 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn) + + /* + * failure => all reset bets are off, nfserr_no_grace... +- * +- * The caller is responsible for freeing name.data if NULL is returned (it +- * will be freed in nfs4_remove_reclaim_record in the normal case). + */ + struct nfs4_client_reclaim * + nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, +@@ -8038,6 +8035,22 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + unsigned int strhashval; + struct nfs4_client_reclaim *crp; + ++ name.data = kmemdup(name.data, name.len, GFP_KERNEL); ++ if (!name.data) { ++ dprintk("%s: failed to allocate memory for name.data!\n", ++ __func__); ++ return NULL; ++ } ++ if (princhash.len) { ++ princhash.data = kmemdup(princhash.data, princhash.len, GFP_KERNEL); ++ if (!princhash.data) { ++ dprintk("%s: failed to allocate memory for princhash.data!\n", ++ __func__); ++ kfree(name.data); ++ return NULL; ++ } ++ } else ++ princhash.data = NULL; + crp = alloc_reclaim(); + if (crp) { + strhashval = clientstr_hashval(name); +@@ -8049,6 +8062,9 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + crp->cr_princhash.len = princhash.len; + crp->cr_clp = NULL; + nn->reclaim_str_hashtbl_size++; ++ } else { ++ kfree(name.data); ++ kfree(princhash.data); + } + return crp; + } +-- +2.53.0 + diff --git a/queue-6.1/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch b/queue-6.1/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch new file mode 100644 index 0000000000..6cbb01417f --- /dev/null +++ b/queue-6.1/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch @@ -0,0 +1,154 @@ +From 0e9a219fd38ce22467fb9271cecfe30496e461a3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 15 Sep 2025 12:55:13 +1000 +Subject: nfsd: move name lookup out of nfsd4_list_rec_dir() + +From: NeilBrown + +[ Upstream commit 89bd77cf436bf25e448817a662ebf76515f22863 ] + +nfsd4_list_rec_dir() is called with two different callbacks. +One of the callbacks uses vfs_rmdir() to remove the directory. +The other doesn't use the dentry at all, just the name. + +As only one callback needs the dentry, this patch moves the lookup into +that callback. This prepares of changes to how directory operations +are locked. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 55 ++++++++++++++++++++++--------------------- + 1 file changed, 28 insertions(+), 27 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index d2505f817d78be..3ad9da783eb686 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -254,7 +254,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + nfs4_reset_creds(original_cred); + } + +-typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); ++typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *); + + struct name_list { + char name[HEXDIR_LEN]; +@@ -308,23 +308,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) + } + + status = iterate_dir(nn->rec_file, &ctx.ctx); +- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +- if (!status) { +- struct dentry *dentry; +- dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1); +- if (IS_ERR(dentry)) { +- status = PTR_ERR(dentry); +- break; +- } +- status = f(dir, dentry, nn); +- dput(dentry); +- } ++ if (!status) ++ status = f(dir, entry->name, nn); ++ + list_del(&entry->list); + kfree(entry); + } +- inode_unlock(d_inode(dir)); + nfs4_reset_creds(original_cred); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +@@ -422,18 +413,19 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) + } + + static int +-purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + int status; ++ struct dentry *child; + struct xdr_netobj name; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -443,10 +435,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) + if (nfs4_has_reclaimed_state(name, nn)) + goto out_free; + +- status = vfs_rmdir(&init_user_ns, d_inode(parent), child); +- if (status) +- printk("failed to remove client recovery directory %pd\n", +- child); ++ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); ++ child = lookup_one_len(cname, parent, HEXDIR_LEN - 1); ++ if (!IS_ERR(child)) { ++ status = vfs_rmdir(&init_user_ns, d_inode(parent), child); ++ if (status) ++ printk("failed to remove client recovery directory %pd\n", ++ child); ++ dput(child); ++ } ++ inode_unlock(d_inode(parent)); ++ + out_free: + kfree(name.data); + out: +@@ -477,18 +476,18 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + } + + static int +-load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + struct xdr_netobj name; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -843,11 +842,13 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + return PTR_ERR(name.data); + name.len = namelen; + } ++#ifdef CONFIG_NFSD_V4 + if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { + name.len = name.len - 5; + memmove(name.data, name.data + 5, name.len); + cn->cn_has_legacy = true; + } ++#endif + if (!nfs4_client_to_reclaim(name, princhash, nn)) { + kfree(name.data); + kfree(princhash.data); +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 57d129b5df..99bd83c088 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -10,3 +10,5 @@ slimbus-qcom-ngd-ctrl-fix-probe-error-path-ordering.patch slimbus-qcom-ngd-ctrl-correct-pdr-and-ssr-cleanup-ow.patch slimbus-convert-to-platform-remove-callback-returnin.patch slimbus-qcom-ngd-ctrl-register-callbacks-after-creat.patch +nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch +nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch diff --git a/queue-6.12/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch b/queue-6.12/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch new file mode 100644 index 0000000000..40e4f44fa8 --- /dev/null +++ b/queue-6.12/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch @@ -0,0 +1,220 @@ +From 67274988fb4f83f18b35d72c468a214c2564cb6f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Sep 2025 11:38:33 +1000 +Subject: nfsd: change nfs4_client_to_reclaim() to allocate data + +From: NeilBrown + +[ Upstream commit 4552f4e3f2c96597914f07b060d5c5db84420ddd ] + +The calling convention for nfs4_client_to_reclaim() is clumsy in that +the caller needs to free memory if the function fails. It is much +cleaner if the function frees its own memory. + +This patch changes nfs4_client_to_reclaim() to re-allocate the .data +fields to be stored in the newly allocated struct nfs4_client_reclaim, +and to free everything on failure. + +__cld_pipe_inprogress_downcall() needs to allocate the data anyway to +copy it from user-space, so now that data is allocated twice. I think +that is a small price to pay for a cleaner interface. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 67 +++++++++++++++---------------------------- + fs/nfsd/nfs4state.c | 22 ++++++++++++-- + 2 files changed, 42 insertions(+), 47 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 4c63f46985c2ea..a8182962a28618 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -168,24 +168,13 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error) + + static void + __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, +- const char *dname, int len, struct nfsd_net *nn) ++ char *dname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = strlen(dname), .data = dname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + struct nfs4_client_reclaim *crp; + +- name.data = kmemdup(dname, len, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- return; +- } +- name.len = len; + crp = nfs4_client_to_reclaim(name, princhash, nn); +- if (!crp) { +- kfree(name.data); +- return; +- } + crp->cr_clp = clp; + } + +@@ -241,8 +230,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + inode_unlock(d_inode(dir)); + if (status == 0) { + if (nn->in_grace) +- __nfsd4_create_reclaim_record_grace(clp, dname, +- HEXDIR_LEN, nn); ++ __nfsd4_create_reclaim_record_grace(clp, dname, nn); + vfs_fsync(nn->rec_file, 0); + } else { + printk(KERN_ERR "NFSD: failed to write recovery record" +@@ -479,7 +467,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + static int + load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + + if (strlen(cname) != HEXDIR_LEN - 1) { +@@ -488,16 +476,7 @@ load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kstrdup(cname, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- goto out; +- } +- name.len = HEXDIR_LEN; +- if (!nfs4_client_to_reclaim(name, princhash, nn)) +- kfree(name.data); +-out: ++ nfs4_client_to_reclaim(name, princhash, nn); + return 0; + } + +@@ -796,6 +775,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + { + uint8_t cmd, princhashlen; + struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; ++ char *namecopy __free(kfree) = NULL; ++ char *princhashcopy __free(kfree) = NULL; + uint16_t namelen; + + if (get_user(cmd, &cmsg->cm_cmd)) { +@@ -813,20 +794,20 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: invalid namelen (%u)", __func__, namelen); + return -EINVAL; + } +- name.data = memdup_user(&ci->cc_name.cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&ci->cc_name.cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; + if (princhashlen > 0) { +- princhash.data = memdup_user( +- &ci->cc_princhash.cp_data, +- princhashlen); +- if (IS_ERR(princhash.data)) { +- kfree(name.data); +- return PTR_ERR(princhash.data); +- } ++ princhashcopy = memdup_user( ++ &ci->cc_princhash.cp_data, ++ princhashlen); ++ if (IS_ERR(princhashcopy)) ++ return PTR_ERR(princhashcopy); ++ princhash.data = princhashcopy; + princhash.len = princhashlen; + } else + princhash.len = 0; +@@ -840,9 +821,10 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: invalid namelen (%u)", __func__, namelen); + return -EINVAL; + } +- name.data = memdup_user(&cnm->cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&cnm->cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + } + #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING +@@ -850,15 +832,12 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + struct cld_net *cn = nn->cld_net; + + name.len = name.len - 5; +- memmove(name.data, name.data + 5, name.len); ++ name.data = name.data + 5; + cn->cn_has_legacy = true; + } + #endif +- if (!nfs4_client_to_reclaim(name, princhash, nn)) { +- kfree(name.data); +- kfree(princhash.data); ++ if (!nfs4_client_to_reclaim(name, princhash, nn)) + return -EFAULT; +- } + return nn->client_tracking_ops->msglen; + } + return -EFAULT; +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index e18d70adcdb903..8c13fa68c08a84 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -8557,9 +8557,6 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn) + + /* + * failure => all reset bets are off, nfserr_no_grace... +- * +- * The caller is responsible for freeing name.data if NULL is returned (it +- * will be freed in nfs4_remove_reclaim_record in the normal case). + */ + struct nfs4_client_reclaim * + nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, +@@ -8568,6 +8565,22 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + unsigned int strhashval; + struct nfs4_client_reclaim *crp; + ++ name.data = kmemdup(name.data, name.len, GFP_KERNEL); ++ if (!name.data) { ++ dprintk("%s: failed to allocate memory for name.data!\n", ++ __func__); ++ return NULL; ++ } ++ if (princhash.len) { ++ princhash.data = kmemdup(princhash.data, princhash.len, GFP_KERNEL); ++ if (!princhash.data) { ++ dprintk("%s: failed to allocate memory for princhash.data!\n", ++ __func__); ++ kfree(name.data); ++ return NULL; ++ } ++ } else ++ princhash.data = NULL; + crp = alloc_reclaim(); + if (crp) { + strhashval = clientstr_hashval(name); +@@ -8579,6 +8592,9 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + crp->cr_princhash.len = princhash.len; + crp->cr_clp = NULL; + nn->reclaim_str_hashtbl_size++; ++ } else { ++ kfree(name.data); ++ kfree(princhash.data); + } + return crp; + } +-- +2.53.0 + diff --git a/queue-6.12/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch b/queue-6.12/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch new file mode 100644 index 0000000000..54b6f0bb6f --- /dev/null +++ b/queue-6.12/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch @@ -0,0 +1,140 @@ +From 58d0a73ee18904cf81bb132b2a0397477df37230 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 15 Sep 2025 12:55:13 +1000 +Subject: nfsd: move name lookup out of nfsd4_list_rec_dir() + +From: NeilBrown + +[ Upstream commit 89bd77cf436bf25e448817a662ebf76515f22863 ] + +nfsd4_list_rec_dir() is called with two different callbacks. +One of the callbacks uses vfs_rmdir() to remove the directory. +The other doesn't use the dentry at all, just the name. + +As only one callback needs the dentry, this patch moves the lookup into +that callback. This prepares of changes to how directory operations +are locked. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 53 +++++++++++++++++++++---------------------- + 1 file changed, 26 insertions(+), 27 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 075715998648a0..4c63f46985c2ea 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -255,7 +255,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + nfs4_reset_creds(original_cred); + } + +-typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); ++typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *); + + struct name_list { + char name[HEXDIR_LEN]; +@@ -309,23 +309,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) + } + + status = iterate_dir(nn->rec_file, &ctx.ctx); +- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +- if (!status) { +- struct dentry *dentry; +- dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1); +- if (IS_ERR(dentry)) { +- status = PTR_ERR(dentry); +- break; +- } +- status = f(dir, dentry, nn); +- dput(dentry); +- } ++ if (!status) ++ status = f(dir, entry->name, nn); ++ + list_del(&entry->list); + kfree(entry); + } +- inode_unlock(d_inode(dir)); + nfs4_reset_creds(original_cred); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +@@ -423,18 +414,19 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) + } + + static int +-purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + int status; ++ struct dentry *child; + struct xdr_netobj name; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -444,10 +436,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) + if (nfs4_has_reclaimed_state(name, nn)) + goto out_free; + +- status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); +- if (status) +- printk("failed to remove client recovery directory %pd\n", +- child); ++ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); ++ child = lookup_one_len(cname, parent, HEXDIR_LEN-1); ++ if (!IS_ERR(child)) { ++ status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); ++ if (status) ++ printk("failed to remove client recovery directory %pd\n", ++ child); ++ dput(child); ++ } ++ inode_unlock(d_inode(parent)); ++ + out_free: + kfree(name.data); + out: +@@ -478,18 +477,18 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + } + + static int +-load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + struct xdr_netobj name; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 2ce5a72f9b..9ee4dba2cf 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -24,3 +24,5 @@ nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch nfsv4-flexfiles-reject-zero-filehandle-version-count.patch locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch +nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch +nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch diff --git a/queue-6.18/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch b/queue-6.18/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch new file mode 100644 index 0000000000..a4c00d7484 --- /dev/null +++ b/queue-6.18/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch @@ -0,0 +1,220 @@ +From d25ea21a59bd530ca5718e460738e9d755710934 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Sep 2025 11:38:33 +1000 +Subject: nfsd: change nfs4_client_to_reclaim() to allocate data + +From: NeilBrown + +[ Upstream commit 4552f4e3f2c96597914f07b060d5c5db84420ddd ] + +The calling convention for nfs4_client_to_reclaim() is clumsy in that +the caller needs to free memory if the function fails. It is much +cleaner if the function frees its own memory. + +This patch changes nfs4_client_to_reclaim() to re-allocate the .data +fields to be stored in the newly allocated struct nfs4_client_reclaim, +and to free everything on failure. + +__cld_pipe_inprogress_downcall() needs to allocate the data anyway to +copy it from user-space, so now that data is allocated twice. I think +that is a small price to pay for a cleaner interface. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 67 +++++++++++++++---------------------------- + fs/nfsd/nfs4state.c | 22 ++++++++++++-- + 2 files changed, 42 insertions(+), 47 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 8ab0b26c1f91ab..8c5b5b8a5c880a 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -147,24 +147,13 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error) + + static void + __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, +- const char *dname, int len, struct nfsd_net *nn) ++ char *dname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = strlen(dname), .data = dname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + struct nfs4_client_reclaim *crp; + +- name.data = kmemdup(dname, len, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- return; +- } +- name.len = len; + crp = nfs4_client_to_reclaim(name, princhash, nn); +- if (!crp) { +- kfree(name.data); +- return; +- } + crp->cr_clp = clp; + } + +@@ -223,8 +212,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + inode_unlock(d_inode(dir)); + if (status == 0) { + if (nn->in_grace) +- __nfsd4_create_reclaim_record_grace(clp, dname, +- HEXDIR_LEN, nn); ++ __nfsd4_create_reclaim_record_grace(clp, dname, nn); + vfs_fsync(nn->rec_file, 0); + } else { + printk(KERN_ERR "NFSD: failed to write recovery record" +@@ -461,7 +449,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + static int + load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + + if (strlen(cname) != HEXDIR_LEN - 1) { +@@ -470,16 +458,7 @@ load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kstrdup(cname, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- goto out; +- } +- name.len = HEXDIR_LEN; +- if (!nfs4_client_to_reclaim(name, princhash, nn)) +- kfree(name.data); +-out: ++ nfs4_client_to_reclaim(name, princhash, nn); + return 0; + } + +@@ -777,6 +756,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + { + uint8_t cmd, princhashlen; + struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; ++ char *namecopy __free(kfree) = NULL; ++ char *princhashcopy __free(kfree) = NULL; + uint16_t namelen; + + if (get_user(cmd, &cmsg->cm_cmd)) { +@@ -794,20 +775,20 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: invalid namelen (%u)", __func__, namelen); + return -EINVAL; + } +- name.data = memdup_user(&ci->cc_name.cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&ci->cc_name.cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; + if (princhashlen > 0) { +- princhash.data = memdup_user( +- &ci->cc_princhash.cp_data, +- princhashlen); +- if (IS_ERR(princhash.data)) { +- kfree(name.data); +- return PTR_ERR(princhash.data); +- } ++ princhashcopy = memdup_user( ++ &ci->cc_princhash.cp_data, ++ princhashlen); ++ if (IS_ERR(princhashcopy)) ++ return PTR_ERR(princhashcopy); ++ princhash.data = princhashcopy; + princhash.len = princhashlen; + } else + princhash.len = 0; +@@ -821,9 +802,10 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: invalid namelen (%u)", __func__, namelen); + return -EINVAL; + } +- name.data = memdup_user(&cnm->cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&cnm->cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + } + #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING +@@ -831,15 +813,12 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + struct cld_net *cn = nn->cld_net; + + name.len = name.len - 5; +- memmove(name.data, name.data + 5, name.len); ++ name.data = name.data + 5; + cn->cn_has_legacy = true; + } + #endif +- if (!nfs4_client_to_reclaim(name, princhash, nn)) { +- kfree(name.data); +- kfree(princhash.data); ++ if (!nfs4_client_to_reclaim(name, princhash, nn)) + return -EFAULT; +- } + return nn->client_tracking_ops->msglen; + } + return -EFAULT; +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index a063a4a83ec22d..a3e6076a3e645e 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -8831,9 +8831,6 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn) + + /* + * failure => all reset bets are off, nfserr_no_grace... +- * +- * The caller is responsible for freeing name.data if NULL is returned (it +- * will be freed in nfs4_remove_reclaim_record in the normal case). + */ + struct nfs4_client_reclaim * + nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, +@@ -8842,6 +8839,22 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + unsigned int strhashval; + struct nfs4_client_reclaim *crp; + ++ name.data = kmemdup(name.data, name.len, GFP_KERNEL); ++ if (!name.data) { ++ dprintk("%s: failed to allocate memory for name.data!\n", ++ __func__); ++ return NULL; ++ } ++ if (princhash.len) { ++ princhash.data = kmemdup(princhash.data, princhash.len, GFP_KERNEL); ++ if (!princhash.data) { ++ dprintk("%s: failed to allocate memory for princhash.data!\n", ++ __func__); ++ kfree(name.data); ++ return NULL; ++ } ++ } else ++ princhash.data = NULL; + crp = alloc_reclaim(); + if (crp) { + strhashval = clientstr_hashval(name); +@@ -8853,6 +8866,9 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + crp->cr_princhash.len = princhash.len; + crp->cr_clp = NULL; + nn->reclaim_str_hashtbl_size++; ++ } else { ++ kfree(name.data); ++ kfree(princhash.data); + } + return crp; + } +-- +2.53.0 + diff --git a/queue-6.18/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch b/queue-6.18/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch new file mode 100644 index 0000000000..553a6cae3f --- /dev/null +++ b/queue-6.18/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch @@ -0,0 +1,141 @@ +From b55d1d817836298eedcc5b6512e1cd6827b1f4e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 15 Sep 2025 12:55:13 +1000 +Subject: nfsd: move name lookup out of nfsd4_list_rec_dir() + +From: NeilBrown + +[ Upstream commit 89bd77cf436bf25e448817a662ebf76515f22863 ] + +nfsd4_list_rec_dir() is called with two different callbacks. +One of the callbacks uses vfs_rmdir() to remove the directory. +The other doesn't use the dentry at all, just the name. + +As only one callback needs the dentry, this patch moves the lookup into +that callback. This prepares of changes to how directory operations +are locked. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 54 +++++++++++++++++++++---------------------- + 1 file changed, 26 insertions(+), 28 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 3ea4b8a8616315..8ab0b26c1f91ab 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -237,7 +237,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + nfs4_reset_creds(original_cred); + } + +-typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); ++typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *); + + struct name_list { + char name[HEXDIR_LEN]; +@@ -291,24 +291,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) + } + + status = iterate_dir(nn->rec_file, &ctx.ctx); +- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +- if (!status) { +- struct dentry *dentry; +- dentry = lookup_one(&nop_mnt_idmap, +- &QSTR(entry->name), dir); +- if (IS_ERR(dentry)) { +- status = PTR_ERR(dentry); +- break; +- } +- status = f(dir, dentry, nn); +- dput(dentry); +- } ++ if (!status) ++ status = f(dir, entry->name, nn); ++ + list_del(&entry->list); + kfree(entry); + } +- inode_unlock(d_inode(dir)); + nfs4_reset_creds(original_cred); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +@@ -406,18 +396,19 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) + } + + static int +-purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + int status; ++ struct dentry *child; + struct xdr_netobj name; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -427,10 +418,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) + if (nfs4_has_reclaimed_state(name, nn)) + goto out_free; + +- status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); +- if (status) +- printk("failed to remove client recovery directory %pd\n", +- child); ++ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); ++ child = lookup_one(&nop_mnt_idmap, &QSTR(cname), parent); ++ if (!IS_ERR(child)) { ++ status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); ++ if (status) ++ printk("failed to remove client recovery directory %pd\n", ++ child); ++ dput(child); ++ } ++ inode_unlock(d_inode(parent)); ++ + out_free: + kfree(name.data); + out: +@@ -461,18 +459,18 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + } + + static int +-load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + struct xdr_netobj name; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +-- +2.53.0 + diff --git a/queue-6.18/series b/queue-6.18/series index 9805dd4626..30724b9331 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -19,3 +19,5 @@ f2fs-detect-more-inconsistent-cases-in-sanity_check_node_footer.patch f2fs-fix-to-do-sanity-check-on-f2fs_get_node_folio_ra.patch fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch net-sched-dualpi2-fix-gso-backlog-accounting.patch +nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch +nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch diff --git a/queue-6.6/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch b/queue-6.6/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch new file mode 100644 index 0000000000..07d2e8efdc --- /dev/null +++ b/queue-6.6/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch @@ -0,0 +1,218 @@ +From 6106a9c99a249d36de9e332e01fcb59f50761577 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Sep 2025 11:38:33 +1000 +Subject: nfsd: change nfs4_client_to_reclaim() to allocate data + +From: NeilBrown + +[ Upstream commit 4552f4e3f2c96597914f07b060d5c5db84420ddd ] + +The calling convention for nfs4_client_to_reclaim() is clumsy in that +the caller needs to free memory if the function fails. It is much +cleaner if the function frees its own memory. + +This patch changes nfs4_client_to_reclaim() to re-allocate the .data +fields to be stored in the newly allocated struct nfs4_client_reclaim, +and to free everything on failure. + +__cld_pipe_inprogress_downcall() needs to allocate the data anyway to +copy it from user-space, so now that data is allocated twice. I think +that is a small price to pay for a cleaner interface. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 67 +++++++++++++++---------------------------- + fs/nfsd/nfs4state.c | 22 ++++++++++++-- + 2 files changed, 42 insertions(+), 47 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 8b8d7e0131fd1f..f4060d0cec8a5e 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -167,24 +167,13 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error) + + static void + __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, +- const char *dname, int len, struct nfsd_net *nn) ++ char *dname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = strlen(dname), .data = dname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + struct nfs4_client_reclaim *crp; + +- name.data = kmemdup(dname, len, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- return; +- } +- name.len = len; + crp = nfs4_client_to_reclaim(name, princhash, nn); +- if (!crp) { +- kfree(name.data); +- return; +- } + crp->cr_clp = clp; + } + +@@ -240,8 +229,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + inode_unlock(d_inode(dir)); + if (status == 0) { + if (nn->in_grace) +- __nfsd4_create_reclaim_record_grace(clp, dname, +- HEXDIR_LEN, nn); ++ __nfsd4_create_reclaim_record_grace(clp, dname, nn); + vfs_fsync(nn->rec_file, 0); + } else { + printk(KERN_ERR "NFSD: failed to write recovery record" +@@ -478,7 +466,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + static int + load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { +- struct xdr_netobj name; ++ struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname }; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + + if (strlen(cname) != HEXDIR_LEN - 1) { +@@ -487,16 +475,7 @@ load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kstrdup(cname, GFP_KERNEL); +- if (!name.data) { +- dprintk("%s: failed to allocate memory for name.data!\n", +- __func__); +- goto out; +- } +- name.len = HEXDIR_LEN; +- if (!nfs4_client_to_reclaim(name, princhash, nn)) +- kfree(name.data); +-out: ++ nfs4_client_to_reclaim(name, princhash, nn); + return 0; + } + +@@ -792,6 +771,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + { + uint8_t cmd, princhashlen; + struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; ++ char *namecopy __free(kfree) = NULL; ++ char *princhashcopy __free(kfree) = NULL; + uint16_t namelen; + struct cld_net *cn = nn->cld_net; + +@@ -810,20 +791,20 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: namelen should not be zero", __func__); + return -EINVAL; + } +- name.data = memdup_user(&ci->cc_name.cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&ci->cc_name.cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; + if (princhashlen > 0) { +- princhash.data = memdup_user( +- &ci->cc_princhash.cp_data, +- princhashlen); +- if (IS_ERR(princhash.data)) { +- kfree(name.data); +- return PTR_ERR(princhash.data); +- } ++ princhashcopy = memdup_user( ++ &ci->cc_princhash.cp_data, ++ princhashlen); ++ if (IS_ERR(princhashcopy)) ++ return PTR_ERR(princhashcopy); ++ princhash.data = princhashcopy; + princhash.len = princhashlen; + } else + princhash.len = 0; +@@ -837,23 +818,21 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + dprintk("%s: namelen should not be zero", __func__); + return -EINVAL; + } +- name.data = memdup_user(&cnm->cn_id, namelen); +- if (IS_ERR(name.data)) +- return PTR_ERR(name.data); ++ namecopy = memdup_user(&cnm->cn_id, namelen); ++ if (IS_ERR(namecopy)) ++ return PTR_ERR(namecopy); ++ name.data = namecopy; + name.len = namelen; + } + #ifdef CONFIG_NFSD_V4 + if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { + name.len = name.len - 5; +- memmove(name.data, name.data + 5, name.len); ++ name.data = name.data + 5; + cn->cn_has_legacy = true; + } + #endif +- if (!nfs4_client_to_reclaim(name, princhash, nn)) { +- kfree(name.data); +- kfree(princhash.data); ++ if (!nfs4_client_to_reclaim(name, princhash, nn)) + return -EFAULT; +- } + return nn->client_tracking_ops->msglen; + } + return -EFAULT; +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index c0dd50b59ad16c..f21878a7f39398 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -8093,9 +8093,6 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn) + + /* + * failure => all reset bets are off, nfserr_no_grace... +- * +- * The caller is responsible for freeing name.data if NULL is returned (it +- * will be freed in nfs4_remove_reclaim_record in the normal case). + */ + struct nfs4_client_reclaim * + nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, +@@ -8104,6 +8101,22 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + unsigned int strhashval; + struct nfs4_client_reclaim *crp; + ++ name.data = kmemdup(name.data, name.len, GFP_KERNEL); ++ if (!name.data) { ++ dprintk("%s: failed to allocate memory for name.data!\n", ++ __func__); ++ return NULL; ++ } ++ if (princhash.len) { ++ princhash.data = kmemdup(princhash.data, princhash.len, GFP_KERNEL); ++ if (!princhash.data) { ++ dprintk("%s: failed to allocate memory for princhash.data!\n", ++ __func__); ++ kfree(name.data); ++ return NULL; ++ } ++ } else ++ princhash.data = NULL; + crp = alloc_reclaim(); + if (crp) { + strhashval = clientstr_hashval(name); +@@ -8115,6 +8128,9 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, + crp->cr_princhash.len = princhash.len; + crp->cr_clp = NULL; + nn->reclaim_str_hashtbl_size++; ++ } else { ++ kfree(name.data); ++ kfree(princhash.data); + } + return crp; + } +-- +2.53.0 + diff --git a/queue-6.6/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch b/queue-6.6/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch new file mode 100644 index 0000000000..5fe370b507 --- /dev/null +++ b/queue-6.6/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch @@ -0,0 +1,154 @@ +From d08281c0218c932d0b2e43a3e6ae01e44151200f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 15 Sep 2025 12:55:13 +1000 +Subject: nfsd: move name lookup out of nfsd4_list_rec_dir() + +From: NeilBrown + +[ Upstream commit 89bd77cf436bf25e448817a662ebf76515f22863 ] + +nfsd4_list_rec_dir() is called with two different callbacks. +One of the callbacks uses vfs_rmdir() to remove the directory. +The other doesn't use the dentry at all, just the name. + +As only one callback needs the dentry, this patch moves the lookup into +that callback. This prepares of changes to how directory operations +are locked. + +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4recover.c | 55 ++++++++++++++++++++++--------------------- + 1 file changed, 28 insertions(+), 27 deletions(-) + +diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c +index 017044c27d381d..8b8d7e0131fd1f 100644 +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -254,7 +254,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) + nfs4_reset_creds(original_cred); + } + +-typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); ++typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *); + + struct name_list { + char name[HEXDIR_LEN]; +@@ -308,23 +308,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) + } + + status = iterate_dir(nn->rec_file, &ctx.ctx); +- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +- if (!status) { +- struct dentry *dentry; +- dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1); +- if (IS_ERR(dentry)) { +- status = PTR_ERR(dentry); +- break; +- } +- status = f(dir, dentry, nn); +- dput(dentry); +- } ++ if (!status) ++ status = f(dir, entry->name, nn); ++ + list_del(&entry->list); + kfree(entry); + } +- inode_unlock(d_inode(dir)); + nfs4_reset_creds(original_cred); + + list_for_each_entry_safe(entry, tmp, &ctx.names, list) { +@@ -422,18 +413,19 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) + } + + static int +-purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + int status; ++ struct dentry *child; + struct xdr_netobj name; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -443,10 +435,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) + if (nfs4_has_reclaimed_state(name, nn)) + goto out_free; + +- status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); +- if (status) +- printk("failed to remove client recovery directory %pd\n", +- child); ++ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); ++ child = lookup_one_len(cname, parent, HEXDIR_LEN-1); ++ if (!IS_ERR(child)) { ++ status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); ++ if (status) ++ printk("failed to remove client recovery directory %pd\n", ++ child); ++ dput(child); ++ } ++ inode_unlock(d_inode(parent)); ++ + out_free: + kfree(name.data); + out: +@@ -477,18 +476,18 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) + } + + static int +-load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) ++load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) + { + struct xdr_netobj name; + struct xdr_netobj princhash = { .len = 0, .data = NULL }; + +- if (child->d_name.len != HEXDIR_LEN - 1) { +- printk("%s: illegal name %pd in recovery directory\n", +- __func__, child); ++ if (strlen(cname) != HEXDIR_LEN - 1) { ++ printk("%s: illegal name %s in recovery directory\n", ++ __func__, cname); + /* Keep trying; maybe the others are OK: */ + return 0; + } +- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); ++ name.data = kstrdup(cname, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); +@@ -843,11 +842,13 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, + return PTR_ERR(name.data); + name.len = namelen; + } ++#ifdef CONFIG_NFSD_V4 + if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { + name.len = name.len - 5; + memmove(name.data, name.data + 5, name.len); + cn->cn_has_legacy = true; + } ++#endif + if (!nfs4_client_to_reclaim(name, princhash, nn)) { + kfree(name.data); + kfree(princhash.data); +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index 0a9113e8e2..6dd435d12a 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -38,3 +38,5 @@ fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch nfsv4-flexfiles-reject-zero-filehandle-version-count.patch nfsd-don-t-reset-the-write-verifier-on-a-commit-eagain.patch apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch +nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch +nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch