]> git.proxmox.com Git - ceph.git/blob - ceph/src/spdk/test/unit/lib/thread/thread.c/thread_ut.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spdk / test / unit / lib / thread / thread.c / thread_ut.c
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
34 #include "spdk/stdinc.h"
35
36 #include "spdk_cunit.h"
37
38 #include "spdk_internal/thread.h"
39
40 #include "thread/thread.c"
41 #include "common/lib/ut_multithread.c"
42
43 static int g_sched_rc = 0;
44
45 static int
46 _thread_schedule(struct spdk_thread *thread)
47 {
48 return g_sched_rc;
49 }
50
51 static void
52 thread_alloc(void)
53 {
54 struct spdk_thread *thread;
55
56 /* No schedule callback */
57 spdk_thread_lib_init(NULL, 0);
58 thread = spdk_thread_create(NULL, NULL);
59 SPDK_CU_ASSERT_FATAL(thread != NULL);
60 spdk_set_thread(thread);
61 spdk_thread_exit(thread);
62 spdk_thread_destroy(thread);
63 spdk_thread_lib_fini();
64
65 /* Schedule callback exists */
66 spdk_thread_lib_init(_thread_schedule, 0);
67
68 /* Scheduling succeeds */
69 g_sched_rc = 0;
70 thread = spdk_thread_create(NULL, NULL);
71 SPDK_CU_ASSERT_FATAL(thread != NULL);
72 spdk_set_thread(thread);
73 spdk_thread_exit(thread);
74 spdk_thread_destroy(thread);
75
76 /* Scheduling fails */
77 g_sched_rc = -1;
78 thread = spdk_thread_create(NULL, NULL);
79 SPDK_CU_ASSERT_FATAL(thread == NULL);
80
81 spdk_thread_lib_fini();
82 }
83
84 static void
85 send_msg_cb(void *ctx)
86 {
87 bool *done = ctx;
88
89 *done = true;
90 }
91
92 static void
93 thread_send_msg(void)
94 {
95 struct spdk_thread *thread0;
96 bool done = false;
97
98 allocate_threads(2);
99 set_thread(0);
100 thread0 = spdk_get_thread();
101
102 set_thread(1);
103 /* Simulate thread 1 sending a message to thread 0. */
104 spdk_thread_send_msg(thread0, send_msg_cb, &done);
105
106 /* We have not polled thread 0 yet, so done should be false. */
107 CU_ASSERT(!done);
108
109 /*
110 * Poll thread 1. The message was sent to thread 0, so this should be
111 * a nop and done should still be false.
112 */
113 poll_thread(1);
114 CU_ASSERT(!done);
115
116 /*
117 * Poll thread 0. This should execute the message and done should then
118 * be true.
119 */
120 poll_thread(0);
121 CU_ASSERT(done);
122
123 free_threads();
124 }
125
126 static int
127 poller_run_done(void *ctx)
128 {
129 bool *poller_run = ctx;
130
131 *poller_run = true;
132
133 return -1;
134 }
135
136 static void
137 thread_poller(void)
138 {
139 struct spdk_poller *poller = NULL;
140 bool poller_run = false;
141
142 allocate_threads(1);
143
144 set_thread(0);
145 MOCK_SET(spdk_get_ticks, 0);
146 /* Register a poller with no-wait time and test execution */
147 poller = spdk_poller_register(poller_run_done, &poller_run, 0);
148 CU_ASSERT(poller != NULL);
149
150 poll_threads();
151 CU_ASSERT(poller_run == true);
152
153 spdk_poller_unregister(&poller);
154 CU_ASSERT(poller == NULL);
155
156 /* Register a poller with 1000us wait time and test single execution */
157 poller_run = false;
158 poller = spdk_poller_register(poller_run_done, &poller_run, 1000);
159 CU_ASSERT(poller != NULL);
160
161 poll_threads();
162 CU_ASSERT(poller_run == false);
163
164 spdk_delay_us(1000);
165 poll_threads();
166 CU_ASSERT(poller_run == true);
167
168 poller_run = false;
169 poll_threads();
170 CU_ASSERT(poller_run == false);
171
172 spdk_delay_us(1000);
173 poll_threads();
174 CU_ASSERT(poller_run == true);
175
176 spdk_poller_unregister(&poller);
177 CU_ASSERT(poller == NULL);
178
179 free_threads();
180 }
181
182 static void
183 for_each_cb(void *ctx)
184 {
185 int *count = ctx;
186
187 (*count)++;
188 }
189
190 static void
191 thread_for_each(void)
192 {
193 int count = 0;
194 int i;
195
196 allocate_threads(3);
197 set_thread(0);
198
199 spdk_for_each_thread(for_each_cb, &count, for_each_cb);
200
201 /* We have not polled thread 0 yet, so count should be 0 */
202 CU_ASSERT(count == 0);
203
204 /* Poll each thread to verify the message is passed to each */
205 for (i = 0; i < 3; i++) {
206 poll_thread(i);
207 CU_ASSERT(count == (i + 1));
208 }
209
210 /*
211 * After each thread is called, the completion calls it
212 * one more time.
213 */
214 poll_thread(0);
215 CU_ASSERT(count == 4);
216
217 free_threads();
218 }
219
220 static int
221 channel_create(void *io_device, void *ctx_buf)
222 {
223 return 0;
224 }
225
226 static void
227 channel_destroy(void *io_device, void *ctx_buf)
228 {
229 }
230
231 static void
232 channel_msg(struct spdk_io_channel_iter *i)
233 {
234 struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
235 int *count = spdk_io_channel_get_ctx(ch);
236
237 (*count)++;
238
239 spdk_for_each_channel_continue(i, 0);
240 }
241
242 static void
243 channel_cpl(struct spdk_io_channel_iter *i, int status)
244 {
245 }
246
247 static void
248 for_each_channel_remove(void)
249 {
250 struct spdk_io_channel *ch0, *ch1, *ch2;
251 int io_target;
252 int count = 0;
253
254 allocate_threads(3);
255 set_thread(0);
256 spdk_io_device_register(&io_target, channel_create, channel_destroy, sizeof(int), NULL);
257 ch0 = spdk_get_io_channel(&io_target);
258 set_thread(1);
259 ch1 = spdk_get_io_channel(&io_target);
260 set_thread(2);
261 ch2 = spdk_get_io_channel(&io_target);
262
263 /*
264 * Test that io_channel handles the case where we start to iterate through
265 * the channels, and during the iteration, one of the channels is deleted.
266 * This is done in some different and sometimes non-intuitive orders, because
267 * some operations are deferred and won't execute until their threads are
268 * polled.
269 *
270 * Case #1: Put the I/O channel before spdk_for_each_channel.
271 */
272 set_thread(0);
273 spdk_put_io_channel(ch0);
274 poll_threads();
275 spdk_for_each_channel(&io_target, channel_msg, &count, channel_cpl);
276 poll_threads();
277
278 /*
279 * Case #2: Put the I/O channel after spdk_for_each_channel, but before
280 * thread 0 is polled.
281 */
282 ch0 = spdk_get_io_channel(&io_target);
283 spdk_for_each_channel(&io_target, channel_msg, &count, channel_cpl);
284 spdk_put_io_channel(ch0);
285 poll_threads();
286
287 set_thread(1);
288 spdk_put_io_channel(ch1);
289 set_thread(2);
290 spdk_put_io_channel(ch2);
291 spdk_io_device_unregister(&io_target, NULL);
292 poll_threads();
293
294 free_threads();
295 }
296
297 struct unreg_ctx {
298 bool ch_done;
299 bool foreach_done;
300 };
301
302 static void
303 unreg_ch_done(struct spdk_io_channel_iter *i)
304 {
305 struct unreg_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
306
307 ctx->ch_done = true;
308
309 SPDK_CU_ASSERT_FATAL(i->cur_thread != NULL);
310 spdk_for_each_channel_continue(i, 0);
311 }
312
313 static void
314 unreg_foreach_done(struct spdk_io_channel_iter *i, int status)
315 {
316 struct unreg_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
317
318 ctx->foreach_done = true;
319 }
320
321 static void
322 for_each_channel_unreg(void)
323 {
324 struct spdk_io_channel *ch0;
325 struct io_device *dev;
326 struct unreg_ctx ctx = {};
327 int io_target;
328
329 allocate_threads(1);
330 set_thread(0);
331 CU_ASSERT(TAILQ_EMPTY(&g_io_devices));
332 spdk_io_device_register(&io_target, channel_create, channel_destroy, sizeof(int), NULL);
333 CU_ASSERT(!TAILQ_EMPTY(&g_io_devices));
334 dev = TAILQ_FIRST(&g_io_devices);
335 SPDK_CU_ASSERT_FATAL(dev != NULL);
336 CU_ASSERT(TAILQ_NEXT(dev, tailq) == NULL);
337 ch0 = spdk_get_io_channel(&io_target);
338 spdk_for_each_channel(&io_target, unreg_ch_done, &ctx, unreg_foreach_done);
339
340 spdk_io_device_unregister(&io_target, NULL);
341 /*
342 * There is an outstanding foreach call on the io_device, so the unregister should not
343 * have removed the device.
344 */
345 CU_ASSERT(dev == TAILQ_FIRST(&g_io_devices));
346 spdk_io_device_register(&io_target, channel_create, channel_destroy, sizeof(int), NULL);
347 /*
348 * There is already a device registered at &io_target, so a new io_device should not
349 * have been added to g_io_devices.
350 */
351 CU_ASSERT(dev == TAILQ_FIRST(&g_io_devices));
352 CU_ASSERT(TAILQ_NEXT(dev, tailq) == NULL);
353
354 poll_thread(0);
355 CU_ASSERT(ctx.ch_done == true);
356 CU_ASSERT(ctx.foreach_done == true);
357 /*
358 * There are no more foreach operations outstanding, so we can unregister the device,
359 * even though a channel still exists for the device.
360 */
361 spdk_io_device_unregister(&io_target, NULL);
362 CU_ASSERT(TAILQ_EMPTY(&g_io_devices));
363
364 set_thread(0);
365 spdk_put_io_channel(ch0);
366
367 poll_threads();
368
369 free_threads();
370 }
371
372 static void
373 thread_name(void)
374 {
375 struct spdk_thread *thread;
376 const char *name;
377
378 spdk_thread_lib_init(NULL, 0);
379
380 /* Create thread with no name, which automatically generates one */
381 thread = spdk_thread_create(NULL, NULL);
382 spdk_set_thread(thread);
383 thread = spdk_get_thread();
384 SPDK_CU_ASSERT_FATAL(thread != NULL);
385 name = spdk_thread_get_name(thread);
386 CU_ASSERT(name != NULL);
387 spdk_thread_exit(thread);
388 spdk_thread_destroy(thread);
389
390 /* Create thread named "test_thread" */
391 thread = spdk_thread_create("test_thread", NULL);
392 spdk_set_thread(thread);
393 thread = spdk_get_thread();
394 SPDK_CU_ASSERT_FATAL(thread != NULL);
395 name = spdk_thread_get_name(thread);
396 SPDK_CU_ASSERT_FATAL(name != NULL);
397 CU_ASSERT(strcmp(name, "test_thread") == 0);
398 spdk_thread_exit(thread);
399 spdk_thread_destroy(thread);
400
401 spdk_thread_lib_fini();
402 }
403
404 static uint64_t device1;
405 static uint64_t device2;
406 static uint64_t device3;
407
408 static uint64_t ctx1 = 0x1111;
409 static uint64_t ctx2 = 0x2222;
410
411 static int g_create_cb_calls = 0;
412 static int g_destroy_cb_calls = 0;
413
414 static int
415 create_cb_1(void *io_device, void *ctx_buf)
416 {
417 CU_ASSERT(io_device == &device1);
418 *(uint64_t *)ctx_buf = ctx1;
419 g_create_cb_calls++;
420 return 0;
421 }
422
423 static void
424 destroy_cb_1(void *io_device, void *ctx_buf)
425 {
426 CU_ASSERT(io_device == &device1);
427 CU_ASSERT(*(uint64_t *)ctx_buf == ctx1);
428 g_destroy_cb_calls++;
429 }
430
431 static int
432 create_cb_2(void *io_device, void *ctx_buf)
433 {
434 CU_ASSERT(io_device == &device2);
435 *(uint64_t *)ctx_buf = ctx2;
436 g_create_cb_calls++;
437 return 0;
438 }
439
440 static void
441 destroy_cb_2(void *io_device, void *ctx_buf)
442 {
443 CU_ASSERT(io_device == &device2);
444 CU_ASSERT(*(uint64_t *)ctx_buf == ctx2);
445 g_destroy_cb_calls++;
446 }
447
448 static void
449 channel(void)
450 {
451 struct spdk_io_channel *ch1, *ch2;
452 void *ctx;
453
454 allocate_threads(1);
455 set_thread(0);
456
457 spdk_io_device_register(&device1, create_cb_1, destroy_cb_1, sizeof(ctx1), NULL);
458 spdk_io_device_register(&device2, create_cb_2, destroy_cb_2, sizeof(ctx2), NULL);
459
460 g_create_cb_calls = 0;
461 ch1 = spdk_get_io_channel(&device1);
462 CU_ASSERT(g_create_cb_calls == 1);
463 SPDK_CU_ASSERT_FATAL(ch1 != NULL);
464
465 g_create_cb_calls = 0;
466 ch2 = spdk_get_io_channel(&device1);
467 CU_ASSERT(g_create_cb_calls == 0);
468 CU_ASSERT(ch1 == ch2);
469 SPDK_CU_ASSERT_FATAL(ch2 != NULL);
470
471 g_destroy_cb_calls = 0;
472 spdk_put_io_channel(ch2);
473 poll_threads();
474 CU_ASSERT(g_destroy_cb_calls == 0);
475
476 g_create_cb_calls = 0;
477 ch2 = spdk_get_io_channel(&device2);
478 CU_ASSERT(g_create_cb_calls == 1);
479 CU_ASSERT(ch1 != ch2);
480 SPDK_CU_ASSERT_FATAL(ch2 != NULL);
481
482 ctx = spdk_io_channel_get_ctx(ch2);
483 CU_ASSERT(*(uint64_t *)ctx == ctx2);
484
485 g_destroy_cb_calls = 0;
486 spdk_put_io_channel(ch1);
487 poll_threads();
488 CU_ASSERT(g_destroy_cb_calls == 1);
489
490 g_destroy_cb_calls = 0;
491 spdk_put_io_channel(ch2);
492 poll_threads();
493 CU_ASSERT(g_destroy_cb_calls == 1);
494
495 ch1 = spdk_get_io_channel(&device3);
496 CU_ASSERT(ch1 == NULL);
497
498 spdk_io_device_unregister(&device1, NULL);
499 poll_threads();
500 spdk_io_device_unregister(&device2, NULL);
501 poll_threads();
502 CU_ASSERT(TAILQ_EMPTY(&g_io_devices));
503 free_threads();
504 CU_ASSERT(TAILQ_EMPTY(&g_threads));
505 }
506
507 static int
508 create_cb(void *io_device, void *ctx_buf)
509 {
510 uint64_t *refcnt = (uint64_t *)ctx_buf;
511
512 CU_ASSERT(*refcnt == 0);
513 *refcnt = 1;
514
515 return 0;
516 }
517
518 static void
519 destroy_cb(void *io_device, void *ctx_buf)
520 {
521 uint64_t *refcnt = (uint64_t *)ctx_buf;
522
523 CU_ASSERT(*refcnt == 1);
524 *refcnt = 0;
525 }
526
527 /**
528 * This test is checking that a sequence of get, put, get, put without allowing
529 * the deferred put operation to complete doesn't result in releasing the memory
530 * for the channel twice.
531 */
532 static void
533 channel_destroy_races(void)
534 {
535 uint64_t device;
536 struct spdk_io_channel *ch;
537
538 allocate_threads(1);
539 set_thread(0);
540
541 spdk_io_device_register(&device, create_cb, destroy_cb, sizeof(uint64_t), NULL);
542
543 ch = spdk_get_io_channel(&device);
544 SPDK_CU_ASSERT_FATAL(ch != NULL);
545
546 spdk_put_io_channel(ch);
547
548 ch = spdk_get_io_channel(&device);
549 SPDK_CU_ASSERT_FATAL(ch != NULL);
550
551 spdk_put_io_channel(ch);
552 poll_threads();
553
554 spdk_io_device_unregister(&device, NULL);
555 poll_threads();
556
557 CU_ASSERT(TAILQ_EMPTY(&g_io_devices));
558 free_threads();
559 CU_ASSERT(TAILQ_EMPTY(&g_threads));
560 }
561
562 int
563 main(int argc, char **argv)
564 {
565 CU_pSuite suite = NULL;
566 unsigned int num_failures;
567
568 if (CU_initialize_registry() != CUE_SUCCESS) {
569 return CU_get_error();
570 }
571
572 suite = CU_add_suite("io_channel", NULL, NULL);
573 if (suite == NULL) {
574 CU_cleanup_registry();
575 return CU_get_error();
576 }
577
578 if (
579 CU_add_test(suite, "thread_alloc", thread_alloc) == NULL ||
580 CU_add_test(suite, "thread_send_msg", thread_send_msg) == NULL ||
581 CU_add_test(suite, "thread_poller", thread_poller) == NULL ||
582 CU_add_test(suite, "thread_for_each", thread_for_each) == NULL ||
583 CU_add_test(suite, "for_each_channel_remove", for_each_channel_remove) == NULL ||
584 CU_add_test(suite, "for_each_channel_unreg", for_each_channel_unreg) == NULL ||
585 CU_add_test(suite, "thread_name", thread_name) == NULL ||
586 CU_add_test(suite, "channel", channel) == NULL ||
587 CU_add_test(suite, "channel_destroy_races", channel_destroy_races) == NULL
588 ) {
589 CU_cleanup_registry();
590 return CU_get_error();
591 }
592
593 CU_basic_set_mode(CU_BRM_VERBOSE);
594 CU_basic_run_tests();
595 num_failures = CU_get_number_of_failures();
596 CU_cleanup_registry();
597 return num_failures;
598 }