bdev/null: complete requests asynchronously

To better match bdev modules like nvme, complete requests
for the bdev/null driver asynchronously.  This will be
done by allocating IO channels that register a poller
and keep a TAILQ of bdev IO to be completed next time
the poller runs.

This is actually more efficient as well, since completing
I/O in submit_request context defers the completion using
an event.  A benchmark of bdevperf with split running on
top of null module shows this patch increases throughput
20%.

Signed-off-by: Jim Harris <james.r.harris@intel.com>
Change-Id: I8c664234660c249fd8ec8d9244eed33502d4103e

Reviewed-on: https://review.gerrithub.io/392528
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Jim Harris 2017-12-19 20:30:20 -07:00
parent d8b8bc4353
commit 45697d33ff

View File

@ -50,6 +50,11 @@ struct null_bdev {
TAILQ_ENTRY(null_bdev) tailq; TAILQ_ENTRY(null_bdev) tailq;
}; };
struct null_io_channel {
struct spdk_poller *poller;
TAILQ_HEAD(, spdk_bdev_io) io;
};
static TAILQ_HEAD(, null_bdev) g_null_bdev_head; static TAILQ_HEAD(, null_bdev) g_null_bdev_head;
static void *g_null_read_buf; static void *g_null_read_buf;
@ -66,8 +71,10 @@ bdev_null_destruct(void *ctx)
} }
static void static void
bdev_null_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io) bdev_null_submit_request(struct spdk_io_channel *_ch, struct spdk_bdev_io *bdev_io)
{ {
struct null_io_channel *ch = spdk_io_channel_get_ctx(_ch);
switch (bdev_io->type) { switch (bdev_io->type) {
case SPDK_BDEV_IO_TYPE_READ: case SPDK_BDEV_IO_TYPE_READ:
if (bdev_io->u.bdev.iovs[0].iov_base == NULL) { if (bdev_io->u.bdev.iovs[0].iov_base == NULL) {
@ -75,12 +82,12 @@ bdev_null_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_i
bdev_io->u.bdev.iovs[0].iov_base = g_null_read_buf; bdev_io->u.bdev.iovs[0].iov_base = g_null_read_buf;
bdev_io->u.bdev.iovs[0].iov_len = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen; bdev_io->u.bdev.iovs[0].iov_len = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen;
} }
spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS); TAILQ_INSERT_TAIL(&ch->io, bdev_io, module_link);
break; break;
case SPDK_BDEV_IO_TYPE_WRITE: case SPDK_BDEV_IO_TYPE_WRITE:
case SPDK_BDEV_IO_TYPE_WRITE_ZEROES: case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
case SPDK_BDEV_IO_TYPE_RESET: case SPDK_BDEV_IO_TYPE_RESET:
spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS); TAILQ_INSERT_TAIL(&ch->io, bdev_io, module_link);
break; break;
case SPDK_BDEV_IO_TYPE_FLUSH: case SPDK_BDEV_IO_TYPE_FLUSH:
case SPDK_BDEV_IO_TYPE_UNMAP: case SPDK_BDEV_IO_TYPE_UNMAP:
@ -168,15 +175,40 @@ create_null_bdev(const char *name, uint64_t num_blocks, uint32_t block_size)
return &bdev->bdev; return &bdev->bdev;
} }
static void
null_io_poll(void *arg)
{
struct null_io_channel *ch = arg;
TAILQ_HEAD(, spdk_bdev_io) io;
struct spdk_bdev_io *bdev_io;
TAILQ_INIT(&io);
TAILQ_SWAP(&ch->io, &io, spdk_bdev_io, module_link);
while (!TAILQ_EMPTY(&io)) {
bdev_io = TAILQ_FIRST(&io);
TAILQ_REMOVE(&io, bdev_io, module_link);
spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS);
}
}
static int static int
null_bdev_create_cb(void *io_device, void *ctx_buf) null_bdev_create_cb(void *io_device, void *ctx_buf)
{ {
struct null_io_channel *ch = ctx_buf;
TAILQ_INIT(&ch->io);
ch->poller = spdk_poller_register(null_io_poll, ch, 0);
return 0; return 0;
} }
static void static void
null_bdev_destroy_cb(void *io_device, void *ctx_buf) null_bdev_destroy_cb(void *io_device, void *ctx_buf)
{ {
struct null_io_channel *ch = ctx_buf;
spdk_poller_unregister(&ch->poller);
} }
static int static int
@ -201,7 +233,8 @@ bdev_null_initialize(void)
* We need to pick some unique address as our "io device" - so just use the * We need to pick some unique address as our "io device" - so just use the
* address of the global tailq. * address of the global tailq.
*/ */
spdk_io_device_register(&g_null_bdev_head, null_bdev_create_cb, null_bdev_destroy_cb, 0); spdk_io_device_register(&g_null_bdev_head, null_bdev_create_cb, null_bdev_destroy_cb,
sizeof(struct null_io_channel));
if (sp == NULL) { if (sp == NULL) {
goto end; goto end;