From 1f9849a8291cee861d659eb4ea1b109d696dd3ed Mon Sep 17 00:00:00 2001 From: Robert Milkowski Date: Mon, 23 Mar 2026 11:26:17 +0000 Subject: [PATCH] pnfs/filelayout: handle data server op_status errors Data servers can return per-operation NFS status in op_status even when the RPC transport succeeds. filelayout_async_handle_error() currently mixes NFS-level and transport-level handling through task->tk_status. Model this on the NFS-status vs RPC-status split in 38074de35b01 ("NFSv4/flexfiles: Fix handling of NFS level errors in I/O"), while keeping filelayout's existing behaviour for local/decode and DS connection failures. Keep the existing negative task->tk_status handling for successful transports, use op_status only once task->tk_status is non-negative, and use tk_rpc_status strictly for transport errors. Wire read/write/commit done callbacks to pass op_status into filelayout_async_handle_error() and handle NFS4ERR_NOMATCHING_LAYOUT via the existing layout invalidate / retry-through-MDS path. Signed-off-by: Robert Milkowski --- fs/nfs/filelayout/filelayout.c | 141 ++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 45 deletions(-) diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index 90a11afa5d05..f941f6fa685e 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -121,6 +121,7 @@ static void filelayout_reset_read(struct nfs_pgio_header *hdr) } static int filelayout_async_handle_error(struct rpc_task *task, + u32 op_status, struct nfs4_state *state, struct nfs_client *clp, struct pnfs_layout_segment *lseg) @@ -129,50 +130,94 @@ static int filelayout_async_handle_error(struct rpc_task *task, struct inode *inode = lo->plh_inode; struct nfs4_deviceid_node *devid = FILELAYOUT_DEVID_NODE(lseg); struct nfs4_slot_table *tbl = &clp->cl_session->fc_slot_table; + int nfs_err = 0; + + /* Only trust NFS status when the RPC reply was received. */ + if (task->tk_rpc_status == 0) { + if (task->tk_status < 0) + nfs_err = task->tk_status; + else if (op_status) + nfs_err = -(int)op_status; + } - if (task->tk_status >= 0) - return 0; - - switch (task->tk_status) { - /* DS session errors */ - case -NFS4ERR_BADSESSION: - case -NFS4ERR_BADSLOT: - case -NFS4ERR_BAD_HIGH_SLOT: - case -NFS4ERR_DEADSESSION: - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: - case -NFS4ERR_SEQ_FALSE_RETRY: - case -NFS4ERR_SEQ_MISORDERED: - dprintk("%s ERROR %d, Reset session. Exchangeid " - "flags 0x%x\n", __func__, task->tk_status, - clp->cl_exchange_flags); - nfs4_schedule_session_recovery(clp->cl_session, task->tk_status); + switch (nfs_err) { + case -NFS4ERR_STALE: + nfs_err = -ESTALE; break; - case -NFS4ERR_DELAY: - case -NFS4ERR_GRACE: - rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX); + case -NFS4ERR_BADHANDLE: + nfs_err = -EBADHANDLE; break; - case -NFS4ERR_RETRY_UNCACHED_REP: + case -NFS4ERR_ISDIR: + nfs_err = -EISDIR; break; - /* Invalidate Layout errors */ - case -NFS4ERR_ACCESS: - case -NFS4ERR_PNFS_NO_LAYOUT: - case -ESTALE: /* mapped NFS4ERR_STALE */ - case -EBADHANDLE: /* mapped NFS4ERR_BADHANDLE */ - case -EISDIR: /* mapped NFS4ERR_ISDIR */ - case -NFS4ERR_FHEXPIRED: - case -NFS4ERR_WRONG_TYPE: - dprintk("%s Invalid layout error %d\n", __func__, - task->tk_status); - /* - * Destroy layout so new i/o will get a new layout. - * Layout will not be destroyed until all current lseg - * references are put. Mark layout as invalid to resend failed - * i/o and all i/o waiting on the slot table to the MDS until - * layout is destroyed and a new valid layout is obtained. - */ - pnfs_destroy_layout(NFS_I(inode)); - rpc_wake_up(&tbl->slot_tbl_waitq); - goto reset; + } + + /* + * Preserve the old task->tk_status handling for non-NFS errors after a + * successful transport. This avoids stale or partially decoded op_status + * values overriding a newer local/decode failure. + */ + if (task->tk_rpc_status == 0 && task->tk_status < 0) { + switch (task->tk_status) { + case -ECONNREFUSED: + case -EHOSTDOWN: + case -EHOSTUNREACH: + case -ENETUNREACH: + case -EIO: + case -ETIMEDOUT: + case -EPIPE: + case -EPROTO: + case -ENODEV: + dprintk("%s DS reply processing error %d\n", __func__, + task->tk_status); + goto ds_unavailable; + } + } + + if (nfs_err < 0) { + switch (nfs_err) { + /* DS session errors */ + case -NFS4ERR_BADSESSION: + case -NFS4ERR_BADSLOT: + case -NFS4ERR_BAD_HIGH_SLOT: + case -NFS4ERR_DEADSESSION: + case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: + case -NFS4ERR_SEQ_FALSE_RETRY: + case -NFS4ERR_SEQ_MISORDERED: + dprintk("%s ERROR %d, Reset session. Exchangeid " + "flags 0x%x\n", __func__, nfs_err, + clp->cl_exchange_flags); + nfs4_schedule_session_recovery(clp->cl_session, nfs_err); + goto out_retry; + case -NFS4ERR_DELAY: + case -NFS4ERR_GRACE: + rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX); + goto out_retry; + case -NFS4ERR_RETRY_UNCACHED_REP: + goto out_retry; + /* Invalidate Layout errors */ + case -NFS4ERR_ACCESS: + case -NFS4ERR_PNFS_NO_LAYOUT: + case -ESTALE: /* mapped NFS4ERR_STALE */ + case -EBADHANDLE: /* mapped NFS4ERR_BADHANDLE */ + case -EISDIR: /* mapped NFS4ERR_ISDIR */ + case -NFS4ERR_FHEXPIRED: + case -NFS4ERR_WRONG_TYPE: + case -NFS4ERR_NOMATCHING_LAYOUT: + dprintk("%s Invalid layout error %d\n", __func__, + nfs_err); + pnfs_destroy_layout(NFS_I(inode)); + rpc_wake_up(&tbl->slot_tbl_waitq); + goto reset; + default: + goto reset; + } + } + + if (task->tk_rpc_status == 0) + return 0; + + switch (task->tk_rpc_status) { /* RPC connection errors */ case -ECONNREFUSED: case -EHOSTDOWN: @@ -184,7 +229,8 @@ static int filelayout_async_handle_error(struct rpc_task *task, case -EPROTO: case -ENODEV: dprintk("%s DS connection error %d\n", __func__, - task->tk_status); + task->tk_rpc_status); +ds_unavailable: nfs4_mark_deviceid_unavailable(devid); pnfs_error_mark_layout_for_return(inode, lseg); pnfs_set_lo_fail(lseg); @@ -193,9 +239,11 @@ static int filelayout_async_handle_error(struct rpc_task *task, default: reset: dprintk("%s Retry through MDS. Error %d\n", __func__, - task->tk_status); + task->tk_rpc_status ? task->tk_rpc_status : nfs_err); return -NFS4ERR_RESET_TO_MDS; } + +out_retry: task->tk_status = 0; return -EAGAIN; } @@ -208,7 +256,8 @@ static int filelayout_read_done_cb(struct rpc_task *task, int err; trace_nfs4_pnfs_read(hdr, task->tk_status); - err = filelayout_async_handle_error(task, hdr->args.context->state, + err = filelayout_async_handle_error(task, hdr->res.op_status, + hdr->args.context->state, hdr->ds_clp, hdr->lseg); switch (err) { @@ -318,7 +367,8 @@ static int filelayout_write_done_cb(struct rpc_task *task, int err; trace_nfs4_pnfs_write(hdr, task->tk_status); - err = filelayout_async_handle_error(task, hdr->args.context->state, + err = filelayout_async_handle_error(task, hdr->res.op_status, + hdr->args.context->state, hdr->ds_clp, hdr->lseg); switch (err) { @@ -346,7 +396,8 @@ static int filelayout_commit_done_cb(struct rpc_task *task, int err; trace_nfs4_pnfs_commit_ds(data, task->tk_status); - err = filelayout_async_handle_error(task, NULL, data->ds_clp, + err = filelayout_async_handle_error(task, data->res.op_status, + NULL, data->ds_clp, data->lseg); switch (err) { -- 2.47.1