Spdk/test/nvme/fused_ordering/fused_ordering.c

205 lines
4.9 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2022 Intel Corporation.
* All rights reserved.
*/
#include "spdk/stdinc.h"
#include "spdk/nvme.h"
#include "spdk/env.h"
#include "spdk/string.h"
#include "spdk/log.h"
static struct spdk_nvme_ctrlr *g_ctrlr;
static struct spdk_nvme_ns *g_ns;
static struct spdk_nvme_qpair *g_qpair;
static struct spdk_nvme_transport_id g_trid = {};
static uint32_t g_outstanding;
static void
io_complete(void *arg, const struct spdk_nvme_cpl *cpl)
{
if (spdk_nvme_cpl_is_error(cpl)) {
spdk_nvme_print_completion(spdk_nvme_qpair_get_id(g_qpair),
(struct spdk_nvme_cpl *)cpl);
exit(1);
}
g_outstanding--;
}
#define WRITE_BLOCKS 128
#define FUSED_BLOCKS 1
static void
fused_ordering(uint32_t poll_count)
{
void *cw_buf, *large_buf;
int rc;
int i;
g_qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_ctrlr, NULL, 0);
if (g_qpair == NULL) {
printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
exit(1);
}
cw_buf = spdk_zmalloc(FUSED_BLOCKS * 4096, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA);
if (cw_buf == NULL) {
printf("ERROR: buffer allocation failed\n");
return;
}
large_buf = spdk_zmalloc(WRITE_BLOCKS * 4096, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY,
SPDK_MALLOC_DMA);
if (large_buf == NULL) {
printf("ERROR: buffer allocation failed\n");
return;
}
/* Issue a bunch of relatively large writes - big enough that the data will not fit
* in-capsule - followed by the compare command. Then poll the completion queue a number of
* times matching the poll_count variable. This adds a variable amount of delay between
* the compare and the subsequent fused write submission.
*
* GitHub issue #2428 showed a problem where once the non-in-capsule data had been fetched from
* the host, that request could get sent to the target layer between the two fused commands. This
* variable delay would eventually induce this condition before the fix.
*/
for (i = 0; i < 8; i++) {
rc = spdk_nvme_ns_cmd_write(g_ns, g_qpair, large_buf, 0, WRITE_BLOCKS, io_complete, NULL, 0);
if (rc != 0) {
fprintf(stderr, "starting write I/O failed\n");
exit(1);
}
g_outstanding++;
}
rc = spdk_nvme_ns_cmd_compare(g_ns, g_qpair, cw_buf, 0, FUSED_BLOCKS, io_complete, NULL,
SPDK_NVME_IO_FLAGS_FUSE_FIRST);
if (rc != 0) {
fprintf(stderr, "starting compare I/O failed\n");
exit(1);
}
g_outstanding++;
while (poll_count--) {
spdk_nvme_qpair_process_completions(g_qpair, 0);
}
rc = spdk_nvme_ns_cmd_write(g_ns, g_qpair, cw_buf, 0, FUSED_BLOCKS, io_complete, NULL,
SPDK_NVME_IO_FLAGS_FUSE_SECOND);
if (rc != 0) {
fprintf(stderr, "starting write I/O failed\n");
exit(1);
}
g_outstanding++;
while (g_outstanding) {
spdk_nvme_qpair_process_completions(g_qpair, 0);
}
spdk_nvme_ctrlr_free_io_qpair(g_qpair);
spdk_free(cw_buf);
spdk_free(large_buf);
}
static void
usage(const char *program_name)
{
printf("%s [options]", program_name);
printf("\t\n");
printf("options:\n");
printf("\t[-r remote NVMe over Fabrics target address]\n");
#ifdef DEBUG
printf("\t[-L enable debug logging]\n");
#else
printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n");
#endif
}
static int
parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
{
int op, rc;
while ((op = getopt(argc, argv, "r:L:")) != -1) {
switch (op) {
case 'r':
if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
fprintf(stderr, "Error parsing transport address\n");
return 1;
}
break;
case 'L':
rc = spdk_log_set_flag(optarg);
if (rc < 0) {
fprintf(stderr, "unknown flag\n");
usage(argv[0]);
exit(EXIT_FAILURE);
}
#ifdef DEBUG
spdk_log_set_print_level(SPDK_LOG_DEBUG);
#endif
break;
default:
usage(argv[0]);
return 1;
}
}
return 0;
}
int
main(int argc, char **argv)
{
int rc, i;
struct spdk_env_opts opts;
struct spdk_nvme_ctrlr_opts ctrlr_opts;
int nsid;
spdk_env_opts_init(&opts);
spdk_log_set_print_level(SPDK_LOG_NOTICE);
rc = parse_args(argc, argv, &opts);
if (rc != 0) {
return rc;
}
opts.name = "fused_ordering";
if (spdk_env_init(&opts) < 0) {
fprintf(stderr, "Unable to initialize SPDK env\n");
return 1;
}
spdk_nvme_ctrlr_get_default_ctrlr_opts(&ctrlr_opts, sizeof(ctrlr_opts));
ctrlr_opts.keep_alive_timeout_ms = 60 * 1000;
g_ctrlr = spdk_nvme_connect(&g_trid, &ctrlr_opts, sizeof(ctrlr_opts));
if (g_ctrlr == NULL) {
fprintf(stderr, "spdk_nvme_connect() failed\n");
rc = 1;
goto exit;
}
printf("Attached to %s\n", g_trid.subnqn);
nsid = spdk_nvme_ctrlr_get_first_active_ns(g_ctrlr);
if (nsid == 0) {
perror("No active namespaces");
exit(1);
}
g_ns = spdk_nvme_ctrlr_get_ns(g_ctrlr, nsid);
printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(g_ns),
spdk_nvme_ns_get_size(g_ns) / 1000000000);
for (i = 0; i < 1024; i++) {
printf("fused_ordering(%d)\n", i);
fused_ordering(i);
}
exit:
spdk_nvme_detach(g_ctrlr);
spdk_env_fini();
return rc;
}