NFSv4/flexfiles: report the intended opnum when DS connection setup fails

When nfs4_ff_layout_prepare_ds() cannot establish a connection to a
data server, it records a device error with opnum OP_ILLEGAL, since no
operation ever made it to the wire.  But servers can use the ff_ioerr4
opnum to distinguish failed read-class operations from write-class
operations when deciding how to recover the affected mirror -- a mirror
that may have missed writes needs to be brought back in sync, while one
that merely failed to serve a read does not.  OP_ILLEGAL gives the
server nothing to act on.

Every caller of nfs4_ff_layout_prepare_ds() knows which operation it
was preparing to send, and the existing fail_return argument already
divides the callers along the same boundary (false for READ, true for
WRITE and COMMIT).  Replace the boolean with the intended opnum, derive
the layout return decision from it, and report it in the tracked device
error instead of OP_ILLEGAL.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Benjamin Coddington <bcodding@hammerspace.com>
Reviewed-by: Tigran Mkrtchyan <tigran.mkrtchyan@desy.de>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
This commit is contained in:
Benjamin Coddington 2026-09-09 13:11:47 -04:00 committed by Anna Schumaker
parent e0697d5806
commit 3a612faba5
3 changed files with 17 additions and 12 deletions

View File

@ -883,7 +883,7 @@ ff_layout_choose_ds_for_read(struct pnfs_layout_segment *lseg,
mirror_ds = ff_layout_get_mirror_ds(lseg->pls_layout, mirror,
*dss_id);
ds = nfs4_ff_layout_prepare_ds(lseg, mirror, mirror_ds,
*dss_id, false);
*dss_id, OP_READ);
if (IS_ERR(ds)) {
nfs4_ff_layout_put_deviceid(mirror_ds);
ret = ERR_CAST(ds);
@ -1132,7 +1132,7 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio,
mirror_ds = ff_layout_get_mirror_ds(pgio->pg_lseg->pls_layout,
mirror, dss_id);
ds = nfs4_ff_layout_prepare_ds(pgio->pg_lseg, mirror,
mirror_ds, dss_id, true);
mirror_ds, dss_id, OP_WRITE);
if (IS_ERR(ds)) {
nfs4_ff_layout_put_deviceid(mirror_ds);
if (!ff_layout_no_fallback_to_mds(pgio->pg_lseg))
@ -2188,7 +2188,8 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
mirror->dss_count,
offset);
mirror_ds = ff_layout_get_mirror_ds(lseg->pls_layout, mirror, dss_id);
ds = nfs4_ff_layout_prepare_ds(lseg, mirror, mirror_ds, dss_id, false);
ds = nfs4_ff_layout_prepare_ds(lseg, mirror, mirror_ds, dss_id,
OP_READ);
if (IS_ERR(ds)) {
ds_fatal_error = nfs_error_is_fatal(PTR_ERR(ds));
goto out_failed;
@ -2288,7 +2289,8 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
mirror->dss_count,
offset);
mirror_ds = ff_layout_get_mirror_ds(lseg->pls_layout, mirror, dss_id);
ds = nfs4_ff_layout_prepare_ds(lseg, mirror, mirror_ds, dss_id, true);
ds = nfs4_ff_layout_prepare_ds(lseg, mirror, mirror_ds, dss_id,
OP_WRITE);
if (IS_ERR(ds)) {
ds_fatal_error = nfs_error_is_fatal(PTR_ERR(ds));
goto out_failed;
@ -2398,7 +2400,8 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
mirror = FF_LAYOUT_COMP(lseg, idx);
dss_id = calc_dss_id_from_commit(lseg, data->ds_commit_index);
mirror_ds = ff_layout_get_mirror_ds(lseg->pls_layout, mirror, dss_id);
ds = nfs4_ff_layout_prepare_ds(lseg, mirror, mirror_ds, dss_id, true);
ds = nfs4_ff_layout_prepare_ds(lseg, mirror, mirror_ds, dss_id,
OP_COMMIT);
if (IS_ERR(ds))
goto out_err;

View File

@ -256,7 +256,7 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
struct nfs4_ff_layout_mirror *mirror,
struct nfs4_ff_layout_ds *mirror_ds,
u32 dss_id,
bool fail_return);
enum nfs_opnum4 opnum);
struct rpc_clnt *
nfs4_ff_find_or_create_ds_client(const struct nfs4_ff_layout_ds *mirror_ds,

View File

@ -379,7 +379,7 @@ ff_layout_get_mirror_ds(struct pnfs_layout_hdr *lo,
* @mirror_ds: referenced device node for the stripe, from
* ff_layout_get_mirror_ds() (may be an ERR_PTR)
* @dss_id: DS stripe id to select stripe to use
* @fail_return: return layout on connect failure?
* @opnum: operation this connection is being prepared for
*
* Try to prepare a DS connection to accept an RPC call. This involves
* selecting a mirror to use and connecting the client to it if it's not
@ -387,8 +387,10 @@ ff_layout_get_mirror_ds(struct pnfs_layout_hdr *lo,
*
* Since we only need a single functioning mirror to satisfy a read, we don't
* want to return the layout if there is one. For writes though, any down
* mirror should result in a LAYOUTRETURN. @fail_return is how we distinguish
* between the two cases.
* mirror should result in a LAYOUTRETURN. @opnum is how we distinguish
* between the two cases. On failure, @opnum is also reported in the tracked
* device error so that the server can tell which class of I/O the client
* was unable to send to the mirror.
*
* Returns a pointer to a connected DS object on success or NULL on failure.
*/
@ -397,7 +399,7 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
struct nfs4_ff_layout_mirror *mirror,
struct nfs4_ff_layout_ds *mirror_ds,
u32 dss_id,
bool fail_return)
enum nfs_opnum4 opnum)
{
struct nfs4_pnfs_ds *ds;
struct inode *ino = lseg->pls_layout->plh_inode;
@ -448,9 +450,9 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
NULL : &mirror_ds->id_node,
dss_id, lseg->pls_range.offset,
lseg->pls_range.length, NFS4ERR_NXIO,
OP_ILLEGAL, GFP_NOIO);
opnum, GFP_NOIO);
ff_layout_send_layouterror(lseg);
if (fail_return || !ff_layout_has_available_ds(lseg))
if (opnum != OP_READ || !ff_layout_has_available_ds(lseg))
pnfs_error_mark_layout_for_return(ino, lseg);
ds = ERR_PTR(status);
out: