diff --git a/test/nvme/Makefile b/test/nvme/Makefile index 1659ae863..41bb14c09 100644 --- a/test/nvme/Makefile +++ b/test/nvme/Makefile @@ -36,7 +36,7 @@ include $(SPDK_ROOT_DIR)/mk/spdk.common.mk DIRS-y = aer reset sgl e2edp overhead deallocated_value err_injection \ startup reserve simple_copy connect_stress boot_partition \ - compliance + compliance fused_ordering DIRS-$(CONFIG_NVME_CUSE) += cuse .PHONY: all clean $(DIRS-y) diff --git a/test/nvme/fused_ordering/.gitignore b/test/nvme/fused_ordering/.gitignore new file mode 100644 index 000000000..1f3dc12d2 --- /dev/null +++ b/test/nvme/fused_ordering/.gitignore @@ -0,0 +1 @@ +fused_ordering diff --git a/test/nvme/fused_ordering/Makefile b/test/nvme/fused_ordering/Makefile new file mode 100644 index 000000000..26cf6f7f5 --- /dev/null +++ b/test/nvme/fused_ordering/Makefile @@ -0,0 +1,38 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Samsung Electronics Co., Ltd. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..) + +APP = fused_ordering + +include $(SPDK_ROOT_DIR)/mk/nvme.libtest.mk diff --git a/test/nvme/fused_ordering/fused_ordering.c b/test/nvme/fused_ordering/fused_ordering.c new file mode 100644 index 000000000..9c9a43019 --- /dev/null +++ b/test/nvme/fused_ordering/fused_ordering.c @@ -0,0 +1,231 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#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; +} diff --git a/test/nvmf/nvmf.sh b/test/nvmf/nvmf.sh index 1fa58d35c..0187f460d 100755 --- a/test/nvmf/nvmf.sh +++ b/test/nvmf/nvmf.sh @@ -25,6 +25,7 @@ if [[ $SPDK_TEST_URING -eq 0 ]]; then run_test "nvmf_abort" test/nvmf/target/abort.sh "${TEST_ARGS[@]}" run_test "nvmf_ns_hotplug_stress" test/nvmf/target/ns_hotplug_stress.sh "${TEST_ARGS[@]}" run_test "nvmf_connect_stress" test/nvmf/target/connect_stress.sh "${TEST_ARGS[@]}" + run_test "nvmf_fused_ordering" test/nvmf/target/fused_ordering.sh "${TEST_ARGS[@]}" run_test "nvmf_delete_subsystem" test/nvmf/target/delete_subsystem.sh "${TEST_ARGS[@]}" run_test "nvmf_multicontroller" test/nvmf/host/multicontroller.sh "${TEST_ARGS[@]}" run_test "nvmf_aer" test/nvmf/host/aer.sh "${TEST_ARGS[@]}" diff --git a/test/nvmf/target/fused_ordering.sh b/test/nvmf/target/fused_ordering.sh new file mode 100755 index 000000000..173485f12 --- /dev/null +++ b/test/nvmf/target/fused_ordering.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/nvmf/common.sh + +rpc_py="$rootdir/scripts/rpc.py" + +nvmftestinit +nvmfappstart -m 0x2 + +$rpc_py nvmf_create_transport $NVMF_TRANSPORT_OPTS -u 8192 +$rpc_py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 -m 10 +$rpc_py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t $TEST_TRANSPORT -a $NVMF_FIRST_TARGET_IP -s $NVMF_PORT +$rpc_py bdev_null_create NULL1 1000 512 +$rpc_py bdev_wait_for_examine +$rpc_py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 NULL1 + +$rootdir/test/nvme/fused_ordering/fused_ordering -r "trtype:$TEST_TRANSPORT adrfam:IPv4 traddr:$NVMF_FIRST_TARGET_IP trsvcid:$NVMF_PORT subnqn:nqn.2016-06.io.spdk:cnode1" + +trap - SIGINT SIGTERM EXIT + +nvmftestfini