[PATCH 3/6] NFSv4.1/pnfs: retry LAYOUTGET with a larger reply buffer on NFS4ERR_TOOSMALL
Benjamin Coddington <ben.coddington-F/[email protected]>
| Newsgroups | gmane.linux.nfs |
|---|---|
| Message-ID | <fc0b7acee2bb88de24825e27d6598bbeca9669db.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-F/[email protected]> --- 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