Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2015 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/pci_ids.h"
12 : :
13 : : struct ctrlr_entry {
14 : : struct spdk_nvme_ctrlr *ctrlr;
15 : : TAILQ_ENTRY(ctrlr_entry) link;
16 : : char name[1024];
17 : : };
18 : :
19 : : struct ns_entry {
20 : : struct spdk_nvme_ns *ns;
21 : : struct spdk_nvme_ctrlr *ctrlr;
22 : : TAILQ_ENTRY(ns_entry) link;
23 : : uint32_t io_size_blocks;
24 : : uint64_t size_in_ios;
25 : : char name[1024];
26 : : };
27 : :
28 : : struct ns_worker_ctx {
29 : : struct ns_entry *entry;
30 : : struct spdk_nvme_qpair *qpair;
31 : : uint64_t io_completed;
32 : : uint64_t io_completed_error;
33 : : uint64_t io_submitted;
34 : : uint64_t current_queue_depth;
35 : : uint64_t offset_in_ios;
36 : : bool is_draining;
37 : :
38 : : TAILQ_ENTRY(ns_worker_ctx) link;
39 : : };
40 : :
41 : : struct reset_task {
42 : : struct ns_worker_ctx *ns_ctx;
43 : : void *buf;
44 : : };
45 : :
46 : : struct worker_thread {
47 : : TAILQ_HEAD(, ns_worker_ctx) ns_ctx;
48 : : unsigned lcore;
49 : : };
50 : :
51 : : static struct spdk_mempool *task_pool;
52 : :
53 : : static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers);
54 : : static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces);
55 : : static int g_num_namespaces = 0;
56 : : static struct worker_thread *g_worker = NULL;
57 : : static bool g_qemu_ssd_found = false;
58 : :
59 : : static uint64_t g_tsc_rate;
60 : :
61 : : static int g_io_size_bytes;
62 : : static int g_rw_percentage;
63 : : static int g_is_random;
64 : : static int g_queue_depth;
65 : : static int g_time_in_sec;
66 : :
67 : : #define TASK_POOL_NUM 8192
68 : :
69 : : static void
70 : 0 : register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
71 : : {
72 : : struct ns_entry *entry;
73 : : const struct spdk_nvme_ctrlr_data *cdata;
74 : :
75 [ # # ]: 0 : if (!spdk_nvme_ns_is_active(ns)) {
76 : 0 : printf("Skipping inactive NS %u\n", spdk_nvme_ns_get_id(ns));
77 : 0 : return;
78 : : }
79 : :
80 : 0 : entry = malloc(sizeof(struct ns_entry));
81 [ # # ]: 0 : if (entry == NULL) {
82 : 0 : perror("ns_entry malloc");
83 : 0 : exit(1);
84 : : }
85 : :
86 : 0 : cdata = spdk_nvme_ctrlr_get_data(ctrlr);
87 : :
88 : 0 : entry->ns = ns;
89 : 0 : entry->ctrlr = ctrlr;
90 : 0 : entry->size_in_ios = spdk_nvme_ns_get_size(ns) /
91 : : g_io_size_bytes;
92 : 0 : entry->io_size_blocks = g_io_size_bytes / spdk_nvme_ns_get_sector_size(ns);
93 : :
94 : 0 : snprintf(entry->name, 44, "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
95 : :
96 : 0 : g_num_namespaces++;
97 : 0 : TAILQ_INSERT_TAIL(&g_namespaces, entry, link);
98 : : }
99 : :
100 : : static void
101 : 0 : register_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
102 : : {
103 : : int nsid;
104 : : struct spdk_nvme_ns *ns;
105 : 0 : struct ctrlr_entry *entry = malloc(sizeof(struct ctrlr_entry));
106 : :
107 [ # # ]: 0 : if (entry == NULL) {
108 : 0 : perror("ctrlr_entry malloc");
109 : 0 : exit(1);
110 : : }
111 : :
112 : 0 : entry->ctrlr = ctrlr;
113 : 0 : TAILQ_INSERT_TAIL(&g_controllers, entry, link);
114 : :
115 [ # # ]: 0 : for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0;
116 : 0 : nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) {
117 : 0 : ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
118 [ # # ]: 0 : if (ns == NULL) {
119 : 0 : continue;
120 : : }
121 : 0 : register_ns(ctrlr, ns);
122 : : }
123 : 0 : }
124 : :
125 : : static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion);
126 : :
127 : : static __thread unsigned int seed = 0;
128 : :
129 : : static void
130 : 0 : submit_single_io(struct ns_worker_ctx *ns_ctx)
131 : : {
132 : 0 : struct reset_task *task = NULL;
133 : : uint64_t offset_in_ios;
134 : : int rc;
135 : 0 : struct ns_entry *entry = ns_ctx->entry;
136 : :
137 : 0 : task = spdk_mempool_get(task_pool);
138 [ # # ]: 0 : if (!task) {
139 : 0 : fprintf(stderr, "Failed to get task from task_pool\n");
140 : 0 : exit(1);
141 : : }
142 : :
143 : 0 : task->buf = spdk_zmalloc(g_io_size_bytes, 0x200, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
144 [ # # ]: 0 : if (!task->buf) {
145 : 0 : spdk_free(task->buf);
146 : 0 : fprintf(stderr, "task->buf spdk_zmalloc failed\n");
147 : 0 : exit(1);
148 : : }
149 : :
150 : 0 : task->ns_ctx = ns_ctx;
151 : :
152 [ # # ]: 0 : if (g_is_random) {
153 : 0 : offset_in_ios = rand_r(&seed) % entry->size_in_ios;
154 : : } else {
155 : 0 : offset_in_ios = ns_ctx->offset_in_ios++;
156 [ # # ]: 0 : if (ns_ctx->offset_in_ios == entry->size_in_ios) {
157 : 0 : ns_ctx->offset_in_ios = 0;
158 : : }
159 : : }
160 : :
161 [ # # ]: 0 : if ((g_rw_percentage == 100) ||
162 [ # # # # ]: 0 : (g_rw_percentage != 0 && ((rand_r(&seed) % 100) < g_rw_percentage))) {
163 : 0 : rc = spdk_nvme_ns_cmd_read(entry->ns, ns_ctx->qpair, task->buf,
164 : 0 : offset_in_ios * entry->io_size_blocks,
165 : : entry->io_size_blocks, io_complete, task, 0);
166 : : } else {
167 : 0 : rc = spdk_nvme_ns_cmd_write(entry->ns, ns_ctx->qpair, task->buf,
168 : 0 : offset_in_ios * entry->io_size_blocks,
169 : : entry->io_size_blocks, io_complete, task, 0);
170 : : }
171 : :
172 [ # # ]: 0 : if (rc != 0) {
173 : 0 : fprintf(stderr, "starting I/O failed\n");
174 : : } else {
175 : 0 : ns_ctx->current_queue_depth++;
176 : 0 : ns_ctx->io_submitted++;
177 : : }
178 : 0 : }
179 : :
180 : : static void
181 : 0 : task_complete(struct reset_task *task, const struct spdk_nvme_cpl *completion)
182 : : {
183 : : struct ns_worker_ctx *ns_ctx;
184 : :
185 : 0 : ns_ctx = task->ns_ctx;
186 : 0 : ns_ctx->current_queue_depth--;
187 : :
188 [ # # # # ]: 0 : if (spdk_nvme_cpl_is_error(completion)) {
189 : 0 : ns_ctx->io_completed_error++;
190 : : } else {
191 : 0 : ns_ctx->io_completed++;
192 : : }
193 : :
194 : 0 : spdk_free(task->buf);
195 : 0 : spdk_mempool_put(task_pool, task);
196 : :
197 : : /*
198 : : * is_draining indicates when time has expired for the test run
199 : : * and we are just waiting for the previously submitted I/O
200 : : * to complete. In this case, do not submit a new I/O to replace
201 : : * the one just completed.
202 : : */
203 [ # # ]: 0 : if (!ns_ctx->is_draining) {
204 : 0 : submit_single_io(ns_ctx);
205 : : }
206 : 0 : }
207 : :
208 : : static void
209 : 0 : io_complete(void *ctx, const struct spdk_nvme_cpl *completion)
210 : : {
211 : 0 : task_complete((struct reset_task *)ctx, completion);
212 : 0 : }
213 : :
214 : : static void
215 : 0 : check_io(struct ns_worker_ctx *ns_ctx)
216 : : {
217 : 0 : spdk_nvme_qpair_process_completions(ns_ctx->qpair, 0);
218 : 0 : }
219 : :
220 : : static void
221 : 0 : submit_io(struct ns_worker_ctx *ns_ctx, int queue_depth)
222 : : {
223 [ # # ]: 0 : while (queue_depth-- > 0) {
224 : 0 : submit_single_io(ns_ctx);
225 : : }
226 : 0 : }
227 : :
228 : : static void
229 : 0 : drain_io(struct ns_worker_ctx *ns_ctx)
230 : : {
231 : 0 : ns_ctx->is_draining = true;
232 [ # # ]: 0 : while (ns_ctx->current_queue_depth > 0) {
233 : 0 : check_io(ns_ctx);
234 : : }
235 : 0 : }
236 : :
237 : : static int
238 : 0 : work_fn(void *arg)
239 : : {
240 : 0 : uint64_t tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
241 : 0 : struct worker_thread *worker = (struct worker_thread *)arg;
242 : 0 : struct ns_worker_ctx *ns_ctx = NULL;
243 : 0 : bool did_reset = false;
244 : :
245 : 0 : printf("Starting thread on core %u\n", worker->lcore);
246 : :
247 : : /* Submit initial I/O for each namespace. */
248 [ # # ]: 0 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
249 : 0 : ns_ctx->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_ctx->entry->ctrlr, NULL, 0);
250 [ # # ]: 0 : if (ns_ctx->qpair == NULL) {
251 : 0 : fprintf(stderr, "spdk_nvme_ctrlr_alloc_io_qpair() failed on core %u\n", worker->lcore);
252 : 0 : return -1;
253 : : }
254 : 0 : submit_io(ns_ctx, g_queue_depth);
255 : : }
256 : :
257 : : while (1) {
258 [ # # # # ]: 0 : if (!did_reset && ((tsc_end - spdk_get_ticks()) / g_tsc_rate) > (uint64_t)g_time_in_sec / 2) {
259 [ # # ]: 0 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
260 [ # # ]: 0 : if (spdk_nvme_ctrlr_reset(ns_ctx->entry->ctrlr) < 0) {
261 : 0 : fprintf(stderr, "nvme reset failed.\n");
262 : 0 : return -1;
263 : : }
264 : : }
265 : 0 : did_reset = true;
266 : : }
267 : :
268 : : /*
269 : : * Check for completed I/O for each controller. A new
270 : : * I/O will be submitted in the io_complete callback
271 : : * to replace each I/O that is completed.
272 : : */
273 [ # # ]: 0 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
274 : 0 : check_io(ns_ctx);
275 : : }
276 : :
277 [ # # ]: 0 : if (spdk_get_ticks() > tsc_end) {
278 : 0 : break;
279 : : }
280 : : }
281 : :
282 [ # # ]: 0 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
283 : 0 : drain_io(ns_ctx);
284 : 0 : spdk_nvme_ctrlr_free_io_qpair(ns_ctx->qpair);
285 : : }
286 : :
287 : 0 : return 0;
288 : : }
289 : :
290 : : static void
291 : 0 : usage(char *program_name)
292 : : {
293 : 0 : printf("%s options", program_name);
294 : 0 : printf("\n");
295 : 0 : printf("\t[-q io depth]\n");
296 : 0 : printf("\t[-o io size in bytes]\n");
297 : 0 : printf("\t[-w io pattern type, must be one of\n");
298 : 0 : printf("\t\t(read, write, randread, randwrite, rw, randrw)]\n");
299 : 0 : printf("\t[-M rwmixread (100 for reads, 0 for writes)]\n");
300 : 0 : printf("\t[-t time in seconds(should be larger than 15 seconds)]\n");
301 : 0 : printf("\t\t(default:0 - unlimited)\n");
302 : 0 : }
303 : :
304 : : static int
305 : 0 : print_stats(void)
306 : : {
307 : : uint64_t io_completed, io_submitted, io_completed_error;
308 : : uint64_t total_completed_io, total_submitted_io, total_completed_err_io;
309 : : struct worker_thread *worker;
310 : : struct ns_worker_ctx *ns_ctx;
311 : :
312 : 0 : total_completed_io = 0;
313 : 0 : total_submitted_io = 0;
314 : 0 : total_completed_err_io = 0;
315 : :
316 : 0 : worker = g_worker;
317 [ # # ]: 0 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
318 : 0 : io_completed = ns_ctx->io_completed;
319 : 0 : io_submitted = ns_ctx->io_submitted;
320 : 0 : io_completed_error = ns_ctx->io_completed_error;
321 : 0 : total_completed_io += io_completed;
322 : 0 : total_submitted_io += io_submitted;
323 : 0 : total_completed_err_io += io_completed_error;
324 : : }
325 : :
326 : 0 : printf("========================================================\n");
327 : 0 : printf("%16" PRIu64 " IO completed successfully\n", total_completed_io);
328 : 0 : printf("%16" PRIu64 " IO completed with error\n", total_completed_err_io);
329 : 0 : printf("--------------------------------------------------------\n");
330 : 0 : printf("%16" PRIu64 " IO completed total\n", total_completed_io + total_completed_err_io);
331 : 0 : printf("%16" PRIu64 " IO submitted\n", total_submitted_io);
332 : :
333 [ # # ]: 0 : if (total_submitted_io != (total_completed_io + total_completed_err_io)) {
334 : 0 : fprintf(stderr, "Some IO are missing......\n");
335 : 0 : return -1;
336 : : }
337 : :
338 : 0 : return 0;
339 : : }
340 : :
341 : : static int
342 : 2 : parse_args(int argc, char **argv)
343 : : {
344 : : const char *workload_type;
345 : : int op;
346 : 2 : bool mix_specified = false;
347 : : long int val;
348 : :
349 : : /* default value */
350 : 2 : g_queue_depth = 0;
351 : 2 : g_io_size_bytes = 0;
352 : 2 : workload_type = NULL;
353 : 2 : g_time_in_sec = 0;
354 : 2 : g_rw_percentage = -1;
355 : :
356 [ + + ]: 10 : while ((op = getopt(argc, argv, "o:q:t:w:M:")) != -1) {
357 [ + + ]: 8 : if (op == 'w') {
358 : 2 : workload_type = optarg;
359 [ - + ]: 6 : } else if (op == '?') {
360 : 0 : usage(argv[0]);
361 : 0 : return -EINVAL;
362 : : } else {
363 : 6 : val = spdk_strtol(optarg, 10);
364 [ - + ]: 6 : if (val < 0) {
365 : 0 : fprintf(stderr, "Converting a string to integer failed\n");
366 : 0 : return val;
367 : : }
368 [ + + + - : 6 : switch (op) {
- ]
369 : 2 : case 'q':
370 : 2 : g_queue_depth = val;
371 : 2 : break;
372 : 2 : case 'o':
373 : 2 : g_io_size_bytes = val;
374 : 2 : break;
375 : 2 : case 't':
376 : 2 : g_time_in_sec = val;
377 : 2 : break;
378 : 0 : case 'M':
379 : 0 : g_rw_percentage = val;
380 : 0 : mix_specified = true;
381 : 0 : break;
382 : 0 : default:
383 : 0 : usage(argv[0]);
384 : 0 : return -EINVAL;
385 : : }
386 : : }
387 : : }
388 : :
389 [ - + ]: 2 : if (!g_queue_depth) {
390 : 0 : usage(argv[0]);
391 : 0 : return 1;
392 : : }
393 [ - + ]: 2 : if (!g_io_size_bytes) {
394 : 0 : usage(argv[0]);
395 : 0 : return 1;
396 : : }
397 [ - + ]: 2 : if (!workload_type) {
398 : 0 : usage(argv[0]);
399 : 0 : return 1;
400 : : }
401 [ - + ]: 2 : if (!g_time_in_sec) {
402 : 0 : usage(argv[0]);
403 : 0 : return 1;
404 : : }
405 : :
406 [ + - ]: 2 : if (strcmp(workload_type, "read") &&
407 [ - + ]: 2 : strcmp(workload_type, "write") &&
408 [ # # ]: 0 : strcmp(workload_type, "randread") &&
409 [ # # ]: 0 : strcmp(workload_type, "randwrite") &&
410 [ # # ]: 0 : strcmp(workload_type, "rw") &&
411 [ # # ]: 0 : strcmp(workload_type, "randrw")) {
412 : 0 : fprintf(stderr,
413 : : "io pattern type must be one of\n"
414 : : "(read, write, randread, randwrite, rw, randrw)\n");
415 : 0 : return 1;
416 : : }
417 : :
418 [ + - ]: 2 : if (!strcmp(workload_type, "read") ||
419 [ - + ]: 2 : !strcmp(workload_type, "randread")) {
420 : 0 : g_rw_percentage = 100;
421 : : }
422 : :
423 [ - + ]: 2 : if (!strcmp(workload_type, "write") ||
424 [ # # ]: 0 : !strcmp(workload_type, "randwrite")) {
425 : 2 : g_rw_percentage = 0;
426 : : }
427 : :
428 [ + - ]: 2 : if (!strcmp(workload_type, "read") ||
429 [ + - ]: 2 : !strcmp(workload_type, "randread") ||
430 [ - + ]: 2 : !strcmp(workload_type, "write") ||
431 [ # # ]: 0 : !strcmp(workload_type, "randwrite")) {
432 [ - + ]: 2 : if (mix_specified) {
433 : 0 : fprintf(stderr, "Ignoring -M option... Please use -M option"
434 : : " only when using rw or randrw.\n");
435 : : }
436 : : }
437 : :
438 [ + - ]: 2 : if (!strcmp(workload_type, "rw") ||
439 [ - + ]: 2 : !strcmp(workload_type, "randrw")) {
440 [ # # # # ]: 0 : if (g_rw_percentage < 0 || g_rw_percentage > 100) {
441 : 0 : fprintf(stderr,
442 : : "-M must be specified to value from 0 to 100 "
443 : : "for rw or randrw.\n");
444 : 0 : return 1;
445 : : }
446 : : }
447 : :
448 [ + - ]: 2 : if (!strcmp(workload_type, "read") ||
449 [ - + ]: 2 : !strcmp(workload_type, "write") ||
450 [ # # ]: 0 : !strcmp(workload_type, "rw")) {
451 : 2 : g_is_random = 0;
452 : : } else {
453 : 0 : g_is_random = 1;
454 : : }
455 : :
456 : 2 : return 0;
457 : : }
458 : :
459 : : static int
460 : 0 : register_worker(void)
461 : : {
462 : : struct worker_thread *worker;
463 : :
464 : 0 : worker = malloc(sizeof(struct worker_thread));
465 [ # # ]: 0 : if (worker == NULL) {
466 : 0 : perror("worker_thread malloc");
467 : 0 : return -1;
468 : : }
469 : :
470 : 0 : memset(worker, 0, sizeof(struct worker_thread));
471 : 0 : TAILQ_INIT(&worker->ns_ctx);
472 : 0 : worker->lcore = spdk_env_get_current_core();
473 : :
474 : 0 : g_worker = worker;
475 : :
476 : 0 : return 0;
477 : : }
478 : :
479 : :
480 : : static bool
481 : 0 : probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
482 : : struct spdk_nvme_ctrlr_opts *opts)
483 : : {
484 : 0 : opts->disable_error_logging = true;
485 : 0 : return true;
486 : : }
487 : :
488 : : static void
489 : 3 : attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
490 : : struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
491 : : {
492 [ + - ]: 3 : if (trid->trtype == SPDK_NVME_TRANSPORT_PCIE) {
493 : 3 : struct spdk_pci_device *dev = spdk_nvme_ctrlr_get_pci_device(ctrlr);
494 : :
495 : : /* QEMU emulated SSDs can't handle this test, so we will skip them. QEMU NVMe SSDs
496 : : * report themselves as VID == (Intel|Red Hat). So we need to check this specific
497 : : * (0x5845|0x0010) device ID to know whether it's QEMU or not.
498 : : */
499 [ - + - - ]: 3 : if ((spdk_pci_device_get_vendor_id(dev) == SPDK_PCI_VID_INTEL &&
500 [ + - ]: 3 : spdk_pci_device_get_device_id(dev) == 0x5845) ||
501 [ + - ]: 6 : (spdk_pci_device_get_vendor_id(dev) == SPDK_PCI_VID_REDHAT &&
502 : 3 : spdk_pci_device_get_device_id(dev) == 0x0010)) {
503 : 3 : g_qemu_ssd_found = true;
504 : 3 : printf("Skipping QEMU NVMe SSD at %s\n", trid->traddr);
505 : 3 : return;
506 : : }
507 : : }
508 : :
509 : 0 : register_ctrlr(ctrlr);
510 : : }
511 : :
512 : : static int
513 : 2 : register_controllers(void)
514 : : {
515 : 2 : printf("Initializing NVMe Controllers\n");
516 : :
517 [ - + ]: 2 : if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL) != 0) {
518 : 0 : fprintf(stderr, "spdk_nvme_probe() failed\n");
519 : 0 : return 1;
520 : : }
521 : :
522 : 2 : return 0;
523 : : }
524 : :
525 : : static void
526 : 0 : unregister_controllers(void)
527 : : {
528 : : struct ctrlr_entry *entry, *tmp;
529 : 0 : struct spdk_nvme_detach_ctx *detach_ctx = NULL;
530 : :
531 [ # # ]: 0 : TAILQ_FOREACH_SAFE(entry, &g_controllers, link, tmp) {
532 [ # # ]: 0 : TAILQ_REMOVE(&g_controllers, entry, link);
533 : 0 : spdk_nvme_detach_async(entry->ctrlr, &detach_ctx);
534 : 0 : free(entry);
535 : : }
536 : :
537 [ # # ]: 0 : if (detach_ctx) {
538 : 0 : spdk_nvme_detach_poll(detach_ctx);
539 : : }
540 : 0 : }
541 : :
542 : : static int
543 : 0 : associate_workers_with_ns(void)
544 : : {
545 : 0 : struct ns_entry *entry = TAILQ_FIRST(&g_namespaces);
546 : 0 : struct worker_thread *worker = g_worker;
547 : : struct ns_worker_ctx *ns_ctx;
548 : : int i, count;
549 : :
550 : 0 : count = g_num_namespaces;
551 : :
552 [ # # ]: 0 : for (i = 0; i < count; i++) {
553 [ # # ]: 0 : if (entry == NULL) {
554 : 0 : break;
555 : : }
556 : 0 : ns_ctx = malloc(sizeof(struct ns_worker_ctx));
557 [ # # ]: 0 : if (!ns_ctx) {
558 : 0 : return -1;
559 : : }
560 : 0 : memset(ns_ctx, 0, sizeof(*ns_ctx));
561 : :
562 : 0 : printf("Associating %s with lcore %d\n", entry->name, worker->lcore);
563 : 0 : ns_ctx->entry = entry;
564 : 0 : TAILQ_INSERT_TAIL(&worker->ns_ctx, ns_ctx, link);
565 : :
566 : 0 : entry = TAILQ_NEXT(entry, link);;
567 [ # # ]: 0 : if (entry == NULL) {
568 : 0 : entry = TAILQ_FIRST(&g_namespaces);
569 : : }
570 : : }
571 : :
572 : 0 : return 0;
573 : : }
574 : :
575 : : static void
576 : 0 : unregister_worker(void)
577 : : {
578 : : struct ns_worker_ctx *ns_ctx, *tmp;
579 : :
580 [ # # ]: 0 : assert(g_worker != NULL);
581 : :
582 [ # # ]: 0 : TAILQ_FOREACH_SAFE(ns_ctx, &g_worker->ns_ctx, link, tmp) {
583 [ # # ]: 0 : TAILQ_REMOVE(&g_worker->ns_ctx, ns_ctx, link);
584 : 0 : free(ns_ctx);
585 : : }
586 : :
587 : 0 : free(g_worker);
588 : 0 : g_worker = NULL;
589 : 0 : }
590 : :
591 : : static int
592 : 0 : run_nvme_reset_cycle(void)
593 : : {
594 : 0 : struct worker_thread *worker = g_worker;
595 : : struct ns_worker_ctx *ns_ctx;
596 : :
597 [ # # ]: 0 : if (work_fn(worker) != 0) {
598 : 0 : return -1;
599 : : }
600 : :
601 [ # # ]: 0 : if (print_stats() != 0) {
602 : 0 : return -1;
603 : : }
604 : :
605 [ # # ]: 0 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
606 : 0 : ns_ctx->io_completed = 0;
607 : 0 : ns_ctx->io_completed_error = 0;
608 : 0 : ns_ctx->io_submitted = 0;
609 : 0 : ns_ctx->is_draining = false;
610 : : }
611 : :
612 : 0 : return 0;
613 : : }
614 : :
615 : : static void
616 : 0 : free_tasks(void)
617 : : {
618 [ # # ]: 0 : if (spdk_mempool_count(task_pool) != TASK_POOL_NUM) {
619 : 0 : fprintf(stderr, "task_pool count is %zu but should be %d\n",
620 : : spdk_mempool_count(task_pool), TASK_POOL_NUM);
621 : : }
622 : 0 : spdk_mempool_free(task_pool);
623 : 0 : }
624 : :
625 : : int
626 : 2 : main(int argc, char **argv)
627 : : {
628 : : int rc;
629 : : int i;
630 : 0 : struct spdk_env_opts opts;
631 : :
632 : :
633 : 2 : rc = parse_args(argc, argv);
634 [ - + ]: 2 : if (rc != 0) {
635 : 0 : return rc;
636 : : }
637 : :
638 : 2 : spdk_env_opts_init(&opts);
639 : 2 : opts.name = "reset";
640 : 2 : opts.core_mask = "0x1";
641 : 2 : opts.shm_id = 0;
642 [ - + ]: 2 : if (spdk_env_init(&opts) < 0) {
643 : 0 : fprintf(stderr, "Unable to initialize SPDK env\n");
644 : 0 : return 1;
645 : : }
646 : :
647 [ - + ]: 2 : if (register_controllers() != 0) {
648 : 0 : return 1;
649 : : }
650 : :
651 [ + - ]: 2 : if (TAILQ_EMPTY(&g_controllers)) {
652 : 2 : printf("No NVMe controller found, %s exiting\n", argv[0]);
653 : 2 : return g_qemu_ssd_found ? 0 : 1;
654 : : }
655 : :
656 : 0 : task_pool = spdk_mempool_create("task_pool", TASK_POOL_NUM,
657 : : sizeof(struct reset_task),
658 : : 64, SPDK_ENV_SOCKET_ID_ANY);
659 [ # # ]: 0 : if (!task_pool) {
660 : 0 : fprintf(stderr, "Cannot create task pool\n");
661 : 0 : return 1;
662 : : }
663 : :
664 : 0 : g_tsc_rate = spdk_get_ticks_hz();
665 : :
666 [ # # ]: 0 : if (register_worker() != 0) {
667 : 0 : return 1;
668 : : }
669 : :
670 [ # # ]: 0 : if (associate_workers_with_ns() != 0) {
671 : 0 : rc = 1;
672 : 0 : goto cleanup;
673 : : }
674 : :
675 : 0 : printf("Initialization complete. Launching workers.\n");
676 : :
677 [ # # ]: 0 : for (i = 2; i >= 0; i--) {
678 : 0 : rc = run_nvme_reset_cycle();
679 [ # # ]: 0 : if (rc != 0) {
680 : 0 : goto cleanup;
681 : : }
682 : : }
683 : :
684 : 0 : cleanup:
685 : 0 : unregister_controllers();
686 : 0 : unregister_worker();
687 : 0 : free_tasks();
688 : :
689 [ # # ]: 0 : if (rc != 0) {
690 : 0 : fprintf(stderr, "%s: errors occurred\n", argv[0]);
691 : : }
692 : :
693 : 0 : return rc;
694 : : }
|