bdev: allow different ways of handling nomem IOs

This is a preparation for reusing the code handling nomem_io for
other type of NOMEM errors (e.g. from pull/push/append_copy).  This
patch doesn't actually change anything functionally - only IOs completed
by a module with SPDK_BDEV_IO_STATUS_NOMEM status are retried.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: I12ecb2efcf2d2cdf75b302f9f766b4c16ac99f3e
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/17676
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Community-CI: Mellanox Build Bot
This commit is contained in:
Konrad Sztyber 2023-04-18 12:17:33 +02:00 committed by David Ko
parent 268f5ee272
commit 72345f3a69
2 changed files with 30 additions and 9 deletions

View File

@ -953,6 +953,9 @@ struct spdk_bdev_io {
/** Indicates whether the IO is split */
bool split;
/** Retry state (resubmit, re-pull, re-push, etc.) */
uint8_t retry_state;
/** bdev allocated memory associated with this request */
void *buf;

View File

@ -366,6 +366,11 @@ struct spdk_bdev_io_error_stat {
uint32_t error_status[-SPDK_MIN_BDEV_IO_STATUS];
};
enum bdev_io_retry_state {
BDEV_IO_RETRY_STATE_INVALID,
BDEV_IO_RETRY_STATE_SUBMIT,
};
#define __bdev_to_io_dev(bdev) (((char *)bdev) + 1)
#define __bdev_from_io_dev(io_dev) ((struct spdk_bdev *)(((char *)io_dev) - 1))
#define __io_ch_to_bdev_ch(io_ch) ((struct spdk_bdev_channel *)spdk_io_channel_get_ctx(io_ch))
@ -917,7 +922,7 @@ bdev_io_use_accel_sequence(struct spdk_bdev_io *bdev_io)
static inline void
bdev_queue_nomem_io_head(struct spdk_bdev_shared_resource *shared_resource,
struct spdk_bdev_io *bdev_io)
struct spdk_bdev_io *bdev_io, enum bdev_io_retry_state state)
{
/* Wait for some of the outstanding I/O to complete before we retry any of the nomem_io.
* Normally we will wait for NOMEM_THRESHOLD_COUNT I/O to complete but for low queue depth
@ -926,17 +931,21 @@ bdev_queue_nomem_io_head(struct spdk_bdev_shared_resource *shared_resource,
shared_resource->nomem_threshold = spdk_max((int64_t)shared_resource->io_outstanding / 2,
(int64_t)shared_resource->io_outstanding - NOMEM_THRESHOLD_COUNT);
assert(state != BDEV_IO_RETRY_STATE_INVALID);
bdev_io->internal.retry_state = state;
TAILQ_INSERT_HEAD(&shared_resource->nomem_io, bdev_io, internal.link);
}
static inline void
bdev_queue_nomem_io_tail(struct spdk_bdev_shared_resource *shared_resource,
struct spdk_bdev_io *bdev_io)
struct spdk_bdev_io *bdev_io, enum bdev_io_retry_state state)
{
/* We only queue IOs at the end of the nomem_io queue if they're submitted by the user while
* the queue isn't empty, so we don't need to update the nomem_threshold here */
assert(!TAILQ_EMPTY(&shared_resource->nomem_io));
assert(state != BDEV_IO_RETRY_STATE_INVALID);
bdev_io->internal.retry_state = state;
TAILQ_INSERT_TAIL(&shared_resource->nomem_io, bdev_io, internal.link);
}
@ -1360,7 +1369,16 @@ bdev_ch_retry_io(struct spdk_bdev_channel *bdev_ch)
while (!TAILQ_EMPTY(&shared_resource->nomem_io)) {
bdev_io = TAILQ_FIRST(&shared_resource->nomem_io);
TAILQ_REMOVE(&shared_resource->nomem_io, bdev_io, internal.link);
switch (bdev_io->internal.retry_state) {
case BDEV_IO_RETRY_STATE_SUBMIT:
bdev_ch_resubmit_io(bdev_ch, bdev_io);
break;
default:
assert(0 && "invalid retry state");
break;
}
if (bdev_io == TAILQ_FIRST(&shared_resource->nomem_io)) {
/* This IO completed again with NOMEM status, so break the loop and
* don't try anymore. Note that a bdev_io that fails with NOMEM
@ -1383,14 +1401,14 @@ _bdev_io_decrement_outstanding(struct spdk_bdev_channel *bdev_ch,
}
static inline bool
_bdev_io_handle_no_mem(struct spdk_bdev_io *bdev_io)
_bdev_io_handle_no_mem(struct spdk_bdev_io *bdev_io, enum bdev_io_retry_state state)
{
struct spdk_bdev_channel *bdev_ch = bdev_io->internal.ch;
struct spdk_bdev_shared_resource *shared_resource = bdev_ch->shared_resource;
if (spdk_unlikely(bdev_io->internal.status == SPDK_BDEV_IO_STATUS_NOMEM)) {
bdev_io->internal.status = SPDK_BDEV_IO_STATUS_PENDING;
bdev_queue_nomem_io_head(shared_resource, bdev_io);
bdev_queue_nomem_io_head(shared_resource, bdev_io, state);
/* If bdev module completed an I/O that has an accel sequence with NOMEM status, the
* ownership of that sequence is transferred back to the bdev layer, so we need to
@ -1429,7 +1447,7 @@ _bdev_io_complete_push_bounce_done(void *ctx, int rc)
/* Continue with IO completion flow */
_bdev_io_decrement_outstanding(bdev_ch, shared_resource);
if (spdk_unlikely(_bdev_io_handle_no_mem(bdev_io))) {
if (spdk_unlikely(_bdev_io_handle_no_mem(bdev_io, BDEV_IO_RETRY_STATE_INVALID))) {
return;
}
@ -2507,7 +2525,7 @@ bdev_io_do_submit(struct spdk_bdev_channel *bdev_ch, struct spdk_bdev_io *bdev_i
bdev_submit_request(bdev, ch, bdev_io);
bdev_io->internal.in_submit_request = false;
} else {
bdev_queue_nomem_io_tail(shared_resource, bdev_io);
bdev_queue_nomem_io_tail(shared_resource, bdev_io, BDEV_IO_RETRY_STATE_SUBMIT);
}
}
@ -6943,7 +6961,7 @@ bdev_io_complete_sequence_cb(void *ctx, int status)
}
_bdev_io_decrement_outstanding(bdev_ch, shared_resource);
if (spdk_unlikely(_bdev_io_handle_no_mem(bdev_io))) {
if (spdk_unlikely(_bdev_io_handle_no_mem(bdev_io, BDEV_IO_RETRY_STATE_INVALID))) {
return;
}
@ -6997,7 +7015,7 @@ spdk_bdev_io_complete(struct spdk_bdev_io *bdev_io, enum spdk_bdev_io_status sta
}
_bdev_io_decrement_outstanding(bdev_ch, shared_resource);
if (spdk_unlikely(_bdev_io_handle_no_mem(bdev_io))) {
if (spdk_unlikely(_bdev_io_handle_no_mem(bdev_io, BDEV_IO_RETRY_STATE_SUBMIT))) {
return;
}
}