Commit 45697d33 authored by Jim Harris's avatar Jim Harris
Browse files

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: default avatarJim Harris <james.r.harris@intel.com>
Change-Id: I8c664234660c249fd8ec8d9244eed33502d4103e

Reviewed-on: https://review.gerrithub.io/392528


Tested-by: default avatarSPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Reviewed-by: default avatarDaniel Verkamp <daniel.verkamp@intel.com>
parent d8b8bc43
Loading
Loading
Loading
Loading
+37 −4
Original line number Diff line number Diff line
@@ -50,6 +50,11 @@ struct null_bdev {
	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 void *g_null_read_buf;

@@ -66,8 +71,10 @@ bdev_null_destruct(void *ctx)
}

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) {
	case SPDK_BDEV_IO_TYPE_READ:
		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_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;
	case SPDK_BDEV_IO_TYPE_WRITE:
	case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
	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;
	case SPDK_BDEV_IO_TYPE_FLUSH:
	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;
}

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
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;
}

static void
null_bdev_destroy_cb(void *io_device, void *ctx_buf)
{
	struct null_io_channel *ch = ctx_buf;

	spdk_poller_unregister(&ch->poller);
}

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
	 *  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) {
		goto end;