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/ioat.h"
9 : : #include "spdk/env.h"
10 : : #include "spdk/queue.h"
11 : : #include "spdk/string.h"
12 : :
13 : : struct user_config {
14 : : int xfer_size_bytes;
15 : : int queue_depth;
16 : : int time_in_sec;
17 : : bool verify;
18 : : char *core_mask;
19 : : int ioat_chan_num;
20 : : };
21 : :
22 : : struct ioat_device {
23 : : struct spdk_ioat_chan *ioat;
24 : : TAILQ_ENTRY(ioat_device) tailq;
25 : : };
26 : :
27 : : static TAILQ_HEAD(, ioat_device) g_devices = TAILQ_HEAD_INITIALIZER(g_devices);
28 : : static struct ioat_device *g_next_device;
29 : :
30 : : static struct user_config g_user_config;
31 : :
32 : : struct ioat_chan_entry {
33 : : struct spdk_ioat_chan *chan;
34 : : int ioat_chan_id;
35 : : uint64_t xfer_completed;
36 : : uint64_t xfer_failed;
37 : : uint64_t current_queue_depth;
38 : : uint64_t waiting_for_flush;
39 : : uint64_t flush_threshold;
40 : : bool is_draining;
41 : : struct spdk_mempool *data_pool;
42 : : struct spdk_mempool *task_pool;
43 : : struct ioat_chan_entry *next;
44 : : };
45 : :
46 : : struct worker_thread {
47 : : struct ioat_chan_entry *ctx;
48 : : struct worker_thread *next;
49 : : unsigned core;
50 : : };
51 : :
52 : : struct ioat_task {
53 : : struct ioat_chan_entry *ioat_chan_entry;
54 : : void *src;
55 : : void *dst;
56 : : };
57 : :
58 : : static struct worker_thread *g_workers = NULL;
59 : : static int g_num_workers = 0;
60 : : static int g_ioat_chan_num = 0;
61 : :
62 : : static void submit_single_xfer(struct ioat_chan_entry *ioat_chan_entry, struct ioat_task *ioat_task,
63 : : void *dst, void *src);
64 : :
65 : : static void
66 : 1 : construct_user_config(struct user_config *self)
67 : : {
68 : 1 : self->xfer_size_bytes = 4096;
69 : 1 : self->ioat_chan_num = 1;
70 : 1 : self->queue_depth = 256;
71 : 1 : self->time_in_sec = 10;
72 : 1 : self->verify = false;
73 : 1 : self->core_mask = "0x1";
74 : 1 : }
75 : :
76 : : static void
77 : 1 : dump_user_config(struct user_config *self)
78 : : {
79 [ - + ]: 1 : printf("User configuration:\n");
80 [ - + ]: 1 : printf("Number of channels: %u\n", self->ioat_chan_num);
81 [ - + ]: 1 : printf("Transfer size: %u bytes\n", self->xfer_size_bytes);
82 [ - + ]: 1 : printf("Queue depth: %u\n", self->queue_depth);
83 [ - + ]: 1 : printf("Run time: %u seconds\n", self->time_in_sec);
84 [ - + ]: 1 : printf("Core mask: %s\n", self->core_mask);
85 [ - + - + : 1 : printf("Verify: %s\n\n", self->verify ? "Yes" : "No");
- + ]
86 : 1 : }
87 : :
88 : : static void
89 : 1 : ioat_exit(void)
90 : : {
91 : : struct ioat_device *dev;
92 : :
93 [ + + ]: 2 : while (!TAILQ_EMPTY(&g_devices)) {
94 : 1 : dev = TAILQ_FIRST(&g_devices);
95 [ - + ]: 1 : TAILQ_REMOVE(&g_devices, dev, tailq);
96 [ + - ]: 1 : if (dev->ioat) {
97 : 1 : spdk_ioat_detach(dev->ioat);
98 : : }
99 : 1 : spdk_dma_free(dev);
100 : : }
101 : 1 : }
102 : :
103 : : static void
104 : 697088 : ioat_done(void *cb_arg)
105 : : {
106 : 697088 : struct ioat_task *ioat_task = (struct ioat_task *)cb_arg;
107 : 697088 : struct ioat_chan_entry *ioat_chan_entry = ioat_task->ioat_chan_entry;
108 : :
109 [ - + - + : 697088 : if (g_user_config.verify && memcmp(ioat_task->src, ioat_task->dst, g_user_config.xfer_size_bytes)) {
- - - - -
- ]
110 : 0 : ioat_chan_entry->xfer_failed++;
111 : : } else {
112 : 697088 : ioat_chan_entry->xfer_completed++;
113 : : }
114 : :
115 : 697088 : ioat_chan_entry->current_queue_depth--;
116 : :
117 [ - + + + ]: 697088 : if (ioat_chan_entry->is_draining) {
118 : 256 : spdk_mempool_put(ioat_chan_entry->data_pool, ioat_task->src);
119 : 256 : spdk_mempool_put(ioat_chan_entry->data_pool, ioat_task->dst);
120 : 256 : spdk_mempool_put(ioat_chan_entry->task_pool, ioat_task);
121 : : } else {
122 : 696832 : submit_single_xfer(ioat_chan_entry, ioat_task, ioat_task->dst, ioat_task->src);
123 : : }
124 : 697088 : }
125 : :
126 : : static int
127 : 1 : register_workers(void)
128 : : {
129 : : uint32_t i;
130 : : struct worker_thread *worker;
131 : :
132 : 1 : g_workers = NULL;
133 : 1 : g_num_workers = 0;
134 : :
135 [ + + ]: 2 : SPDK_ENV_FOREACH_CORE(i) {
136 : 1 : worker = calloc(1, sizeof(*worker));
137 [ - + ]: 1 : if (worker == NULL) {
138 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate worker\n");
139 : 0 : return 1;
140 : : }
141 : :
142 : 1 : worker->core = i;
143 : 1 : worker->next = g_workers;
144 : 1 : g_workers = worker;
145 : 1 : g_num_workers++;
146 : : }
147 : :
148 : 1 : return 0;
149 : : }
150 : :
151 : : static void
152 : 1 : unregister_workers(void)
153 : : {
154 : 1 : struct worker_thread *worker = g_workers;
155 : : struct ioat_chan_entry *entry, *entry1;
156 : :
157 : : /* Free ioat_chan_entry and worker thread */
158 [ + + ]: 2 : while (worker) {
159 : 1 : struct worker_thread *next_worker = worker->next;
160 : 1 : entry = worker->ctx;
161 [ + + ]: 2 : while (entry) {
162 : 1 : entry1 = entry->next;
163 : 1 : spdk_mempool_free(entry->data_pool);
164 : 1 : spdk_mempool_free(entry->task_pool);
165 : 1 : free(entry);
166 : 1 : entry = entry1;
167 : : }
168 : 1 : free(worker);
169 : 1 : worker = next_worker;
170 : : }
171 : 1 : }
172 : :
173 : : static bool
174 : 16 : probe_cb(void *cb_ctx, struct spdk_pci_device *pci_dev)
175 : : {
176 [ - + ]: 16 : printf(" Found matching device at %04x:%02x:%02x.%x "
177 : : "vendor:0x%04x device:0x%04x\n",
178 : : spdk_pci_device_get_domain(pci_dev),
179 : 16 : spdk_pci_device_get_bus(pci_dev), spdk_pci_device_get_dev(pci_dev),
180 : 16 : spdk_pci_device_get_func(pci_dev),
181 : 16 : spdk_pci_device_get_vendor_id(pci_dev), spdk_pci_device_get_device_id(pci_dev));
182 : :
183 : 16 : return true;
184 : : }
185 : :
186 : : static void
187 : 16 : attach_cb(void *cb_ctx, struct spdk_pci_device *pci_dev, struct spdk_ioat_chan *ioat)
188 : : {
189 : : struct ioat_device *dev;
190 : :
191 [ + + ]: 16 : if (g_ioat_chan_num >= g_user_config.ioat_chan_num) {
192 : 15 : return;
193 : : }
194 : :
195 : 1 : dev = spdk_dma_zmalloc(sizeof(*dev), 0, NULL);
196 [ - + ]: 1 : if (dev == NULL) {
197 [ # # ]: 0 : printf("Failed to allocate device struct\n");
198 : 0 : return;
199 : : }
200 : :
201 : 1 : dev->ioat = ioat;
202 : 1 : g_ioat_chan_num++;
203 : 1 : TAILQ_INSERT_TAIL(&g_devices, dev, tailq);
204 : : }
205 : :
206 : : static int
207 : 1 : ioat_init(void)
208 : : {
209 [ - + ]: 1 : if (spdk_ioat_probe(NULL, probe_cb, attach_cb) != 0) {
210 [ # # # # ]: 0 : fprintf(stderr, "ioat_probe() failed\n");
211 : 0 : return 1;
212 : : }
213 : :
214 : 1 : return 0;
215 : : }
216 : :
217 : : static void
218 : 0 : usage(char *program_name)
219 : : {
220 [ # # ]: 0 : printf("%s options\n", program_name);
221 [ # # ]: 0 : printf("\t[-h help message]\n");
222 [ # # ]: 0 : printf("\t[-c core mask for distributing I/O submission/completion work]\n");
223 [ # # ]: 0 : printf("\t[-q queue depth]\n");
224 [ # # ]: 0 : printf("\t[-n number of channels]\n");
225 [ # # ]: 0 : printf("\t[-o transfer size in bytes]\n");
226 [ # # ]: 0 : printf("\t[-t time in seconds]\n");
227 [ # # ]: 0 : printf("\t[-v verify copy result if this switch is on]\n");
228 : 0 : }
229 : :
230 : : static int
231 : 1 : parse_args(int argc, char **argv)
232 : : {
233 : : int op;
234 : :
235 : 1 : construct_user_config(&g_user_config);
236 [ - + - + : 2 : while ((op = getopt(argc, argv, "c:hn:o:q:t:v")) != -1) {
+ + ]
237 [ - - - + : 1 : switch (op) {
- - - - ]
238 : 0 : case 'o':
239 : 0 : g_user_config.xfer_size_bytes = spdk_strtol(optarg, 10);
240 : 0 : break;
241 : 0 : case 'n':
242 : 0 : g_user_config.ioat_chan_num = spdk_strtol(optarg, 10);
243 : 0 : break;
244 : 0 : case 'q':
245 : 0 : g_user_config.queue_depth = spdk_strtol(optarg, 10);
246 : 0 : break;
247 : 1 : case 't':
248 : 1 : g_user_config.time_in_sec = spdk_strtol(optarg, 10);
249 : 1 : break;
250 : 0 : case 'c':
251 : 0 : g_user_config.core_mask = optarg;
252 : 0 : break;
253 : 0 : case 'v':
254 : 0 : g_user_config.verify = true;
255 : 0 : break;
256 : 0 : case 'h':
257 : 0 : usage(argv[0]);
258 : 0 : exit(0);
259 : 0 : default:
260 : 0 : usage(argv[0]);
261 : 0 : return 1;
262 : : }
263 : : }
264 [ + - + - ]: 1 : if (g_user_config.xfer_size_bytes <= 0 || g_user_config.queue_depth <= 0 ||
265 [ + - + - ]: 1 : g_user_config.time_in_sec <= 0 || !g_user_config.core_mask ||
266 [ - + ]: 1 : g_user_config.ioat_chan_num <= 0) {
267 : 0 : usage(argv[0]);
268 : 0 : return 1;
269 : : }
270 : :
271 : 1 : return 0;
272 : : }
273 : :
274 : : static void
275 : 1 : drain_io(struct ioat_chan_entry *ioat_chan_entry)
276 : : {
277 : 1 : spdk_ioat_flush(ioat_chan_entry->chan);
278 [ + + ]: 3 : while (ioat_chan_entry->current_queue_depth > 0) {
279 : 2 : spdk_ioat_process_events(ioat_chan_entry->chan);
280 : : }
281 : 1 : }
282 : :
283 : : static void
284 : 697088 : submit_single_xfer(struct ioat_chan_entry *ioat_chan_entry, struct ioat_task *ioat_task, void *dst,
285 : : void *src)
286 : : {
287 : 697088 : ioat_task->ioat_chan_entry = ioat_chan_entry;
288 : 697088 : ioat_task->src = src;
289 : 697088 : ioat_task->dst = dst;
290 : :
291 : 697088 : spdk_ioat_build_copy(ioat_chan_entry->chan, ioat_task, ioat_done, dst, src,
292 : 697088 : g_user_config.xfer_size_bytes);
293 : 697088 : ioat_chan_entry->waiting_for_flush++;
294 [ + + ]: 697088 : if (ioat_chan_entry->waiting_for_flush >= ioat_chan_entry->flush_threshold) {
295 : 5446 : spdk_ioat_flush(ioat_chan_entry->chan);
296 : 5446 : ioat_chan_entry->waiting_for_flush = 0;
297 : : }
298 : :
299 : 697088 : ioat_chan_entry->current_queue_depth++;
300 : 697088 : }
301 : :
302 : : static int
303 : 1 : submit_xfers(struct ioat_chan_entry *ioat_chan_entry, uint64_t queue_depth)
304 : : {
305 [ + + ]: 257 : while (queue_depth-- > 0) {
306 : 256 : void *src = NULL, *dst = NULL;
307 : 256 : struct ioat_task *ioat_task = NULL;
308 : :
309 : 256 : src = spdk_mempool_get(ioat_chan_entry->data_pool);
310 : 256 : dst = spdk_mempool_get(ioat_chan_entry->data_pool);
311 : 256 : ioat_task = spdk_mempool_get(ioat_chan_entry->task_pool);
312 [ - + ]: 256 : if (!ioat_task) {
313 [ # # # # ]: 0 : fprintf(stderr, "Unable to get ioat_task\n");
314 : 0 : return 1;
315 : : }
316 : :
317 : 256 : submit_single_xfer(ioat_chan_entry, ioat_task, dst, src);
318 : : }
319 : 1 : return 0;
320 : : }
321 : :
322 : : static int
323 : 1 : work_fn(void *arg)
324 : : {
325 : : uint64_t tsc_end;
326 : 1 : struct worker_thread *worker = (struct worker_thread *)arg;
327 : 1 : struct ioat_chan_entry *t = NULL;
328 : :
329 [ - + ]: 1 : printf("Starting thread on core %u\n", worker->core);
330 : :
331 : 1 : tsc_end = spdk_get_ticks() + g_user_config.time_in_sec * spdk_get_ticks_hz();
332 : :
333 : 1 : t = worker->ctx;
334 [ + + ]: 2 : while (t != NULL) {
335 : : /* begin to submit transfers */
336 : 1 : t->waiting_for_flush = 0;
337 : 1 : t->flush_threshold = g_user_config.queue_depth / 2;
338 [ - + ]: 1 : if (submit_xfers(t, g_user_config.queue_depth) != 0) {
339 : 0 : return 1;
340 : : }
341 : 1 : t = t->next;
342 : : }
343 : :
344 : : while (1) {
345 : 5444 : t = worker->ctx;
346 [ + + ]: 10888 : while (t != NULL) {
347 : 5444 : spdk_ioat_process_events(t->chan);
348 : 5444 : t = t->next;
349 : : }
350 : :
351 [ + + ]: 5444 : if (spdk_get_ticks() > tsc_end) {
352 : 1 : break;
353 : : }
354 : : }
355 : :
356 : 1 : t = worker->ctx;
357 [ + + ]: 2 : while (t != NULL) {
358 : : /* begin to drain io */
359 : 1 : t->is_draining = true;
360 : 1 : drain_io(t);
361 : 1 : t = t->next;
362 : : }
363 : :
364 : 1 : return 0;
365 : : }
366 : :
367 : : static int
368 : 1 : init(void)
369 : : {
370 : 0 : struct spdk_env_opts opts;
371 : :
372 : 1 : spdk_env_opts_init(&opts);
373 : 1 : opts.name = "ioat_perf";
374 : 1 : opts.core_mask = g_user_config.core_mask;
375 [ - + ]: 1 : if (spdk_env_init(&opts) < 0) {
376 : 0 : return 1;
377 : : }
378 : :
379 : 1 : return 0;
380 : : }
381 : :
382 : : static int
383 : 1 : dump_result(void)
384 : : {
385 : 1 : uint64_t total_completed = 0;
386 : 1 : uint64_t total_failed = 0;
387 : : uint64_t total_xfer_per_sec, total_bw_in_MiBps;
388 : 1 : struct worker_thread *worker = g_workers;
389 : :
390 [ - + ]: 1 : printf("Channel_ID Core Transfers Bandwidth Failed\n");
391 [ - + ]: 1 : printf("-----------------------------------------------------------\n");
392 [ + + ]: 2 : while (worker != NULL) {
393 : 1 : struct ioat_chan_entry *t = worker->ctx;
394 [ + + ]: 2 : while (t) {
395 [ - + ]: 1 : uint64_t xfer_per_sec = t->xfer_completed / g_user_config.time_in_sec;
396 [ - + ]: 1 : uint64_t bw_in_MiBps = (t->xfer_completed * g_user_config.xfer_size_bytes) /
397 : 1 : (g_user_config.time_in_sec * 1024 * 1024);
398 : :
399 : 1 : total_completed += t->xfer_completed;
400 : 1 : total_failed += t->xfer_failed;
401 : :
402 [ + - ]: 1 : if (xfer_per_sec) {
403 [ - + ]: 1 : printf("%10d%10d%12" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 "\n",
404 : : t->ioat_chan_id, worker->core, xfer_per_sec,
405 : : bw_in_MiBps, t->xfer_failed);
406 : : }
407 : 1 : t = t->next;
408 : : }
409 : 1 : worker = worker->next;
410 : : }
411 : :
412 [ - + ]: 1 : total_xfer_per_sec = total_completed / g_user_config.time_in_sec;
413 : 0 : total_bw_in_MiBps = (total_completed * g_user_config.xfer_size_bytes) /
414 [ - + ]: 1 : (g_user_config.time_in_sec * 1024 * 1024);
415 : :
416 [ - + ]: 1 : printf("===========================================================\n");
417 [ - + ]: 1 : printf("Total:%26" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 "\n",
418 : : total_xfer_per_sec, total_bw_in_MiBps, total_failed);
419 : :
420 : 1 : return total_failed ? 1 : 0;
421 : : }
422 : :
423 : : static struct spdk_ioat_chan *
424 : 2 : get_next_chan(void)
425 : : {
426 : : struct spdk_ioat_chan *chan;
427 : :
428 [ + + ]: 2 : if (g_next_device == NULL) {
429 : 1 : return NULL;
430 : : }
431 : :
432 : 1 : chan = g_next_device->ioat;
433 : :
434 : 1 : g_next_device = TAILQ_NEXT(g_next_device, tailq);
435 : :
436 : 1 : return chan;
437 : : }
438 : :
439 : : static int
440 : 1 : associate_workers_with_chan(void)
441 : : {
442 : 1 : struct spdk_ioat_chan *chan = get_next_chan();
443 : 1 : struct worker_thread *worker = g_workers;
444 : : struct ioat_chan_entry *t;
445 : 0 : char buf_pool_name[30], task_pool_name[30];
446 : 1 : int i = 0;
447 : :
448 [ + + ]: 2 : while (chan != NULL) {
449 : 1 : t = calloc(1, sizeof(struct ioat_chan_entry));
450 [ - + ]: 1 : if (!t) {
451 : 0 : return 1;
452 : : }
453 : :
454 : 1 : t->ioat_chan_id = i;
455 [ - + ]: 1 : snprintf(buf_pool_name, sizeof(buf_pool_name), "buf_pool_%d", i);
456 [ - + ]: 1 : snprintf(task_pool_name, sizeof(task_pool_name), "task_pool_%d", i);
457 : 1 : t->data_pool = spdk_mempool_create(buf_pool_name,
458 : 1 : g_user_config.queue_depth * 2, /* src + dst */
459 : 1 : g_user_config.xfer_size_bytes,
460 : : SPDK_MEMPOOL_DEFAULT_CACHE_SIZE,
461 : : SPDK_ENV_SOCKET_ID_ANY);
462 : 1 : t->task_pool = spdk_mempool_create(task_pool_name,
463 : 1 : g_user_config.queue_depth,
464 : : sizeof(struct ioat_task),
465 : : SPDK_MEMPOOL_DEFAULT_CACHE_SIZE,
466 : : SPDK_ENV_SOCKET_ID_ANY);
467 [ + - - + ]: 1 : if (!t->data_pool || !t->task_pool) {
468 [ # # # # ]: 0 : fprintf(stderr, "Could not allocate buffer pool.\n");
469 : 0 : spdk_mempool_free(t->data_pool);
470 : 0 : spdk_mempool_free(t->task_pool);
471 : 0 : free(t);
472 : 0 : return 1;
473 : : }
474 [ - + ]: 1 : printf("Associating ioat_channel %d with core %d\n", i, worker->core);
475 : 1 : t->chan = chan;
476 : 1 : t->next = worker->ctx;
477 : 1 : worker->ctx = t;
478 : :
479 : 1 : worker = worker->next;
480 [ + - ]: 1 : if (worker == NULL) {
481 : 1 : worker = g_workers;
482 : : }
483 : :
484 : 1 : chan = get_next_chan();
485 : 1 : i++;
486 : : }
487 : :
488 : 1 : return 0;
489 : : }
490 : :
491 : : int
492 : 1 : main(int argc, char **argv)
493 : : {
494 : : int rc;
495 : : struct worker_thread *worker, *main_worker;
496 : : unsigned main_core;
497 : :
498 [ - + ]: 1 : if (parse_args(argc, argv) != 0) {
499 : 0 : return 1;
500 : : }
501 : :
502 [ - + ]: 1 : if (init() != 0) {
503 : 0 : return 1;
504 : : }
505 : :
506 [ - + ]: 1 : if (register_workers() != 0) {
507 : 0 : rc = 1;
508 : 0 : goto cleanup;
509 : : }
510 : :
511 [ - + ]: 1 : if (ioat_init() != 0) {
512 : 0 : rc = 1;
513 : 0 : goto cleanup;
514 : : }
515 : :
516 [ - + ]: 1 : if (g_ioat_chan_num == 0) {
517 [ # # ]: 0 : printf("No channels found\n");
518 : 0 : rc = 1;
519 : 0 : goto cleanup;
520 : : }
521 : :
522 [ - + ]: 1 : if (g_user_config.ioat_chan_num > g_ioat_chan_num) {
523 [ # # ]: 0 : printf("%d channels are requested, but only %d are found,"
524 : : "so only test %d channels\n", g_user_config.ioat_chan_num,
525 : : g_ioat_chan_num, g_ioat_chan_num);
526 : 0 : g_user_config.ioat_chan_num = g_ioat_chan_num;
527 : : }
528 : :
529 : 1 : g_next_device = TAILQ_FIRST(&g_devices);
530 : 1 : dump_user_config(&g_user_config);
531 : :
532 [ - + ]: 1 : if (associate_workers_with_chan() != 0) {
533 : 0 : rc = 1;
534 : 0 : goto cleanup;
535 : : }
536 : :
537 : : /* Launch all of the secondary workers */
538 : 1 : main_core = spdk_env_get_current_core();
539 : 1 : main_worker = NULL;
540 : 1 : worker = g_workers;
541 [ + + ]: 2 : while (worker != NULL) {
542 [ - + ]: 1 : if (worker->core != main_core) {
543 : 0 : spdk_env_thread_launch_pinned(worker->core, work_fn, worker);
544 : : } else {
545 [ - + ]: 1 : assert(main_worker == NULL);
546 : 1 : main_worker = worker;
547 : : }
548 : 1 : worker = worker->next;
549 : : }
550 : :
551 [ - + ]: 1 : assert(main_worker != NULL);
552 : 1 : rc = work_fn(main_worker);
553 [ - + ]: 1 : if (rc != 0) {
554 : 0 : goto cleanup;
555 : : }
556 : :
557 : 1 : spdk_env_thread_wait_all();
558 : :
559 : 1 : rc = dump_result();
560 : :
561 : 1 : cleanup:
562 : 1 : unregister_workers();
563 : 1 : ioat_exit();
564 : :
565 : 1 : spdk_env_fini();
566 : 1 : return rc;
567 : : }
|