nvme_perf: Introduce function pointer table for IO type dependent operations

Introduce a function pointer table to ns_entry to remove if-else
sequence in every operation depending on the type, AIO or NVMe.

This will simplify upcoming DIF and DIX support in the Perf tool.

Change-Id: Ibbd9a9ac3a0b5df529d5b60706ce750a746114c3
Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-on: https://review.gerrithub.io/c/439630
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Chandler-Test-Pool: SPDK Automated Test System <sys_sgsw@intel.com>
This commit is contained in:
Shuhei Matsumoto 2019-01-16 08:23:35 +09:00 committed by Jim Harris
parent f75128a943
commit 07fa7327df

View File

@ -59,8 +59,11 @@ enum entry_type {
ENTRY_TYPE_AIO_FILE,
};
struct ns_fn_table;
struct ns_entry {
enum entry_type type;
const struct ns_fn_table *fn_table;
union {
struct {
@ -151,6 +154,21 @@ struct worker_thread {
unsigned lcore;
};
struct ns_fn_table {
void (*setup_payload)(struct perf_task *task, uint8_t pattern);
int (*submit_io)(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
struct ns_entry *entry, uint64_t offset_in_ios);
void (*check_io)(struct ns_worker_ctx *ns_ctx);
void (*verify_io)(struct perf_task *task, struct ns_entry *entry);
int (*init_ns_worker_ctx)(struct ns_worker_ctx *ns_ctx);
void (*cleanup_ns_worker_ctx)(struct ns_worker_ctx *ns_ctx);
};
static int g_outstanding_commands;
static bool g_latency_ssd_tracking_enable = false;
@ -200,6 +218,17 @@ static void
task_complete(struct perf_task *task);
#if HAVE_LIBAIO
static void
aio_setup_payload(struct perf_task *task, uint8_t pattern)
{
task->buf = spdk_dma_zmalloc(g_io_size_bytes, g_io_align, NULL);
if (task->buf == NULL) {
fprintf(stderr, "spdk_dma_zmalloc() for task->buf failed\n");
exit(1);
}
memset(task->buf, pattern, g_io_size_bytes);
}
static int
aio_submit(io_context_t aio_ctx, struct iocb *iocb, int fd, enum io_iocb_cmd cmd, void *buf,
unsigned long nbytes, uint64_t offset, void *cb_ctx)
@ -220,6 +249,19 @@ aio_submit(io_context_t aio_ctx, struct iocb *iocb, int fd, enum io_iocb_cmd cmd
return 0;
}
static int
aio_submit_io(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
struct ns_entry *entry, uint64_t offset_in_ios)
{
if (task->is_read) {
return aio_submit(ns_ctx->u.aio.ctx, &task->iocb, entry->u.aio.fd, IO_CMD_PREAD, task->buf,
g_io_size_bytes, offset_in_ios * g_io_size_bytes, task);
} else {
return aio_submit(ns_ctx->u.aio.ctx, &task->iocb, entry->u.aio.fd, IO_CMD_PWRITE, task->buf,
g_io_size_bytes, offset_in_ios * g_io_size_bytes, task);
}
}
static void
aio_check_io(struct ns_worker_ctx *ns_ctx)
{
@ -240,6 +282,43 @@ aio_check_io(struct ns_worker_ctx *ns_ctx)
}
}
static void
aio_verify_io(struct perf_task *task, struct ns_entry *entry)
{
}
static int
aio_init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
{
ns_ctx->u.aio.events = calloc(g_queue_depth, sizeof(struct io_event));
if (!ns_ctx->u.aio.events) {
return -1;
}
ns_ctx->u.aio.ctx = 0;
if (io_setup(g_queue_depth, &ns_ctx->u.aio.ctx) < 0) {
free(ns_ctx->u.aio.events);
perror("io_setup");
return -1;
}
return 0;
}
static void
aio_cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
{
io_destroy(ns_ctx->u.aio.ctx);
free(ns_ctx->u.aio.events);
}
static const struct ns_fn_table aio_fn_table = {
.setup_payload = aio_setup_payload,
.submit_io = aio_submit_io,
.check_io = aio_check_io,
.verify_io = aio_verify_io,
.init_ns_worker_ctx = aio_init_ns_worker_ctx,
.cleanup_ns_worker_ctx = aio_cleanup_ns_worker_ctx,
};
static int
register_aio_file(const char *path)
{
@ -295,6 +374,7 @@ register_aio_file(const char *path)
}
entry->type = ENTRY_TYPE_AIO_FILE;
entry->fn_table = &aio_fn_table;
entry->u.aio.fd = fd;
entry->size_in_ios = size / g_io_size_bytes;
entry->io_size_blocks = g_io_size_bytes / blklen;
@ -459,6 +539,105 @@ task_extended_lba_pi_verify(struct ns_entry *entry, struct perf_task *task,
static void io_complete(void *ctx, const struct spdk_nvme_cpl *cpl);
static void
nvme_setup_payload(struct perf_task *task, uint8_t pattern)
{
uint32_t max_io_size_bytes;
/* maximum extended lba format size from all active namespace,
* it's same with g_io_size_bytes for namespace without metadata.
*/
max_io_size_bytes = g_io_size_bytes + g_max_io_md_size * g_max_io_size_blocks;
task->buf = spdk_dma_zmalloc(max_io_size_bytes, g_io_align, NULL);
if (task->buf == NULL) {
fprintf(stderr, "task->buf spdk_dma_zmalloc failed\n");
exit(1);
}
memset(task->buf, pattern, max_io_size_bytes);
}
static int
nvme_submit_io(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
struct ns_entry *entry, uint64_t offset_in_ios)
{
task->lba = offset_in_ios * entry->io_size_blocks;
task_extended_lba_setup_pi(entry, task, task->lba,
entry->io_size_blocks, !task->is_read);
if (task->is_read) {
return spdk_nvme_ns_cmd_read_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair,
task->buf, NULL,
task->lba,
entry->io_size_blocks, io_complete,
task, entry->io_flags,
task->appmask, task->apptag);
} else {
return spdk_nvme_ns_cmd_write_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair,
task->buf, NULL,
task->lba,
entry->io_size_blocks, io_complete,
task, entry->io_flags,
task->appmask, task->apptag);
}
}
static void
nvme_check_io(struct ns_worker_ctx *ns_ctx)
{
spdk_nvme_qpair_process_completions(ns_ctx->u.nvme.qpair, g_max_completions);
}
static void
nvme_verify_io(struct perf_task *task, struct ns_entry *entry)
{
if (spdk_nvme_ns_supports_extended_lba(entry->u.nvme.ns) &&
task->is_read && !g_metacfg_pract_flag) {
task_extended_lba_pi_verify(entry, task, task->lba,
entry->io_size_blocks);
}
}
/*
* TODO: If a controller has multiple namespaces, they could all use the same queue.
* For now, give each namespace/thread combination its own queue.
*/
static int
nvme_init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
{
struct spdk_nvme_io_qpair_opts opts;
struct ns_entry *entry = ns_ctx->entry;
spdk_nvme_ctrlr_get_default_io_qpair_opts(entry->u.nvme.ctrlr, &opts, sizeof(opts));
if (opts.io_queue_requests < entry->num_io_requests) {
opts.io_queue_requests = entry->num_io_requests;
}
ns_ctx->u.nvme.qpair = spdk_nvme_ctrlr_alloc_io_qpair(entry->u.nvme.ctrlr, &opts,
sizeof(opts));
if (!ns_ctx->u.nvme.qpair) {
printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
return -1;
}
return 0;
}
static void
nvme_cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
{
spdk_nvme_ctrlr_free_io_qpair(ns_ctx->u.nvme.qpair);
}
static const struct ns_fn_table nvme_fn_table = {
.setup_payload = nvme_setup_payload,
.submit_io = nvme_submit_io,
.check_io = nvme_check_io,
.verify_io = nvme_verify_io,
.init_ns_worker_ctx = nvme_init_ns_worker_ctx,
.cleanup_ns_worker_ctx = nvme_cleanup_ns_worker_ctx,
};
static void
register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
{
@ -509,6 +688,7 @@ register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
}
entry->type = ENTRY_TYPE_NVME_NS;
entry->fn_table = &nvme_fn_table;
entry->u.nvme.ctrlr = ctrlr;
entry->u.nvme.ns = ns;
entry->num_io_requests = entries;
@ -655,50 +835,17 @@ submit_single_io(struct perf_task *task)
}
}
task->is_read = false;
task->submit_tsc = spdk_get_ticks();
task->lba = offset_in_ios * entry->io_size_blocks;
if ((g_rw_percentage == 100) ||
(g_rw_percentage != 0 && ((rand_r(&seed) % 100) < g_rw_percentage))) {
#if HAVE_LIBAIO
if (entry->type == ENTRY_TYPE_AIO_FILE) {
rc = aio_submit(ns_ctx->u.aio.ctx, &task->iocb, entry->u.aio.fd, IO_CMD_PREAD, task->buf,
g_io_size_bytes, offset_in_ios * g_io_size_bytes, task);
} else
#endif
{
task_extended_lba_setup_pi(entry, task, task->lba,
entry->io_size_blocks, false);
task->is_read = true;
rc = spdk_nvme_ns_cmd_read_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair,
task->buf, NULL,
task->lba,
entry->io_size_blocks, io_complete,
task, entry->io_flags,
task->appmask, task->apptag);
}
task->is_read = true;
} else {
#if HAVE_LIBAIO
if (entry->type == ENTRY_TYPE_AIO_FILE) {
rc = aio_submit(ns_ctx->u.aio.ctx, &task->iocb, entry->u.aio.fd, IO_CMD_PWRITE, task->buf,
g_io_size_bytes, offset_in_ios * g_io_size_bytes, task);
} else
#endif
{
task_extended_lba_setup_pi(entry, task, task->lba,
entry->io_size_blocks, true);
rc = spdk_nvme_ns_cmd_write_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair,
task->buf, NULL,
task->lba,
entry->io_size_blocks, io_complete,
task, entry->io_flags,
task->appmask, task->apptag);
}
task->is_read = false;
}
rc = entry->fn_table->submit_io(task, ns_ctx, entry, offset_in_ios);
if (rc != 0) {
fprintf(stderr, "starting I/O failed\n");
} else {
@ -730,13 +877,7 @@ task_complete(struct perf_task *task)
}
/* add application level verification for end-to-end data protection */
if (entry->type == ENTRY_TYPE_NVME_NS) {
if (spdk_nvme_ns_supports_extended_lba(entry->u.nvme.ns) &&
task->is_read && !g_metacfg_pract_flag) {
task_extended_lba_pi_verify(entry, task, task->lba,
entry->io_size_blocks);
}
}
entry->fn_table->verify_io(task, entry);
/*
* is_draining indicates when time has expired for the test run
@ -769,21 +910,13 @@ io_complete(void *ctx, const struct spdk_nvme_cpl *cpl)
static void
check_io(struct ns_worker_ctx *ns_ctx)
{
#if HAVE_LIBAIO
if (ns_ctx->entry->type == ENTRY_TYPE_AIO_FILE) {
aio_check_io(ns_ctx);
} else
#endif
{
spdk_nvme_qpair_process_completions(ns_ctx->u.nvme.qpair, g_max_completions);
}
ns_ctx->entry->fn_table->check_io(ns_ctx);
}
static struct perf_task *
allocate_task(struct ns_worker_ctx *ns_ctx, int queue_depth)
{
struct perf_task *task;
uint32_t max_io_size_bytes;
task = calloc(1, sizeof(*task));
if (task == NULL) {
@ -791,16 +924,7 @@ allocate_task(struct ns_worker_ctx *ns_ctx, int queue_depth)
exit(1);
}
/* maximum extended lba format size from all active namespace,
* it's same with g_io_size_bytes for namespace without metadata.
*/
max_io_size_bytes = g_io_size_bytes + g_max_io_md_size * g_max_io_size_blocks;
task->buf = spdk_dma_zmalloc(max_io_size_bytes, g_io_align, NULL);
if (task->buf == NULL) {
fprintf(stderr, "task->buf spdk_dma_zmalloc failed\n");
exit(1);
}
memset(task->buf, queue_depth % 8 + 1, max_io_size_bytes);
ns_ctx->entry->fn_table->setup_payload(task, queue_depth % 8 + 1);
task->ns_ctx = ns_ctx;
@ -821,53 +945,13 @@ submit_io(struct ns_worker_ctx *ns_ctx, int queue_depth)
static int
init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
{
if (ns_ctx->entry->type == ENTRY_TYPE_AIO_FILE) {
#ifdef HAVE_LIBAIO
ns_ctx->u.aio.events = calloc(g_queue_depth, sizeof(struct io_event));
if (!ns_ctx->u.aio.events) {
return -1;
}
ns_ctx->u.aio.ctx = 0;
if (io_setup(g_queue_depth, &ns_ctx->u.aio.ctx) < 0) {
free(ns_ctx->u.aio.events);
perror("io_setup");
return -1;
}
#endif
} else {
/*
* TODO: If a controller has multiple namespaces, they could all use the same queue.
* For now, give each namespace/thread combination its own queue.
*/
struct spdk_nvme_io_qpair_opts opts;
spdk_nvme_ctrlr_get_default_io_qpair_opts(ns_ctx->entry->u.nvme.ctrlr, &opts, sizeof(opts));
if (opts.io_queue_requests < ns_ctx->entry->num_io_requests) {
opts.io_queue_requests = ns_ctx->entry->num_io_requests;
}
ns_ctx->u.nvme.qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_ctx->entry->u.nvme.ctrlr, &opts,
sizeof(opts));
if (!ns_ctx->u.nvme.qpair) {
printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
return -1;
}
}
return 0;
return ns_ctx->entry->fn_table->init_ns_worker_ctx(ns_ctx);
}
static void
cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
{
if (ns_ctx->entry->type == ENTRY_TYPE_AIO_FILE) {
#ifdef HAVE_LIBAIO
io_destroy(ns_ctx->u.aio.ctx);
free(ns_ctx->u.aio.events);
#endif
} else {
spdk_nvme_ctrlr_free_io_qpair(ns_ctx->u.nvme.qpair);
}
ns_ctx->entry->fn_table->cleanup_ns_worker_ctx(ns_ctx);
}
static int