Commit a79f194a authored by Trond Myklebust's avatar Trond Myklebust

NFSv4/flexfiles: Abort I/O early if the layout segment was invalidated

If a layout segment gets invalidated while a pNFS I/O operation
is queued for transmission, then we ideally want to abort
immediately. This is particularly the case when there is a large
number of I/O related RPCs queued in the RPC layer, and the layout
segment gets invalidated due to an ENOSPC error, or an EACCES (because
the client was fenced). We may end up forced to spam the MDS with a
lot of otherwise unnecessary LAYOUTERRORs after that I/O fails.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent 39a5201a
...@@ -1071,6 +1071,8 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task, ...@@ -1071,6 +1071,8 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,
break; break;
case -NFS4ERR_RETRY_UNCACHED_REP: case -NFS4ERR_RETRY_UNCACHED_REP:
break; break;
case -EAGAIN:
return -NFS4ERR_RESET_TO_PNFS;
/* Invalidate Layout errors */ /* Invalidate Layout errors */
case -NFS4ERR_PNFS_NO_LAYOUT: case -NFS4ERR_PNFS_NO_LAYOUT:
case -ESTALE: /* mapped NFS4ERR_STALE */ case -ESTALE: /* mapped NFS4ERR_STALE */
...@@ -1131,6 +1133,7 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task, ...@@ -1131,6 +1133,7 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task,
case -EBADHANDLE: case -EBADHANDLE:
case -ELOOP: case -ELOOP:
case -ENOSPC: case -ENOSPC:
case -EAGAIN:
break; break;
case -EJUKEBOX: case -EJUKEBOX:
nfs_inc_stats(lseg->pls_layout->plh_inode, NFSIOS_DELAY); nfs_inc_stats(lseg->pls_layout->plh_inode, NFSIOS_DELAY);
...@@ -1369,6 +1372,16 @@ static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data) ...@@ -1369,6 +1372,16 @@ static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data)
ff_layout_read_prepare_common(task, hdr); ff_layout_read_prepare_common(task, hdr);
} }
static void
ff_layout_io_prepare_transmit(struct rpc_task *task,
void *data)
{
struct nfs_pgio_header *hdr = data;
if (!pnfs_is_valid_lseg(hdr->lseg))
rpc_exit(task, -EAGAIN);
}
static void ff_layout_read_call_done(struct rpc_task *task, void *data) static void ff_layout_read_call_done(struct rpc_task *task, void *data)
{ {
struct nfs_pgio_header *hdr = data; struct nfs_pgio_header *hdr = data;
...@@ -1657,6 +1670,7 @@ static void ff_layout_commit_release(void *data) ...@@ -1657,6 +1670,7 @@ static void ff_layout_commit_release(void *data)
static const struct rpc_call_ops ff_layout_read_call_ops_v3 = { static const struct rpc_call_ops ff_layout_read_call_ops_v3 = {
.rpc_call_prepare = ff_layout_read_prepare_v3, .rpc_call_prepare = ff_layout_read_prepare_v3,
.rpc_call_prepare_transmit = ff_layout_io_prepare_transmit,
.rpc_call_done = ff_layout_read_call_done, .rpc_call_done = ff_layout_read_call_done,
.rpc_count_stats = ff_layout_read_count_stats, .rpc_count_stats = ff_layout_read_count_stats,
.rpc_release = ff_layout_read_release, .rpc_release = ff_layout_read_release,
...@@ -1664,6 +1678,7 @@ static const struct rpc_call_ops ff_layout_read_call_ops_v3 = { ...@@ -1664,6 +1678,7 @@ static const struct rpc_call_ops ff_layout_read_call_ops_v3 = {
static const struct rpc_call_ops ff_layout_read_call_ops_v4 = { static const struct rpc_call_ops ff_layout_read_call_ops_v4 = {
.rpc_call_prepare = ff_layout_read_prepare_v4, .rpc_call_prepare = ff_layout_read_prepare_v4,
.rpc_call_prepare_transmit = ff_layout_io_prepare_transmit,
.rpc_call_done = ff_layout_read_call_done, .rpc_call_done = ff_layout_read_call_done,
.rpc_count_stats = ff_layout_read_count_stats, .rpc_count_stats = ff_layout_read_count_stats,
.rpc_release = ff_layout_read_release, .rpc_release = ff_layout_read_release,
...@@ -1671,6 +1686,7 @@ static const struct rpc_call_ops ff_layout_read_call_ops_v4 = { ...@@ -1671,6 +1686,7 @@ static const struct rpc_call_ops ff_layout_read_call_ops_v4 = {
static const struct rpc_call_ops ff_layout_write_call_ops_v3 = { static const struct rpc_call_ops ff_layout_write_call_ops_v3 = {
.rpc_call_prepare = ff_layout_write_prepare_v3, .rpc_call_prepare = ff_layout_write_prepare_v3,
.rpc_call_prepare_transmit = ff_layout_io_prepare_transmit,
.rpc_call_done = ff_layout_write_call_done, .rpc_call_done = ff_layout_write_call_done,
.rpc_count_stats = ff_layout_write_count_stats, .rpc_count_stats = ff_layout_write_count_stats,
.rpc_release = ff_layout_write_release, .rpc_release = ff_layout_write_release,
...@@ -1678,6 +1694,7 @@ static const struct rpc_call_ops ff_layout_write_call_ops_v3 = { ...@@ -1678,6 +1694,7 @@ static const struct rpc_call_ops ff_layout_write_call_ops_v3 = {
static const struct rpc_call_ops ff_layout_write_call_ops_v4 = { static const struct rpc_call_ops ff_layout_write_call_ops_v4 = {
.rpc_call_prepare = ff_layout_write_prepare_v4, .rpc_call_prepare = ff_layout_write_prepare_v4,
.rpc_call_prepare_transmit = ff_layout_io_prepare_transmit,
.rpc_call_done = ff_layout_write_call_done, .rpc_call_done = ff_layout_write_call_done,
.rpc_count_stats = ff_layout_write_count_stats, .rpc_count_stats = ff_layout_write_count_stats,
.rpc_release = ff_layout_write_release, .rpc_release = ff_layout_write_release,
......
...@@ -97,6 +97,7 @@ typedef void (*rpc_action)(struct rpc_task *); ...@@ -97,6 +97,7 @@ typedef void (*rpc_action)(struct rpc_task *);
struct rpc_call_ops { struct rpc_call_ops {
void (*rpc_call_prepare)(struct rpc_task *, void *); void (*rpc_call_prepare)(struct rpc_task *, void *);
void (*rpc_call_prepare_transmit)(struct rpc_task *, void *);
void (*rpc_call_done)(struct rpc_task *, void *); void (*rpc_call_done)(struct rpc_task *, void *);
void (*rpc_count_stats)(struct rpc_task *, void *); void (*rpc_count_stats)(struct rpc_task *, void *);
void (*rpc_release)(void *); void (*rpc_release)(void *);
......
...@@ -1330,6 +1330,13 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task) ...@@ -1330,6 +1330,13 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task)
status = -EBADMSG; status = -EBADMSG;
goto out_dequeue; goto out_dequeue;
} }
if (task->tk_ops->rpc_call_prepare_transmit) {
task->tk_ops->rpc_call_prepare_transmit(task,
task->tk_calldata);
status = task->tk_status;
if (status < 0)
goto out_dequeue;
}
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment