]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Wed, 8 Jul 2026 12:04:39 +0000 (08:04 -0400)
committerSasha Levin <sashal@kernel.org>
Wed, 8 Jul 2026 12:04:39 +0000 (08:04 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
18 files changed:
queue-5.10/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch [new file with mode: 0644]
queue-5.10/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch [new file with mode: 0644]
queue-5.15/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch [new file with mode: 0644]
queue-5.15/series
queue-6.1/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch [new file with mode: 0644]
queue-6.1/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch [new file with mode: 0644]
queue-6.1/series
queue-6.12/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch [new file with mode: 0644]
queue-6.12/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch [new file with mode: 0644]
queue-6.12/series
queue-6.18/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch [new file with mode: 0644]
queue-6.18/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch [new file with mode: 0644]
queue-6.18/series
queue-6.6/nfsd-change-nfs4_client_to_reclaim-to-allocate-data.patch [new file with mode: 0644]
queue-6.6/nfsd-move-name-lookup-out-of-nfsd4_list_rec_dir.patch [new file with mode: 0644]
queue-6.6/series

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 (file)
index 0000000..04b211e
--- /dev/null
@@ -0,0 +1,218 @@
+From d13e59aca2e1a167bd79c398ca888bf2d0b432da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Sep 2025 11:38:33 +1000
+Subject: nfsd: change nfs4_client_to_reclaim() to allocate data
+
+From: NeilBrown <neil@brown.name>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 (file)
index 0000000..85d3cd5
--- /dev/null
@@ -0,0 +1,154 @@
+From 3c83fd41ed6deb900cfcad7444540c105c0e3c80 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 15 Sep 2025 12:55:13 +1000
+Subject: nfsd: move name lookup out of nfsd4_list_rec_dir()
+
+From: NeilBrown <neilb@ownmail.net>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
index c6c484d9c3acd0fdd846ec568e43a75d352772d6..990bb0dcb2579098a05b80044340c5d2918c21ba 100644 (file)
@@ -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 (file)
index 0000000..aa01366
--- /dev/null
@@ -0,0 +1,218 @@
+From 2d47b2334b3baecf9a04ccb8563696184d8e22da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Sep 2025 11:38:33 +1000
+Subject: nfsd: change nfs4_client_to_reclaim() to allocate data
+
+From: NeilBrown <neil@brown.name>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 (file)
index 0000000..b1bad55
--- /dev/null
@@ -0,0 +1,154 @@
+From 49e851bd264042ac2500fe199aac08233f65e463 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 15 Sep 2025 12:55:13 +1000
+Subject: nfsd: move name lookup out of nfsd4_list_rec_dir()
+
+From: NeilBrown <neilb@ownmail.net>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
index 9c069ffbd953088f10c536608cb7b6cfbd938e75..f06ab4c903ee7461b89c86a2bc3b1fcf05c1ab06 100644 (file)
@@ -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 (file)
index 0000000..0bb578e
--- /dev/null
@@ -0,0 +1,218 @@
+From 0e6ced058fed27806e5dc705143487c2949129cc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Sep 2025 11:38:33 +1000
+Subject: nfsd: change nfs4_client_to_reclaim() to allocate data
+
+From: NeilBrown <neil@brown.name>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 (file)
index 0000000..6cbb014
--- /dev/null
@@ -0,0 +1,154 @@
+From 0e9a219fd38ce22467fb9271cecfe30496e461a3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 15 Sep 2025 12:55:13 +1000
+Subject: nfsd: move name lookup out of nfsd4_list_rec_dir()
+
+From: NeilBrown <neilb@ownmail.net>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
index 57d129b5df11135035b47ce11f8c4d4bc51f2ea1..99bd83c0888ba1bc137846aa864eec7dc38b1487 100644 (file)
@@ -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 (file)
index 0000000..40e4f44
--- /dev/null
@@ -0,0 +1,220 @@
+From 67274988fb4f83f18b35d72c468a214c2564cb6f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Sep 2025 11:38:33 +1000
+Subject: nfsd: change nfs4_client_to_reclaim() to allocate data
+
+From: NeilBrown <neil@brown.name>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 (file)
index 0000000..54b6f0b
--- /dev/null
@@ -0,0 +1,140 @@
+From 58d0a73ee18904cf81bb132b2a0397477df37230 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 15 Sep 2025 12:55:13 +1000
+Subject: nfsd: move name lookup out of nfsd4_list_rec_dir()
+
+From: NeilBrown <neilb@ownmail.net>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
index 2ce5a72f9bd102f1fd3c04ac74f51b9b77644f39..9ee4dba2cfe3cffe6d30e62fdce1a0a57cb6e4e6 100644 (file)
@@ -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 (file)
index 0000000..a4c00d7
--- /dev/null
@@ -0,0 +1,220 @@
+From d25ea21a59bd530ca5718e460738e9d755710934 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Sep 2025 11:38:33 +1000
+Subject: nfsd: change nfs4_client_to_reclaim() to allocate data
+
+From: NeilBrown <neil@brown.name>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 (file)
index 0000000..553a6ca
--- /dev/null
@@ -0,0 +1,141 @@
+From b55d1d817836298eedcc5b6512e1cd6827b1f4e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 15 Sep 2025 12:55:13 +1000
+Subject: nfsd: move name lookup out of nfsd4_list_rec_dir()
+
+From: NeilBrown <neilb@ownmail.net>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
index 9805dd462662e47e922ff8a81dc09a908ca66cb0..30724b9331d86bf077996e20a8aa927f10f93924 100644 (file)
@@ -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 (file)
index 0000000..07d2e8e
--- /dev/null
@@ -0,0 +1,218 @@
+From 6106a9c99a249d36de9e332e01fcb59f50761577 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Sep 2025 11:38:33 +1000
+Subject: nfsd: change nfs4_client_to_reclaim() to allocate data
+
+From: NeilBrown <neil@brown.name>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 (file)
index 0000000..5fe370b
--- /dev/null
@@ -0,0 +1,154 @@
+From d08281c0218c932d0b2e43a3e6ae01e44151200f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 15 Sep 2025 12:55:13 +1000
+Subject: nfsd: move name lookup out of nfsd4_list_rec_dir()
+
+From: NeilBrown <neilb@ownmail.net>
+
+[ 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 <neil@brown.name>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
index 0a9113e8e20f2e565b9604bf88ec54026019a84e..6dd435d12ab2c71cbfc4e854b035b588a6950d3f 100644 (file)
@@ -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