From: Benjamin Coddington <ben.coddington@hammerspace.com>
To: Trond Myklebust <trondmy@kernel.org>, Anna Schumaker <anna@kernel.org>
Cc: linux-nfs@vger.kernel.org,
Jonathan Curley <jcurley@purestorage.com>,
Mike Snitzer <snitzer@kernel.org>,
Jeff Layton <jlayton@kernel.org>
Subject: [PATCH 3/6] NFSv4.1/pnfs: retry LAYOUTGET with a larger reply buffer on NFS4ERR_TOOSMALL
Date: Thu, 13 Aug 2026 16:41:28 -0400 [thread overview]
Message-ID: <fc0b7acee2bb88de24825e27d6598bbeca9669db.1786653456.git.bcodding@hammerspace.com> (raw)
In-Reply-To: <cover.1786653456.git.bcodding@hammerspace.com>
Layout drivers cap the LAYOUTGET reply buffer with
max_layoutget_response; the flexfiles driver caps it at a single page,
which limits a striped layout segment to roughly 28 stripes. A server
striping wider than that returns NFS4ERR_TOOSMALL, and the client
falls back to I/O through the MDS -- pNFS never engages for those
files.
Instead of failing over to the MDS on the first NFS4ERR_TOOSMALL,
retry the LAYOUTGET once with the reply buffer raised to the session's
maximum response size, the same bound GETDEVICEINFO already uses. The
server offers no size hint in the TOOSMALL error, and the page array
is transient (freed when the RPC completes), so a single jump to the
ceiling is preferred over incremental growth. If the layout does not
fit even the session-sized buffer, fall back to the MDS as before.
The common path is unchanged: the first LAYOUTGET for a layout is
still sent with the driver's default reply buffer, and larger buffers
are only ever allocated against servers that actually hand out wide
layouts.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Benjamin Coddington <bcodding@hammerspace.com>
---
fs/nfs/pnfs.c | 45 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 38 insertions(+), 7 deletions(-)
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 88deefd45202..b596a65c7ef2 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -1166,11 +1166,12 @@ pnfs_alloc_init_layoutget_args(struct inode *ino,
struct nfs_open_context *ctx,
const nfs4_stateid *stateid,
const struct pnfs_layout_range *range,
- gfp_t gfp_flags)
+ size_t min_reply_sz, gfp_t gfp_flags)
{
struct nfs_server *server = pnfs_find_server(ino, ctx);
size_t max_reply_sz = server->pnfs_curr_ld->max_layoutget_response;
- size_t max_pages = max_response_pages(server);
+ size_t session_pages = max_response_pages(server);
+ size_t max_pages = session_pages;
struct nfs4_layoutget *lgp;
dprintk("--> %s\n", __func__);
@@ -1185,6 +1186,17 @@ pnfs_alloc_init_layoutget_args(struct inode *ino,
max_pages = npages;
}
+ /*
+ * A previous LAYOUTGET for this layout did not fit the reply
+ * buffer: raise the layout driver's default up to the session's
+ * maximum response size.
+ */
+ if (min_reply_sz) {
+ size_t npages = (min_reply_sz + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ if (npages > max_pages)
+ max_pages = min(npages, session_pages);
+ }
+
lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags);
if (!lgp->args.layout.pages) {
kfree(lgp);
@@ -2153,6 +2165,7 @@ pnfs_update_layout(struct inode *ino,
.inode = ino,
};
unsigned long giveup = jiffies + (clp->cl_lease_time << 1);
+ size_t reply_sz = 0;
bool first;
if (!pnfs_enabled_sb(NFS_SERVER(ino))) {
@@ -2311,7 +2324,8 @@ pnfs_update_layout(struct inode *ino,
if (arg.length != NFS4_MAX_UINT64)
arg.length = PAGE_ALIGN(arg.length);
- lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &stateid, &arg, gfp_flags);
+ lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &stateid, &arg, reply_sz,
+ gfp_flags);
if (!lgp) {
lseg = ERR_PTR(-ENOMEM);
trace_pnfs_update_layout(ino, pos, count, iomode, lo, NULL,
@@ -2338,12 +2352,29 @@ pnfs_update_layout(struct inode *ino,
break;
case -ENODATA:
/* The server returned NFS4ERR_LAYOUTUNAVAILABLE */
- case -EMSGSIZE:
- /* The layout exceeded loga_maxcount (NFS4ERR_TOOSMALL) */
pnfs_layout_set_fail_bit(
lo, pnfs_iomode_to_fail_bit(iomode));
lseg = NULL;
goto out_put_layout_hdr;
+ case -EMSGSIZE: {
+ /*
+ * The layout exceeded loga_maxcount (NFS4ERR_TOOSMALL):
+ * retry once with the reply buffer raised to the
+ * session's maximum response size before falling back
+ * to I/O through the MDS.
+ */
+ size_t max = max_response_pages(server) << PAGE_SHIFT;
+
+ if (reply_sz < max) {
+ reply_sz = max;
+ exception.retry = 1;
+ break;
+ }
+ pnfs_layout_set_fail_bit(
+ lo, pnfs_iomode_to_fail_bit(iomode));
+ lseg = NULL;
+ goto out_put_layout_hdr;
+ }
default:
if (!nfs_error_is_fatal(PTR_ERR(lseg))) {
pnfs_layout_clear_fail_bit(lo, pnfs_iomode_to_fail_bit(iomode));
@@ -2456,7 +2487,7 @@ static void _lgopen_prepare_attached(struct nfs4_opendata *data,
lo = _pnfs_grab_empty_layout(ino, ctx);
if (!lo)
return;
- lgp = pnfs_alloc_init_layoutget_args(ino, ctx, ¤t_stateid, &rng,
+ lgp = pnfs_alloc_init_layoutget_args(ino, ctx, ¤t_stateid, &rng, 0,
nfs_io_gfp_mask());
if (!lgp) {
pnfs_clear_first_layoutget(lo);
@@ -2482,7 +2513,7 @@ static void _lgopen_prepare_floating(struct nfs4_opendata *data,
};
struct nfs4_layoutget *lgp;
- lgp = pnfs_alloc_init_layoutget_args(ino, ctx, ¤t_stateid, &rng,
+ lgp = pnfs_alloc_init_layoutget_args(ino, ctx, ¤t_stateid, &rng, 0,
nfs_io_gfp_mask());
if (!lgp)
return;
--
2.53.0
next prev parent reply other threads:[~2026-08-13 20:41 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-13 20:41 [PATCH 0/6] NFS: size the LAYOUTGET reply buffer for wide flexfiles layouts Benjamin Coddington
2026-08-13 20:41 ` [PATCH 1/6] NFSv4.1/pnfs: suspend pNFS on NFS4ERR_TOOSMALL from LAYOUTGET Benjamin Coddington
2026-08-13 20:41 ` [PATCH 2/6] NFSv4.1/pnfs: derive loga_maxcount from the LAYOUTGET reply buffer Benjamin Coddington
2026-08-13 20:41 ` Benjamin Coddington [this message]
2026-08-13 20:41 ` [PATCH 4/6] NFSv4.1/pnfs: treat an oversized LAYOUTGET reply as -EMSGSIZE Benjamin Coddington
2026-08-13 20:41 ` [PATCH 5/6] NFSv4.1/pnfs: remember when a server needs a larger LAYOUTGET reply buffer Benjamin Coddington
2026-08-13 20:41 ` [PATCH 6/6] NFSv4/flexfiles: allocate the per-mirror stripe array with kvzalloc_objs Benjamin Coddington
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=fc0b7acee2bb88de24825e27d6598bbeca9669db.1786653456.git.bcodding@hammerspace.com \
--to=ben.coddington@hammerspace.com \
--cc=anna@kernel.org \
--cc=jcurley@purestorage.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=snitzer@kernel.org \
--cc=trondmy@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.