1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 8 #include "spdk/nvme.h" 9 #include "spdk/env.h" 10 #include "spdk/string.h" 11 #include "spdk/log.h" 12 13 static struct spdk_nvme_ctrlr *g_ctrlr; 14 static struct spdk_nvme_ns *g_ns; 15 static struct spdk_nvme_qpair *g_qpair; 16 static struct spdk_nvme_transport_id g_trid = {}; 17 static uint32_t g_outstanding; 18 19 static void 20 io_complete(void *arg, const struct spdk_nvme_cpl *cpl) 21 { 22 if (spdk_nvme_cpl_is_error(cpl)) { 23 spdk_nvme_print_completion(spdk_nvme_qpair_get_id(g_qpair), 24 (struct spdk_nvme_cpl *)cpl); 25 exit(1); 26 } 27 28 g_outstanding--; 29 } 30 31 #define WRITE_BLOCKS 128 32 #define FUSED_BLOCKS 1 33 34 static void 35 fused_ordering(uint32_t poll_count) 36 { 37 void *cw_buf, *large_buf; 38 int rc; 39 int i; 40 41 g_qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_ctrlr, NULL, 0); 42 if (g_qpair == NULL) { 43 printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 44 exit(1); 45 } 46 47 cw_buf = spdk_zmalloc(FUSED_BLOCKS * 4096, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 48 if (cw_buf == NULL) { 49 printf("ERROR: buffer allocation failed\n"); 50 return; 51 } 52 53 large_buf = spdk_zmalloc(WRITE_BLOCKS * 4096, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, 54 SPDK_MALLOC_DMA); 55 if (large_buf == NULL) { 56 printf("ERROR: buffer allocation failed\n"); 57 return; 58 } 59 60 /* Issue a bunch of relatively large writes - big enough that the data will not fit 61 * in-capsule - followed by the compare command. Then poll the completion queue a number of 62 * times matching the poll_count variable. This adds a variable amount of delay between 63 * the compare and the subsequent fused write submission. 64 * 65 * GitHub issue #2428 showed a problem where once the non-in-capsule data had been fetched from 66 * the host, that request could get sent to the target layer between the two fused commands. This 67 * variable delay would eventually induce this condition before the fix. 68 */ 69 for (i = 0; i < 8; i++) { 70 rc = spdk_nvme_ns_cmd_write(g_ns, g_qpair, large_buf, 0, WRITE_BLOCKS, io_complete, NULL, 0); 71 if (rc != 0) { 72 fprintf(stderr, "starting write I/O failed\n"); 73 exit(1); 74 } 75 g_outstanding++; 76 } 77 78 rc = spdk_nvme_ns_cmd_compare(g_ns, g_qpair, cw_buf, 0, FUSED_BLOCKS, io_complete, NULL, 79 SPDK_NVME_IO_FLAGS_FUSE_FIRST); 80 if (rc != 0) { 81 fprintf(stderr, "starting compare I/O failed\n"); 82 exit(1); 83 } 84 g_outstanding++; 85 while (poll_count--) { 86 spdk_nvme_qpair_process_completions(g_qpair, 0); 87 } 88 89 rc = spdk_nvme_ns_cmd_write(g_ns, g_qpair, cw_buf, 0, FUSED_BLOCKS, io_complete, NULL, 90 SPDK_NVME_IO_FLAGS_FUSE_SECOND); 91 if (rc != 0) { 92 fprintf(stderr, "starting write I/O failed\n"); 93 exit(1); 94 } 95 g_outstanding++; 96 97 while (g_outstanding) { 98 spdk_nvme_qpair_process_completions(g_qpair, 0); 99 } 100 101 spdk_nvme_ctrlr_free_io_qpair(g_qpair); 102 spdk_free(cw_buf); 103 spdk_free(large_buf); 104 } 105 106 static void 107 usage(const char *program_name) 108 { 109 printf("%s [options]", program_name); 110 printf("\t\n"); 111 printf("options:\n"); 112 printf("\t[-r remote NVMe over Fabrics target address]\n"); 113 #ifdef DEBUG 114 printf("\t[-L enable debug logging]\n"); 115 #else 116 printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)\n"); 117 #endif 118 } 119 120 static int 121 parse_args(int argc, char **argv, struct spdk_env_opts *env_opts) 122 { 123 int op, rc; 124 125 while ((op = getopt(argc, argv, "r:L:")) != -1) { 126 switch (op) { 127 case 'r': 128 if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) { 129 fprintf(stderr, "Error parsing transport address\n"); 130 return 1; 131 } 132 break; 133 case 'L': 134 rc = spdk_log_set_flag(optarg); 135 if (rc < 0) { 136 fprintf(stderr, "unknown flag\n"); 137 usage(argv[0]); 138 exit(EXIT_FAILURE); 139 } 140 #ifdef DEBUG 141 spdk_log_set_print_level(SPDK_LOG_DEBUG); 142 #endif 143 break; 144 default: 145 usage(argv[0]); 146 return 1; 147 } 148 } 149 150 return 0; 151 } 152 153 int 154 main(int argc, char **argv) 155 { 156 int rc, i; 157 struct spdk_env_opts opts; 158 struct spdk_nvme_ctrlr_opts ctrlr_opts; 159 int nsid; 160 161 spdk_env_opts_init(&opts); 162 spdk_log_set_print_level(SPDK_LOG_NOTICE); 163 rc = parse_args(argc, argv, &opts); 164 if (rc != 0) { 165 return rc; 166 } 167 168 opts.name = "fused_ordering"; 169 if (spdk_env_init(&opts) < 0) { 170 fprintf(stderr, "Unable to initialize SPDK env\n"); 171 return 1; 172 } 173 174 spdk_nvme_ctrlr_get_default_ctrlr_opts(&ctrlr_opts, sizeof(ctrlr_opts)); 175 ctrlr_opts.keep_alive_timeout_ms = 60 * 1000; 176 g_ctrlr = spdk_nvme_connect(&g_trid, &ctrlr_opts, sizeof(ctrlr_opts)); 177 if (g_ctrlr == NULL) { 178 fprintf(stderr, "spdk_nvme_connect() failed\n"); 179 rc = 1; 180 goto exit; 181 } 182 183 printf("Attached to %s\n", g_trid.subnqn); 184 185 nsid = spdk_nvme_ctrlr_get_first_active_ns(g_ctrlr); 186 if (nsid == 0) { 187 perror("No active namespaces"); 188 exit(1); 189 } 190 g_ns = spdk_nvme_ctrlr_get_ns(g_ctrlr, nsid); 191 192 printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(g_ns), 193 spdk_nvme_ns_get_size(g_ns) / 1000000000); 194 195 for (i = 0; i < 1024; i++) { 196 printf("fused_ordering(%d)\n", i); 197 fused_ordering(i); 198 } 199 200 exit: 201 spdk_nvme_detach(g_ctrlr); 202 spdk_env_fini(); 203 return rc; 204 } 205