Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 : : */
4 : :
5 : : #include "spdk/stdinc.h"
6 : :
7 : : #include "spdk/env.h"
8 : : #include "spdk/event.h"
9 : : #include "spdk/string.h"
10 : : #include "spdk/thread.h"
11 : : #include "spdk/util.h"
12 : : #include "spdk_internal/thread.h"
13 : : #include "spdk/barrier.h"
14 : :
15 : : #include "thread/thread.c"
16 : :
17 : : /*
18 : : * Used by multiple tests
19 : : */
20 : :
21 : : typedef void (*test_setup_fn)(void);
22 : :
23 : : struct test {
24 : : /* Initialized in g_tests array */
25 : : const char *name;
26 : : uint32_t thread_count;
27 : : test_setup_fn setup_fn;
28 : : spdk_poller_fn end_fn;
29 : : uint32_t poller_thread_number;
30 : : /* State set while a test is running */
31 : : struct spdk_poller *poller;
32 : : };
33 : :
34 : : #define ASSERT(cond) do { \
35 : : if (cond) { \
36 : : g_pass++; \
37 : : } else { \
38 : : g_fail++; \
39 : : printf("FAIL: %s:%d %s %s\n", __FILE__, __LINE__, __func__, #cond); \
40 : : } \
41 : : } while (0);
42 : :
43 : : #define WORKER_COUNT 2
44 : :
45 : : static uint32_t g_pass;
46 : : static uint32_t g_fail;
47 : : static struct spdk_thread *g_thread[WORKER_COUNT];
48 : : /* Protects g_lock_error_count during updates by spin_abort_fn(). */
49 : : pthread_mutex_t g_lock_lock = PTHREAD_MUTEX_INITIALIZER;
50 : : uint32_t g_lock_error_count[SPIN_ERR_LAST];
51 : :
52 : : static void launch_next_test(void *arg);
53 : :
54 : : static bool
55 : 32 : check_spin_err_count(enum spin_error *expect)
56 : : {
57 : : enum spin_error i;
58 : 32 : bool ret = true;
59 : :
60 [ + + ]: 352 : for (i = SPIN_ERR_NONE; i < SPIN_ERR_LAST; i++) {
61 [ - + ]: 320 : if (g_lock_error_count[i] != expect[i]) {
62 [ # # ]: 0 : printf("FAIL: %s: Error %d expected %u, got %u\n", __func__, i,
63 : 0 : expect[i], g_lock_error_count[i]);
64 : 0 : ret = false;
65 : : }
66 : : }
67 : :
68 : 32 : return ret;
69 : : }
70 : :
71 : : /* A spin_abort_fn() implementation */
72 : : static void
73 : 20 : do_not_abort(enum spin_error error)
74 : : {
75 : 20 : struct spdk_thread *thread = spdk_get_thread();
76 : : uint32_t i;
77 : :
78 : : /*
79 : : * Only count on threads for the current test. Those from a previous test may continue to
80 : : * rack up errors in their death throes. A real application will abort() or exit() on the
81 : : * first error.
82 : : */
83 [ + + ]: 60 : for (i = 0; i < SPDK_COUNTOF(g_thread); i++) {
84 [ + + ]: 40 : if (g_thread[i] != thread) {
85 : 24 : continue;
86 : : }
87 [ + - - - ]: 16 : ASSERT(error >= SPIN_ERR_NONE && error < SPIN_ERR_LAST);
88 [ + - ]: 16 : if (error >= SPIN_ERR_NONE && error < SPIN_ERR_LAST) {
89 [ - + ]: 16 : pthread_mutex_lock(&g_lock_lock);
90 : 16 : g_lock_error_count[error]++;
91 [ - + ]: 16 : pthread_mutex_unlock(&g_lock_lock);
92 : : }
93 : : }
94 : 20 : }
95 : :
96 : : /*
97 : : * contend - make sure that two concurrent threads can take turns at getting the lock
98 : : */
99 : :
100 : : struct contend_worker_data {
101 : : struct spdk_poller *poller;
102 : : uint64_t wait_time;
103 : : uint64_t hold_time;
104 : : uint32_t increments;
105 : : uint32_t delay_us;
106 : : uint32_t bit;
107 : : };
108 : :
109 : : static struct spdk_spinlock g_contend_spinlock;
110 : : static uint32_t g_contend_remaining;
111 : : static uint32_t g_get_lock_times = 50000;
112 : : static struct contend_worker_data g_contend_data[WORKER_COUNT] = {
113 : : { .bit = 0, .delay_us = 3 },
114 : : { .bit = 1, .delay_us = 5 },
115 : : };
116 : :
117 : : static inline uint64_t
118 : 800000 : timediff(struct timespec *ts0, struct timespec *ts1)
119 : : {
120 : 800000 : return (ts1->tv_sec - ts0->tv_sec) * SPDK_SEC_TO_NSEC + ts1->tv_nsec - ts0->tv_nsec;
121 : : }
122 : :
123 : : static uint32_t g_contend_word;
124 : :
125 : : static int
126 : 400000 : contend_worker_fn(void *arg)
127 : : {
128 : 400000 : struct contend_worker_data *data = arg;
129 : 400000 : struct timespec ts0, ts1, ts2;
130 [ - + ]: 400000 : const uint32_t mask = 1 << data->bit;
131 : :
132 : 400000 : clock_gettime(CLOCK_MONOTONIC, &ts0);
133 : 400000 : spdk_spin_lock(&g_contend_spinlock);
134 : 400000 : clock_gettime(CLOCK_MONOTONIC, &ts1);
135 : 400000 : data->wait_time += timediff(&ts0, &ts1);
136 : :
137 [ + + - ]: 400000 : switch (data->increments & 0x1) {
138 : 200000 : case 0:
139 [ + - ]: 200000 : ASSERT((g_contend_word & mask) == 0);
140 : 200000 : g_contend_word |= mask;
141 : 200000 : break;
142 : 200000 : case 1:
143 [ + - ]: 200000 : ASSERT((g_contend_word & mask) == mask);
144 : 200000 : g_contend_word ^= mask;
145 : 200000 : break;
146 : 0 : default:
147 : 0 : abort();
148 : : }
149 : 400000 : data->increments++;
150 : 400000 : spdk_delay_us(data->delay_us);
151 : :
152 [ + + ]: 400000 : if (data->increments == g_get_lock_times) {
153 : 8 : g_contend_remaining--;
154 : 8 : spdk_poller_unregister(&data->poller);
155 [ - + ]: 8 : assert(data->poller == NULL);
156 : : }
157 : :
158 : 400000 : spdk_spin_unlock(&g_contend_spinlock);
159 : 400000 : clock_gettime(CLOCK_MONOTONIC, &ts2);
160 : 400000 : data->hold_time += timediff(&ts1, &ts2);
161 : :
162 : 400000 : return SPDK_POLLER_BUSY;
163 : : }
164 : :
165 : : static void
166 : 8 : contend_start_worker_poller(void *ctx)
167 : : {
168 : 8 : struct contend_worker_data *data = ctx;
169 : :
170 : 8 : data->poller = SPDK_POLLER_REGISTER(contend_worker_fn, data, 0);
171 [ - + ]: 8 : if (data->poller == NULL) {
172 [ # # ]: 0 : fprintf(stderr, "Failed to start poller\n");
173 : 0 : abort();
174 : : }
175 : 8 : }
176 : :
177 : : static void
178 : 4 : contend_setup(void)
179 : : {
180 : : uint32_t i;
181 : :
182 [ - + ]: 4 : memset(&g_contend_spinlock, 0, sizeof(g_contend_spinlock));
183 : 4 : spdk_spin_init(&g_contend_spinlock);
184 : 4 : g_contend_remaining = SPDK_COUNTOF(g_contend_data);
185 : :
186 : : /* Add a poller to each thread */
187 [ + + ]: 12 : for (i = 0; i < SPDK_COUNTOF(g_contend_data); i++) {
188 : 8 : spdk_thread_send_msg(g_thread[i], contend_start_worker_poller, &g_contend_data[i]);
189 : : }
190 : 4 : }
191 : :
192 : : static int
193 : 21221 : contend_end(void *arg)
194 : : {
195 : 21221 : struct test *test = arg;
196 : 21221 : enum spin_error expect[SPIN_ERR_LAST] = { 0 };
197 : : uint32_t i;
198 : :
199 [ + + ]: 21221 : if (g_contend_remaining != 0) {
200 : 21217 : return SPDK_POLLER_IDLE;
201 : : }
202 : :
203 [ + - - - ]: 4 : ASSERT(check_spin_err_count(expect));
204 [ + - - - ]: 4 : ASSERT(g_get_lock_times == g_contend_data[0].increments);
205 [ + - - - ]: 4 : ASSERT(g_get_lock_times == g_contend_data[1].increments);
206 : :
207 [ - + ]: 4 : printf("%8s %8s %8s %8s %8s\n", "Worker", "Delay", "Wait us", "Hold us", "Total us");
208 [ + + ]: 12 : for (i = 0; i < SPDK_COUNTOF(g_contend_data); i++) {
209 [ - + ]: 8 : printf("%8" PRIu32 " %8" PRIu32 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 "\n",
210 : 4 : i, g_contend_data[i].delay_us,
211 : 8 : g_contend_data[i].wait_time / 1000, g_contend_data[i].hold_time / 1000,
212 : 8 : (g_contend_data[i].wait_time + g_contend_data[i].hold_time) / 1000);
213 : : }
214 : :
215 : 4 : spdk_poller_unregister(&test->poller);
216 : 4 : spdk_thread_send_msg(spdk_thread_get_app_thread(), launch_next_test, NULL);
217 : 4 : return SPDK_POLLER_BUSY;
218 : : }
219 : :
220 : : /*
221 : : * hold_by_poller - a lock held by a poller when it returns trips an assert
222 : : */
223 : :
224 : : static struct spdk_spinlock g_hold_by_poller_spinlock;
225 : : struct spdk_poller *g_hold_by_poller_poller;
226 : : static bool g_hold_by_poller_done;
227 : :
228 : : static int
229 : 8 : hold_by_poller(void *arg)
230 : : {
231 : : static int times_called = 0;
232 : 8 : enum spin_error expect[SPIN_ERR_LAST] = { 0 };
233 : :
234 : : /* This polller will be called three times, trying to take the lock the first two times. */
235 [ + + - ]: 8 : switch (times_called) {
236 : 4 : case 0:
237 [ + - ]: 4 : ASSERT(check_spin_err_count(expect));
238 : 4 : break;
239 : 4 : case 1:
240 : 4 : expect[SPIN_ERR_HOLD_DURING_SWITCH] = 1;
241 [ + - ]: 4 : ASSERT(check_spin_err_count(expect));
242 : 4 : break;
243 : 0 : default:
244 : 0 : abort();
245 : : }
246 : :
247 : 8 : spdk_spin_lock(&g_hold_by_poller_spinlock);
248 : :
249 : 8 : memset(expect, 0, sizeof(expect));
250 [ + + - ]: 8 : switch (times_called) {
251 : 4 : case 0:
252 [ + - ]: 4 : ASSERT(check_spin_err_count(expect));
253 : 4 : break;
254 : 4 : case 1:
255 : 4 : expect[SPIN_ERR_DEADLOCK] = 1;
256 : 4 : expect[SPIN_ERR_HOLD_DURING_SWITCH] = 1;
257 [ + - ]: 4 : ASSERT(check_spin_err_count(expect));
258 : : /*
259 : : * Unlock so that future polls don't continue to increase the "hold during switch"
260 : : * count. Without this, the SPIN_ERR_HOLD_DURING_SWITCH is indeterminant.
261 : : */
262 : 4 : spdk_spin_unlock(&g_hold_by_poller_spinlock);
263 [ + - ]: 4 : ASSERT(check_spin_err_count(expect));
264 : 4 : spdk_poller_unregister(&g_hold_by_poller_poller);
265 : 4 : g_hold_by_poller_done = true;
266 : 4 : break;
267 : 0 : default:
268 : 0 : abort();
269 : : }
270 : :
271 : 8 : times_called++;
272 : :
273 : 8 : return SPDK_POLLER_BUSY;
274 : : }
275 : :
276 : : static void
277 : 4 : hold_by_poller_start(void *arg)
278 : : {
279 [ - + ]: 4 : memset(g_lock_error_count, 0, sizeof(g_lock_error_count));
280 : 4 : spdk_spin_init(&g_hold_by_poller_spinlock);
281 : :
282 : 4 : g_hold_by_poller_poller = spdk_poller_register(hold_by_poller, NULL, 0);
283 : 4 : }
284 : :
285 : : static void
286 : 4 : hold_by_poller_setup(void)
287 : : {
288 : 4 : spdk_thread_send_msg(g_thread[0], hold_by_poller_start, NULL);
289 : 4 : }
290 : :
291 : : static int
292 : 8 : hold_by_poller_end(void *arg)
293 : : {
294 : 8 : struct test *test = arg;
295 : 8 : enum spin_error expect[SPIN_ERR_LAST] = { 0 };
296 : :
297 : : /* Wait for hold_by_poller() to complete its work. */
298 [ + + + + ]: 8 : if (!g_hold_by_poller_done) {
299 : 4 : return SPDK_POLLER_IDLE;
300 : : }
301 : :
302 : : /* Some final checks to be sure all the expected errors were seen */
303 : 4 : expect[SPIN_ERR_DEADLOCK] = 1;
304 : 4 : expect[SPIN_ERR_HOLD_DURING_SWITCH] = 1;
305 [ + - - - ]: 4 : ASSERT(check_spin_err_count(expect));
306 : :
307 : : /* All done, move on to next test */
308 : 4 : spdk_poller_unregister(&test->poller);
309 : 4 : spdk_thread_send_msg(spdk_thread_get_app_thread(), launch_next_test, NULL);
310 : :
311 : 4 : return SPDK_POLLER_BUSY;
312 : : }
313 : :
314 : : /*
315 : : * hold_by_message - A message sent to a thread retains the lock when it returns.
316 : : */
317 : :
318 : : static struct spdk_spinlock g_hold_by_message_spinlock;
319 : : static bool g_hold_by_message_done;
320 : :
321 : : static void
322 : 4 : hold_by_message(void *ctx)
323 : : {
324 : 4 : spdk_spin_lock(&g_hold_by_message_spinlock);
325 : :
326 : 4 : g_hold_by_message_done = true;
327 : 4 : }
328 : :
329 : : static void
330 : 4 : hold_by_message_setup(void)
331 : : {
332 [ - + ]: 4 : memset(g_lock_error_count, 0, sizeof(g_lock_error_count));
333 : 4 : spdk_spin_init(&g_hold_by_message_spinlock);
334 : :
335 : 4 : spdk_thread_send_msg(g_thread[0], hold_by_message, NULL);
336 : 4 : }
337 : :
338 : : static int
339 : 4 : hold_by_message_end(void *arg)
340 : : {
341 : 4 : struct test *test = arg;
342 : 4 : enum spin_error expect[SPIN_ERR_LAST] = { 0 };
343 : :
344 : : /* Wait for the message to be processed */
345 [ - + - + ]: 4 : if (!g_hold_by_message_done) {
346 : 0 : return SPDK_POLLER_IDLE;
347 : : }
348 : :
349 : : /* Verify an error was seen */
350 : 4 : expect[SPIN_ERR_HOLD_DURING_SWITCH] = 1;
351 [ + - - - ]: 4 : ASSERT(check_spin_err_count(expect));
352 : :
353 : : /* All done, move on to next test */
354 : 4 : spdk_poller_unregister(&test->poller);
355 : 4 : spdk_thread_send_msg(spdk_thread_get_app_thread(), launch_next_test, NULL);
356 : :
357 : 4 : return SPDK_POLLER_BUSY;
358 : : }
359 : :
360 : : /*
361 : : * Test definitions
362 : : */
363 : :
364 : : static void
365 : 12 : start_threads(uint32_t count)
366 : : {
367 : : struct spdk_cpuset *cpuset;
368 : : uint32_t i;
369 : :
370 : 12 : cpuset = spdk_cpuset_alloc();
371 [ - + ]: 12 : if (cpuset == NULL) {
372 [ # # ]: 0 : fprintf(stderr, "failed to allocate cpuset\n");
373 : 0 : abort();
374 : : }
375 : :
376 [ - + ]: 12 : assert(count <= SPDK_COUNTOF(g_thread));
377 : :
378 [ + + ]: 28 : for (i = 0; i < count; i++) {
379 : 16 : spdk_cpuset_zero(cpuset);
380 : 16 : spdk_cpuset_set_cpu(cpuset, i, true);
381 : 16 : g_thread[i] = spdk_thread_create("worker", cpuset);
382 [ - + ]: 16 : if (g_thread[i] == NULL) {
383 [ # # ]: 0 : fprintf(stderr, "failed to create thread\n");
384 : 0 : abort();
385 : : }
386 : : }
387 : 12 : spdk_cpuset_free(cpuset);
388 : 12 : }
389 : :
390 : : static void
391 : 16 : stop_thread(void *arg)
392 : : {
393 : 16 : struct spdk_thread *thread = arg;
394 : :
395 : 16 : spdk_thread_exit(thread);
396 : 16 : }
397 : :
398 : : static void
399 : 12 : stop_threads(void)
400 : : {
401 : : uint32_t i;
402 : :
403 [ + + ]: 28 : for (i = 0; i < SPDK_COUNTOF(g_thread); i++) {
404 [ + + ]: 24 : if (g_thread[i] == NULL) {
405 : 8 : break;
406 : : }
407 : 16 : spdk_thread_send_msg(g_thread[i], stop_thread, g_thread[i]);
408 : 16 : g_thread[i] = NULL;
409 : : }
410 : 12 : }
411 : :
412 : : static struct test g_tests[] = {
413 : : {"contend", 2, contend_setup, contend_end, 0},
414 : : {"hold_by_poller", 1, hold_by_poller_setup, hold_by_poller_end, 0},
415 : : {"hold_by_message", 1, hold_by_message_setup, hold_by_message_end, 1},
416 : : };
417 : :
418 : : static void
419 : 12 : launch_end_poller(void *arg)
420 : : {
421 : 12 : struct test *test = arg;
422 : :
423 : 12 : test->poller = SPDK_POLLER_REGISTER(test->end_fn, test, 100);
424 : 12 : }
425 : :
426 : : static void
427 : 16 : launch_next_test(void *arg)
428 : : {
429 : : struct test *test;
430 : : static uint32_t last_fail_count = 0;
431 : : static uint32_t current_test = 0;
432 : :
433 [ - + ]: 16 : assert(spdk_thread_is_app_thread(NULL));
434 : :
435 [ + + ]: 16 : if (current_test != 0) {
436 : 12 : const char *name = g_tests[current_test - 1].name;
437 [ + - ]: 12 : if (g_fail == last_fail_count) {
438 : 12 : printf("PASS test %s\n", name);
439 : : } else {
440 : 0 : printf("FAIL test %s (%u failed assertions)\n", name,
441 : : g_fail - last_fail_count);
442 : : }
443 : 12 : stop_threads();
444 : : }
445 : :
446 [ + + ]: 16 : if (current_test == SPDK_COUNTOF(g_tests)) {
447 : 4 : spdk_app_stop(g_fail);
448 : :
449 : 4 : return;
450 : : }
451 : :
452 : 12 : test = &g_tests[current_test];
453 : :
454 : 12 : printf("Starting test %s\n", test->name);
455 : 12 : start_threads(test->thread_count);
456 : :
457 [ + + ]: 12 : if (test->poller_thread_number == 0) {
458 : 8 : launch_end_poller(test);
459 : : } else {
460 : : /*
461 : : * A test may set a done flag then return, expecting the error to be generated
462 : : * when the poller or message goes off CPU. To ensure that we don't check for the
463 : : * error between the time that "done" is set and the time the error is registered,
464 : : * check for the error on the thread that runs the poller or handles the message.
465 : : */
466 : 4 : spdk_thread_send_msg(g_thread[test->poller_thread_number - 1],
467 : : launch_end_poller, test);
468 : : }
469 : :
470 : : /*
471 : : * The setup function starts after the end poller. If it's not done this way, the start
472 : : * function may trigger an error condition (thread->lock_count != 0) that would cause
473 : : * extraneous calls to spin_abort_fn() as the end poller is registered.
474 : : */
475 : 12 : test->setup_fn();
476 : :
477 : 12 : current_test++;
478 : : }
479 : :
480 : : static void
481 : 4 : start_tests(void *arg)
482 : : {
483 : 4 : g_spin_abort_fn = do_not_abort;
484 : 4 : spdk_thread_send_msg(spdk_thread_get_app_thread(), launch_next_test, NULL);
485 : 4 : }
486 : :
487 : : int
488 : 4 : main(int argc, char **argv)
489 : : {
490 : 4 : struct spdk_app_opts opts;
491 : 4 : char *me = argv[0];
492 : : int ret;
493 : 4 : char mask[8];
494 : :
495 : 4 : spdk_app_opts_init(&opts, sizeof(opts));
496 : 4 : opts.name = "spdk_lock_test";
497 [ - + ]: 4 : snprintf(mask, sizeof(mask), "0x%x", (1 << SPDK_COUNTOF(g_thread)) - 1);
498 : 4 : opts.reactor_mask = mask;
499 : :
500 : 4 : spdk_app_start(&opts, start_tests, NULL);
501 : :
502 : 4 : spdk_app_fini();
503 : :
504 [ - + ]: 4 : printf("%s summary:\n", me);
505 [ - + ]: 4 : printf(" %8u assertions passed\n", g_pass);
506 [ - + ]: 4 : printf(" %8u assertions failed\n", g_fail);
507 : :
508 [ - + ]: 4 : if (g_pass + g_fail == 0) {
509 : 0 : ret = 1;
510 : : } else {
511 : 4 : ret = spdk_min(g_fail, 127);
512 : : }
513 : 4 : return ret;
514 : : }
|