Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2022 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 : : #include "spdk/util.h"
13 : : #include "spdk/likely.h"
14 : :
15 : : #define WRITE_BLOCKS 128
16 : : #define FUSED_BLOCKS 1
17 : : #define NO_WRITE_CMDS 8
18 : :
19 : : struct worker_thread {
20 : : TAILQ_ENTRY(worker_thread) link;
21 : : unsigned lcore;
22 : : void *cw_buf;
23 : : void *large_buf;
24 : : struct spdk_nvme_qpair *qpair;
25 : : uint32_t poll_count;
26 : : uint32_t outstanding;
27 : : int status;
28 : : };
29 : :
30 : : static struct spdk_nvme_ctrlr *g_ctrlr;
31 : : static struct spdk_nvme_ns *g_ns;
32 : : static struct spdk_nvme_transport_id g_trid = {};
33 : : static uint32_t g_num_workers = 0;
34 : : static TAILQ_HEAD(, worker_thread) g_workers = TAILQ_HEAD_INITIALIZER(g_workers);
35 : :
36 : :
37 : : static void
38 : 30720 : io_complete(void *arg, const struct spdk_nvme_cpl *cpl)
39 : : {
40 : 30720 : struct worker_thread *worker = arg;
41 : :
42 [ + - - + ]: 30720 : if (spdk_nvme_cpl_is_error(cpl)) {
43 : 0 : spdk_nvme_print_completion(spdk_nvme_qpair_get_id(worker->qpair),
44 : : (struct spdk_nvme_cpl *)cpl);
45 : 0 : exit(1);
46 : : }
47 : :
48 : 30720 : worker->outstanding--;
49 : 30720 : }
50 : :
51 : : static int
52 : 3 : register_workers(void)
53 : : {
54 : : uint32_t i;
55 : : struct worker_thread *worker;
56 : :
57 [ + + ]: 6 : SPDK_ENV_FOREACH_CORE(i) {
58 : 3 : worker = calloc(1, sizeof(*worker));
59 [ - + ]: 3 : if (worker == NULL) {
60 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate worker\n");
61 : 0 : return -1;
62 : : }
63 : :
64 : 3 : worker->lcore = i;
65 : 3 : TAILQ_INSERT_TAIL(&g_workers, worker, link);
66 : 3 : g_num_workers++;
67 : : }
68 : :
69 : 3 : return 0;
70 : : }
71 : :
72 : : static void
73 : 3 : unregister_workers(void)
74 : : {
75 : : struct worker_thread *worker, *tmp_worker;
76 : :
77 [ + + ]: 6 : TAILQ_FOREACH_SAFE(worker, &g_workers, link, tmp_worker) {
78 [ - + ]: 3 : TAILQ_REMOVE(&g_workers, worker, link);
79 : 3 : free(worker);
80 : : }
81 : 3 : }
82 : :
83 : : static unsigned
84 : 3 : init_workers(void)
85 : : {
86 : 3 : void *cw_buf = NULL, *large_buf = NULL;
87 : : struct worker_thread *worker;
88 : 3 : int rc = 0;
89 : :
90 [ - + ]: 3 : assert(g_num_workers);
91 : :
92 : 3 : cw_buf = spdk_zmalloc(FUSED_BLOCKS * 4096, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY,
93 : : SPDK_MALLOC_DMA);
94 [ - + ]: 3 : if (cw_buf == NULL) {
95 [ # # ]: 0 : printf("ERROR: buffer allocation failed.\n");
96 : 0 : rc = -1;
97 : 0 : goto error;
98 : : }
99 : :
100 : 3 : large_buf = spdk_zmalloc(WRITE_BLOCKS * 4096, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY,
101 : : SPDK_MALLOC_DMA);
102 [ - + ]: 3 : if (large_buf == NULL) {
103 [ # # ]: 0 : printf("ERROR: buffer allocation failed.\n");
104 : 0 : rc = -1;
105 : 0 : goto error;
106 : : }
107 : :
108 [ + + ]: 6 : TAILQ_FOREACH(worker, &g_workers, link) {
109 : 3 : worker->qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_ctrlr, NULL, 0);
110 [ - + ]: 3 : if (worker->qpair == NULL) {
111 [ # # ]: 0 : printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed.\n");
112 : 0 : rc = -1;
113 : 0 : goto error;
114 : : }
115 : 3 : worker->cw_buf = cw_buf;
116 : 3 : worker->large_buf = large_buf;
117 : : }
118 : 3 : goto exit;
119 : :
120 : 0 : error:
121 [ # # ]: 0 : TAILQ_FOREACH(worker, &g_workers, link) {
122 : 0 : spdk_nvme_ctrlr_free_io_qpair(worker->qpair);
123 : : }
124 : 0 : spdk_free(large_buf);
125 : 0 : spdk_free(cw_buf);
126 : 3 : exit:
127 : 3 : return rc;
128 : : }
129 : :
130 : : static void
131 : 3 : fini_workers(void)
132 : : {
133 : 3 : void *cw_buf = NULL, *large_buf = NULL;
134 : : struct worker_thread *worker;
135 : :
136 [ + + ]: 6 : TAILQ_FOREACH(worker, &g_workers, link) {
137 : 3 : spdk_nvme_ctrlr_free_io_qpair(worker->qpair);
138 : 3 : cw_buf = worker->cw_buf;
139 : 3 : large_buf = worker->large_buf;
140 : : }
141 : :
142 : 3 : spdk_free(large_buf);
143 : 3 : spdk_free(cw_buf);
144 : 3 : }
145 : :
146 : : static int
147 : 3072 : fused_ordering(void *arg)
148 : : {
149 : 3072 : struct worker_thread *worker = (struct worker_thread *)arg;
150 : : uint32_t i;
151 : 3072 : uint32_t rc = 0;
152 : :
153 : : /* Issue relatively large writes - big enough that the data will not fit
154 : : * in-capsule - followed by the compare command. Then poll the completion queue a number of
155 : : * times matching the poll_count variable. This adds a variable amount of delay between
156 : : * the compare and the subsequent fused write submission.
157 : : *
158 : : * GitHub issue #2428 showed a problem where once the non-in-capsule data had been fetched from
159 : : * the host, that request could get sent to the target layer between the two fused commands. This
160 : : * variable delay would eventually induce this condition before the fix.
161 : : */
162 : : /* Submit 8 write commands per queue */
163 [ + + ]: 27648 : for (i = 0; i < NO_WRITE_CMDS; i++) {
164 : 24576 : rc = spdk_nvme_ns_cmd_write(g_ns, worker->qpair, worker->large_buf,
165 : : 0,
166 : : WRITE_BLOCKS, io_complete,
167 : : worker,
168 : : 0);
169 [ - + ]: 24576 : if (rc != 0) {
170 [ # # # # ]: 0 : fprintf(stderr, "starting write I/O failed\n");
171 : 0 : goto out;
172 : : }
173 : :
174 : 24576 : worker->outstanding++;
175 : : }
176 : :
177 : : /* Submit first fuse command, per queue */
178 : 3072 : rc = spdk_nvme_ns_cmd_compare(g_ns, worker->qpair, worker->cw_buf,
179 : : 0,
180 : : FUSED_BLOCKS, io_complete,
181 : : worker,
182 : : SPDK_NVME_IO_FLAGS_FUSE_FIRST);
183 [ - + ]: 3072 : if (rc != 0) {
184 [ # # # # ]: 0 : fprintf(stderr, "starting compare I/O failed\n");
185 : 0 : goto out;
186 : : }
187 : :
188 : 3072 : worker->outstanding++;
189 : :
190 : : /* Process completions */
191 [ + + ]: 1574400 : while (worker->poll_count-- > 0) {
192 : 1571328 : spdk_nvme_qpair_process_completions(worker->qpair, 0);
193 : : }
194 : :
195 : : /* Submit second fuse command, one per queue */
196 : 3072 : rc = spdk_nvme_ns_cmd_write(g_ns, worker->qpair, worker->cw_buf, 0,
197 : : FUSED_BLOCKS, io_complete,
198 : : worker,
199 : : SPDK_NVME_IO_FLAGS_FUSE_SECOND);
200 [ - + ]: 3072 : if (rc != 0) {
201 [ # # # # ]: 0 : fprintf(stderr, "starting write I/O failed\n");
202 : 0 : goto out;
203 : : }
204 : :
205 : 3072 : worker->outstanding++;
206 : :
207 : : /* Process completions */
208 [ + + ]: 401884 : while (worker->outstanding > 0) {
209 : 398812 : spdk_nvme_qpair_process_completions(worker->qpair, 0);
210 : : }
211 : :
212 : 3072 : out:
213 : 3072 : worker->status = rc;
214 : 3072 : return rc;
215 : : }
216 : :
217 : : static void
218 : 0 : usage(const char *program_name)
219 : : {
220 [ # # ]: 0 : printf("%s [options]", program_name);
221 [ # # ]: 0 : printf("\t\n");
222 [ # # ]: 0 : printf("options:\n");
223 [ # # ]: 0 : printf("\t[-r remote NVMe over Fabrics target address]\n");
224 : : #ifdef DEBUG
225 [ # # ]: 0 : printf("\t[-L enable debug logging]\n");
226 : : #else
227 : : printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n");
228 : : printf("\t[-c core mask]\n");
229 : : #endif
230 [ # # ]: 0 : printf("\t[-s memory size in MB for DPDK (default: 0MB)]\n");
231 [ # # ]: 0 : printf("\t[--no-huge SPDK is run without hugepages]\n");
232 : 0 : }
233 : :
234 : : #define FUSED_GETOPT_STRING "r:L:q:c:s:"
235 : : static const struct option g_fused_cmdline_opts[] = {
236 : : #define FUSED_NO_HUGE 257
237 : : {"no-huge", no_argument, NULL, FUSED_NO_HUGE},
238 : : {0, 0, 0, 0}
239 : : };
240 : :
241 : : static int
242 : 3 : parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
243 : : {
244 : 0 : int op, rc, opt_index;
245 : : long int value;
246 : :
247 [ - + - + ]: 6 : while ((op = getopt_long(argc, argv, FUSED_GETOPT_STRING, g_fused_cmdline_opts,
248 [ + + ]: 6 : &opt_index)) != -1) {
249 [ + - - - : 3 : switch (op) {
- - ]
250 : 3 : case 'r':
251 [ - + ]: 3 : if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
252 [ # # # # ]: 0 : fprintf(stderr, "Error parsing transport address\n");
253 : 0 : return 1;
254 : : }
255 : 3 : break;
256 : 0 : case 'L':
257 : 0 : rc = spdk_log_set_flag(optarg);
258 [ # # ]: 0 : if (rc < 0) {
259 [ # # # # ]: 0 : fprintf(stderr, "unknown flag\n");
260 : 0 : usage(argv[0]);
261 : 0 : exit(EXIT_FAILURE);
262 : : }
263 : : #ifdef DEBUG
264 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
265 : : #endif
266 : 0 : break;
267 : 0 : case 'c':
268 : 0 : env_opts->core_mask = optarg;
269 : 0 : break;
270 : 0 : case 's':
271 : 0 : value = spdk_strtol(optarg, 10);
272 [ # # ]: 0 : if (value < 0) {
273 [ # # # # ]: 0 : fprintf(stderr, "converting a string to integer failed\n");
274 : 0 : return -EINVAL;
275 : : }
276 : 0 : env_opts->mem_size = value;
277 : 0 : break;
278 : 0 : case FUSED_NO_HUGE:
279 : 0 : env_opts->no_huge = true;
280 : 0 : break;
281 : 0 : default:
282 : 0 : usage(argv[0]);
283 : 0 : return 1;
284 : : }
285 : : }
286 : :
287 : 3 : return 0;
288 : : }
289 : :
290 : : int
291 : 3 : main(int argc, char **argv)
292 : : {
293 : : int rc, i;
294 : 0 : struct spdk_env_opts opts;
295 : 0 : struct spdk_nvme_ctrlr_opts ctrlr_opts;
296 : : int nsid;
297 : : const struct spdk_nvme_ctrlr_opts *ctrlr_opts_actual;
298 : : uint32_t ctrlr_io_queues;
299 : : uint32_t main_core;
300 : 3 : struct worker_thread *main_worker = NULL, *worker = NULL;
301 : :
302 : 3 : spdk_env_opts_init(&opts);
303 : 3 : spdk_log_set_print_level(SPDK_LOG_NOTICE);
304 : 3 : rc = parse_args(argc, argv, &opts);
305 [ - + ]: 3 : if (rc != 0) {
306 : 0 : return rc;
307 : : }
308 : :
309 : 3 : opts.name = "fused_ordering";
310 [ - + ]: 3 : if (spdk_env_init(&opts) < 0) {
311 [ # # ]: 0 : fprintf(stderr, "Unable to initialize SPDK env\n");
312 : 0 : return 1;
313 : : }
314 : :
315 [ - + ]: 3 : if (register_workers() != 0) {
316 : 0 : rc = -1;
317 : 0 : goto exit;
318 : : }
319 : :
320 : 3 : spdk_nvme_ctrlr_get_default_ctrlr_opts(&ctrlr_opts, sizeof(ctrlr_opts));
321 : 3 : ctrlr_opts.keep_alive_timeout_ms = 60 * 1000;
322 : 3 : g_ctrlr = spdk_nvme_connect(&g_trid, &ctrlr_opts, sizeof(ctrlr_opts));
323 [ - + ]: 3 : if (g_ctrlr == NULL) {
324 [ # # ]: 0 : fprintf(stderr, "spdk_nvme_connect() failed\n");
325 : 0 : rc = 1;
326 : 0 : goto exit;
327 : : }
328 : :
329 : 3 : printf("Attached to %s\n", g_trid.subnqn);
330 : :
331 : 3 : nsid = spdk_nvme_ctrlr_get_first_active_ns(g_ctrlr);
332 [ - + ]: 3 : if (nsid == 0) {
333 : 0 : perror("No active namespaces");
334 : 0 : exit(1);
335 : : }
336 : 3 : g_ns = spdk_nvme_ctrlr_get_ns(g_ctrlr, nsid);
337 : :
338 : 3 : printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(g_ns),
339 : 3 : spdk_nvme_ns_get_size(g_ns) / 1000000000);
340 : :
341 : 3 : ctrlr_opts_actual = spdk_nvme_ctrlr_get_opts(g_ctrlr);
342 : 3 : ctrlr_io_queues = ctrlr_opts_actual->num_io_queues;
343 : :
344 : : /* One qpair per core */
345 [ - + ]: 3 : if (g_num_workers > ctrlr_io_queues) {
346 : 0 : printf("ERROR: Number of IO queues requested %d more then ctrlr caps %d.\n", g_num_workers,
347 : : ctrlr_io_queues);
348 : 0 : rc = -1;
349 : 0 : goto exit;
350 : : }
351 : :
352 : 3 : rc = init_workers();
353 [ - + ]: 3 : if (rc) {
354 : 0 : printf("ERROR: Workers initialization failed.\n");
355 : 0 : goto exit;
356 : : }
357 : :
358 [ + + ]: 3075 : for (i = 0; i < 1024; i++) {
359 : 3072 : printf("fused_ordering(%d)\n", i);
360 : 3072 : main_core = spdk_env_get_current_core();
361 [ + + ]: 6144 : TAILQ_FOREACH(worker, &g_workers, link) {
362 : 3072 : worker->poll_count = i;
363 [ - + ]: 3072 : if (worker->lcore != main_core) {
364 : 0 : spdk_env_thread_launch_pinned(worker->lcore, fused_ordering, worker);
365 : : } else {
366 : 3072 : main_worker = worker;
367 : : }
368 : : }
369 : :
370 [ + - ]: 3072 : if (main_worker != NULL) {
371 : 3072 : fused_ordering(main_worker);
372 : : }
373 : :
374 : 3072 : spdk_env_thread_wait_all();
375 : :
376 [ + + ]: 6144 : TAILQ_FOREACH(worker, &g_workers, link) {
377 [ - + ]: 3072 : if (spdk_unlikely(worker->status != 0)) {
378 : 0 : SPDK_ERRLOG("Iteration of fused ordering(%d) failed.\n", i - 1);
379 : 0 : rc = -1;
380 : 0 : goto exit;
381 : : }
382 : : }
383 : : }
384 : :
385 : 3 : exit:
386 : 3 : fini_workers();
387 : 3 : unregister_workers();
388 : 3 : spdk_nvme_detach(g_ctrlr);
389 : 3 : spdk_env_fini();
390 : 3 : return rc;
391 : : }
|