]> git.proxmox.com Git - ceph.git/blame - ceph/src/spdk/examples/ioat/verify/verify.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spdk / examples / ioat / verify / verify.c
CommitLineData
7c673cae
FG
1/*-
2 * BSD LICENSE
3 *
4 * Copyright (c) Intel Corporation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
11fdf7f2 34#include "spdk/stdinc.h"
7c673cae
FG
35
36#include "spdk/ioat.h"
37#include "spdk/env.h"
38#include "spdk/queue.h"
39#include "spdk/string.h"
11fdf7f2 40#include "spdk/util.h"
7c673cae
FG
41
42#define SRC_BUFFER_SIZE (512*1024)
43
44enum ioat_task_type {
45 IOAT_COPY_TYPE,
46 IOAT_FILL_TYPE,
47};
48
49struct user_config {
50 int queue_depth;
51 int time_in_sec;
52 char *core_mask;
53};
54
55struct ioat_device {
56 struct spdk_ioat_chan *ioat;
57 TAILQ_ENTRY(ioat_device) tailq;
58};
59
60static TAILQ_HEAD(, ioat_device) g_devices;
61static struct ioat_device *g_next_device;
62
63static struct user_config g_user_config;
64
65struct thread_entry {
66 struct spdk_ioat_chan *chan;
67 uint64_t xfer_completed;
68 uint64_t xfer_failed;
69 uint64_t fill_completed;
70 uint64_t fill_failed;
71 uint64_t current_queue_depth;
72 unsigned lcore_id;
73 bool is_draining;
11fdf7f2 74 bool init_failed;
7c673cae
FG
75 struct spdk_mempool *data_pool;
76 struct spdk_mempool *task_pool;
77};
78
79struct ioat_task {
80 enum ioat_task_type type;
81 struct thread_entry *thread_entry;
82 void *buffer;
83 int len;
84 uint64_t fill_pattern;
85 void *src;
86 void *dst;
87};
88
89static __thread unsigned int seed = 0;
90
91static unsigned char *g_src;
92
93static void submit_single_xfer(struct ioat_task *ioat_task);
94
95static void
96construct_user_config(struct user_config *self)
97{
98 self->queue_depth = 32;
99 self->time_in_sec = 10;
100 self->core_mask = "0x1";
101}
102
103static void
104dump_user_config(struct user_config *self)
105{
106 printf("User configuration:\n");
107 printf("Run time: %u seconds\n", self->time_in_sec);
108 printf("Core mask: %s\n", self->core_mask);
109 printf("Queue depth: %u\n", self->queue_depth);
110}
111
112static void
113ioat_exit(void)
114{
115 struct ioat_device *dev;
116
117 while (!TAILQ_EMPTY(&g_devices)) {
118 dev = TAILQ_FIRST(&g_devices);
119 TAILQ_REMOVE(&g_devices, dev, tailq);
120 if (dev->ioat) {
121 spdk_ioat_detach(dev->ioat);
122 }
123 free(dev);
124 }
125}
126static void prepare_ioat_task(struct thread_entry *thread_entry, struct ioat_task *ioat_task)
127{
128 int len;
11fdf7f2
TL
129 uintptr_t src_offset;
130 uintptr_t dst_offset;
7c673cae
FG
131 uint64_t fill_pattern;
132
133 if (ioat_task->type == IOAT_FILL_TYPE) {
134 fill_pattern = rand_r(&seed);
135 fill_pattern = fill_pattern << 32 | rand_r(&seed);
136
11fdf7f2
TL
137 /* Ensure that the length of memset block is 8 Bytes aligned.
138 * In case the buffer crosses hugepage boundary and must be split,
139 * we also need to ensure 8 byte address alignment. We do it
140 * unconditionally to keep things simple.
141 */
142 len = 8 + ((rand_r(&seed) % (SRC_BUFFER_SIZE - 16)) & ~0x7);
143 dst_offset = 8 + rand_r(&seed) % (SRC_BUFFER_SIZE - 8 - len);
7c673cae 144 ioat_task->fill_pattern = fill_pattern;
11fdf7f2 145 ioat_task->dst = (void *)(((uintptr_t)ioat_task->buffer + dst_offset) & ~0x7);
7c673cae
FG
146 } else {
147 src_offset = rand_r(&seed) % SRC_BUFFER_SIZE;
148 len = rand_r(&seed) % (SRC_BUFFER_SIZE - src_offset);
149 dst_offset = rand_r(&seed) % (SRC_BUFFER_SIZE - len);
150
151 memset(ioat_task->buffer, 0, SRC_BUFFER_SIZE);
11fdf7f2
TL
152 ioat_task->src = (void *)((uintptr_t)g_src + src_offset);
153 ioat_task->dst = (void *)((uintptr_t)ioat_task->buffer + dst_offset);
7c673cae
FG
154 }
155 ioat_task->len = len;
7c673cae
FG
156 ioat_task->thread_entry = thread_entry;
157}
158
159static void
160ioat_done(void *cb_arg)
161{
162 char *value;
163 int i, failed = 0;
164 struct ioat_task *ioat_task = (struct ioat_task *)cb_arg;
165 struct thread_entry *thread_entry = ioat_task->thread_entry;
166
167 if (ioat_task->type == IOAT_FILL_TYPE) {
168 value = ioat_task->dst;
169 for (i = 0; i < ioat_task->len / 8; i++) {
170 if (memcmp(value, &ioat_task->fill_pattern, 8) != 0) {
171 thread_entry->fill_failed++;
172 failed = 1;
173 break;
174 }
175 value += 8;
176 }
11fdf7f2 177 if (!failed) {
7c673cae 178 thread_entry->fill_completed++;
11fdf7f2 179 }
7c673cae
FG
180 } else {
181 if (memcmp(ioat_task->src, ioat_task->dst, ioat_task->len)) {
182 thread_entry->xfer_failed++;
183 } else {
184 thread_entry->xfer_completed++;
185 }
186 }
187
188 thread_entry->current_queue_depth--;
189 if (thread_entry->is_draining) {
190 spdk_mempool_put(thread_entry->data_pool, ioat_task->buffer);
191 spdk_mempool_put(thread_entry->task_pool, ioat_task);
192 } else {
193 prepare_ioat_task(thread_entry, ioat_task);
194 submit_single_xfer(ioat_task);
195 }
196}
197
198static bool
199probe_cb(void *cb_ctx, struct spdk_pci_device *pci_dev)
200{
201 printf(" Found matching device at %04x:%02x:%02x.%x "
202 "vendor:0x%04x device:0x%04x\n",
203 spdk_pci_device_get_domain(pci_dev),
204 spdk_pci_device_get_bus(pci_dev), spdk_pci_device_get_dev(pci_dev),
205 spdk_pci_device_get_func(pci_dev),
206 spdk_pci_device_get_vendor_id(pci_dev), spdk_pci_device_get_device_id(pci_dev));
207
208 return true;
209}
210
211static void
212attach_cb(void *cb_ctx, struct spdk_pci_device *pci_dev, struct spdk_ioat_chan *ioat)
213{
214 struct ioat_device *dev;
215
216 dev = malloc(sizeof(*dev));
217 if (dev == NULL) {
218 printf("Failed to allocate device struct\n");
219 return;
220 }
221 memset(dev, 0, sizeof(*dev));
222
223 dev->ioat = ioat;
224 TAILQ_INSERT_TAIL(&g_devices, dev, tailq);
225}
226
227static int
228ioat_init(void)
229{
230 TAILQ_INIT(&g_devices);
231
232 if (spdk_ioat_probe(NULL, probe_cb, attach_cb) != 0) {
233 fprintf(stderr, "ioat_probe() failed\n");
234 return 1;
235 }
236
237 return 0;
238}
239
240static void
241usage(char *program_name)
242{
243 printf("%s options\n", program_name);
244 printf("\t[-h help message]\n");
245 printf("\t[-c core mask for distributing I/O submission/completion work]\n");
246 printf("\t[-t time in seconds]\n");
247 printf("\t[-q queue depth]\n");
248}
249
250static int
251parse_args(int argc, char **argv)
252{
253 int op;
254
255 construct_user_config(&g_user_config);
256 while ((op = getopt(argc, argv, "c:ht:q:")) != -1) {
257 switch (op) {
258 case 't':
9f95a23c 259 g_user_config.time_in_sec = spdk_strtol(optarg, 10);
7c673cae
FG
260 break;
261 case 'c':
262 g_user_config.core_mask = optarg;
263 break;
264 case 'q':
9f95a23c 265 g_user_config.queue_depth = spdk_strtol(optarg, 10);
7c673cae
FG
266 break;
267 case 'h':
268 usage(argv[0]);
269 exit(0);
270 default:
271 usage(argv[0]);
272 return 1;
273 }
274 }
9f95a23c
TL
275 if (g_user_config.time_in_sec <= 0 || !g_user_config.core_mask ||
276 g_user_config.queue_depth <= 0) {
7c673cae
FG
277 usage(argv[0]);
278 return 1;
279 }
11fdf7f2 280
7c673cae
FG
281 return 0;
282}
283
284static void
285drain_xfers(struct thread_entry *thread_entry)
286{
287 while (thread_entry->current_queue_depth > 0) {
288 spdk_ioat_process_events(thread_entry->chan);
289 }
290}
291
292static void
293submit_single_xfer(struct ioat_task *ioat_task)
294{
295 if (ioat_task->type == IOAT_FILL_TYPE)
296 spdk_ioat_submit_fill(ioat_task->thread_entry->chan, ioat_task, ioat_done,
297 ioat_task->dst, ioat_task->fill_pattern, ioat_task->len);
298 else
299 spdk_ioat_submit_copy(ioat_task->thread_entry->chan, ioat_task, ioat_done,
300 ioat_task->dst, ioat_task->src, ioat_task->len);
301 ioat_task->thread_entry->current_queue_depth++;
302}
303
304static void
305submit_xfers(struct thread_entry *thread_entry, uint64_t queue_depth)
306{
307 while (queue_depth-- > 0) {
308 struct ioat_task *ioat_task = NULL;
309 ioat_task = spdk_mempool_get(thread_entry->task_pool);
310 ioat_task->buffer = spdk_mempool_get(thread_entry->data_pool);
311
312 ioat_task->type = IOAT_COPY_TYPE;
313 if (spdk_ioat_get_dma_capabilities(thread_entry->chan) & SPDK_IOAT_ENGINE_FILL_SUPPORTED) {
11fdf7f2 314 if (queue_depth % 2) {
7c673cae 315 ioat_task->type = IOAT_FILL_TYPE;
11fdf7f2 316 }
7c673cae
FG
317 }
318 prepare_ioat_task(thread_entry, ioat_task);
319 submit_single_xfer(ioat_task);
320 }
321}
322
323static int
324work_fn(void *arg)
325{
326 uint64_t tsc_end;
327 char buf_pool_name[20], task_pool_name[20];
328 struct thread_entry *t = (struct thread_entry *)arg;
329
330 if (!t->chan) {
331 return 0;
332 }
333
11fdf7f2 334 t->lcore_id = spdk_env_get_current_core();
7c673cae 335
11fdf7f2
TL
336 snprintf(buf_pool_name, sizeof(buf_pool_name), "buf_pool_%u", t->lcore_id);
337 snprintf(task_pool_name, sizeof(task_pool_name), "task_pool_%u", t->lcore_id);
338 t->data_pool = spdk_mempool_create(buf_pool_name, g_user_config.queue_depth, SRC_BUFFER_SIZE,
339 SPDK_MEMPOOL_DEFAULT_CACHE_SIZE,
7c673cae
FG
340 SPDK_ENV_SOCKET_ID_ANY);
341 t->task_pool = spdk_mempool_create(task_pool_name, g_user_config.queue_depth,
11fdf7f2
TL
342 sizeof(struct ioat_task),
343 SPDK_MEMPOOL_DEFAULT_CACHE_SIZE,
344 SPDK_ENV_SOCKET_ID_ANY);
7c673cae
FG
345 if (!t->data_pool || !t->task_pool) {
346 fprintf(stderr, "Could not allocate buffer pool.\n");
11fdf7f2 347 t->init_failed = true;
7c673cae
FG
348 return 1;
349 }
350
351 tsc_end = spdk_get_ticks() + g_user_config.time_in_sec * spdk_get_ticks_hz();
352
353 submit_xfers(t, g_user_config.queue_depth);
354 while (spdk_get_ticks() < tsc_end) {
355 spdk_ioat_process_events(t->chan);
356 }
357
358 t->is_draining = true;
359 drain_xfers(t);
360
361 return 0;
362}
363
364static int
365init_src_buffer(void)
366{
367 int i;
368
11fdf7f2 369 g_src = spdk_dma_zmalloc(SRC_BUFFER_SIZE, 512, NULL);
7c673cae
FG
370 if (g_src == NULL) {
371 fprintf(stderr, "Allocate src buffer failed\n");
372 return -1;
373 }
374
375 for (i = 0; i < SRC_BUFFER_SIZE / 4; i++) {
376 memset((g_src + (4 * i)), i, 4);
377 }
378
379 return 0;
380}
381
382static int
383init(void)
384{
385 struct spdk_env_opts opts;
386
387 spdk_env_opts_init(&opts);
388 opts.name = "verify";
389 opts.core_mask = g_user_config.core_mask;
11fdf7f2
TL
390 if (spdk_env_init(&opts) < 0) {
391 fprintf(stderr, "Unable to initialize SPDK env\n");
392 return 1;
393 }
7c673cae
FG
394
395 if (init_src_buffer() != 0) {
396 fprintf(stderr, "Could not init src buffer\n");
397 return 1;
398 }
399 if (ioat_init() != 0) {
400 fprintf(stderr, "Could not init ioat\n");
401 return 1;
402 }
403
404 return 0;
405}
406
407static int
11fdf7f2 408dump_result(struct thread_entry *threads, uint32_t num_threads)
7c673cae 409{
11fdf7f2 410 uint32_t i;
7c673cae
FG
411 uint64_t total_completed = 0;
412 uint64_t total_failed = 0;
413
11fdf7f2 414 for (i = 0; i < num_threads; i++) {
7c673cae 415 struct thread_entry *t = &threads[i];
11fdf7f2
TL
416
417 if (!t->chan) {
418 continue;
419 }
420
421 if (t->init_failed) {
422 total_failed++;
423 continue;
424 }
425
7c673cae
FG
426 total_completed += t->xfer_completed;
427 total_completed += t->fill_completed;
428 total_failed += t->xfer_failed;
429 total_failed += t->fill_failed;
11fdf7f2
TL
430 if (total_completed || total_failed)
431 printf("lcore = %d, copy success = %ld, copy failed = %ld, fill success = %ld, fill failed = %ld\n",
7c673cae
FG
432 t->lcore_id, t->xfer_completed, t->xfer_failed, t->fill_completed, t->fill_failed);
433 }
434 return total_failed ? 1 : 0;
435}
436
437static struct spdk_ioat_chan *
438get_next_chan(void)
439{
440 struct spdk_ioat_chan *chan;
441
442 if (g_next_device == NULL) {
11fdf7f2
TL
443 fprintf(stderr, "Not enough ioat channels found. Check that ioat channels are bound\n");
444 fprintf(stderr, "to uio_pci_generic or vfio-pci. scripts/setup.sh can help with this.\n");
7c673cae
FG
445 return NULL;
446 }
447
448 chan = g_next_device->ioat;
449
450 g_next_device = TAILQ_NEXT(g_next_device, tailq);
451
452 return chan;
453}
454
11fdf7f2
TL
455static uint32_t
456get_max_core(void)
457{
458 uint32_t i;
459 uint32_t max_core = 0;
460
461 SPDK_ENV_FOREACH_CORE(i) {
462 if (i > max_core) {
463 max_core = i;
464 }
465 }
466
467 return max_core;
468}
469
7c673cae
FG
470int
471main(int argc, char **argv)
472{
473 uint32_t i, current_core;
11fdf7f2
TL
474 struct thread_entry *threads;
475 uint32_t num_threads;
7c673cae
FG
476 int rc;
477
478 if (parse_args(argc, argv) != 0) {
479 return 1;
480 }
481
482 if (init() != 0) {
483 return 1;
484 }
485
486 dump_user_config(&g_user_config);
487
488 g_next_device = TAILQ_FIRST(&g_devices);
489
11fdf7f2
TL
490 num_threads = get_max_core() + 1;
491 threads = calloc(num_threads, sizeof(*threads));
492 if (!threads) {
493 fprintf(stderr, "Thread memory allocation failed\n");
7c673cae
FG
494 rc = 1;
495 goto cleanup;
496 }
497
11fdf7f2 498 current_core = spdk_env_get_current_core();
7c673cae
FG
499 SPDK_ENV_FOREACH_CORE(i) {
500 if (i != current_core) {
11fdf7f2
TL
501 threads[i].chan = get_next_chan();
502 spdk_env_thread_launch_pinned(i, work_fn, &threads[i]);
7c673cae
FG
503 }
504 }
505
11fdf7f2
TL
506 threads[current_core].chan = get_next_chan();
507 work_fn(&threads[current_core]);
508
509 spdk_env_thread_wait_all();
510 rc = dump_result(threads, num_threads);
7c673cae
FG
511
512cleanup:
11fdf7f2 513 spdk_dma_free(g_src);
7c673cae 514 ioat_exit();
11fdf7f2 515 free(threads);
7c673cae
FG
516
517 return rc;
518}