Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2020 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : :
8 : : #include "spdk_internal/cunit.h"
9 : : #include "common/lib/test_env.c"
10 : : #include "event/reactor.c"
11 : : #include "spdk/thread.h"
12 : : #include "spdk_internal/thread.h"
13 : : #include "event/scheduler_static.c"
14 : : #include "../module/scheduler/dynamic/scheduler_dynamic.c"
15 : :
16 : : static void
17 : 3 : test_create_reactor(void)
18 : : {
19 : 3 : struct spdk_reactor *reactor;
20 : : int rc;
21 : :
22 : : /* See SPDK issue #3004. Seems like a bug with gcc + asan on Fedora 38, so we can't
23 : : * allocate g_reactors on the stack and need to explicitly used aligned allocation here.
24 : : */
25 [ - + ]: 3 : rc = posix_memalign((void **)&reactor, SPDK_CACHE_LINE_SIZE, sizeof(*reactor));
26 [ - + ]: 3 : SPDK_CU_ASSERT_FATAL(rc == 0);
27 : :
28 : 3 : g_reactors = reactor;
29 : 3 : g_reactor_count = 1;
30 : :
31 : 3 : reactor_construct(reactor, 0);
32 : :
33 : 3 : CU_ASSERT(spdk_reactor_get(0) == reactor);
34 : :
35 : 3 : spdk_ring_free(reactor->events);
36 : 3 : reactor_interrupt_fini(reactor);
37 : 3 : free(reactor);
38 : 3 : g_reactors = NULL;
39 : 3 : }
40 : :
41 : : static void
42 : 3 : test_init_reactors(void)
43 : : {
44 : : uint32_t core;
45 : :
46 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
47 : :
48 : 3 : allocate_cores(3);
49 : :
50 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
51 : :
52 : 3 : CU_ASSERT(g_reactor_state == SPDK_REACTOR_STATE_INITIALIZED);
53 [ + + ]: 12 : for (core = 0; core < 3; core++) {
54 : 9 : CU_ASSERT(spdk_reactor_get(core) != NULL);
55 : : }
56 : :
57 : 3 : spdk_reactors_fini();
58 : :
59 : 3 : free_cores();
60 : :
61 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
62 : 3 : }
63 : :
64 : : static void
65 : 3 : ut_event_fn(void *arg1, void *arg2)
66 : : {
67 : 3 : uint8_t *test1 = arg1;
68 : 3 : uint8_t *test2 = arg2;
69 : :
70 : 3 : *test1 = 1;
71 : 3 : *test2 = 0xFF;
72 : 3 : }
73 : :
74 : : static void
75 : 3 : test_event_call(void)
76 : : {
77 : 3 : uint8_t test1 = 0, test2 = 0;
78 : : struct spdk_event *evt;
79 : : struct spdk_reactor *reactor;
80 : :
81 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
82 : :
83 : 3 : allocate_cores(1);
84 : :
85 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
86 : :
87 : 3 : evt = spdk_event_allocate(0, ut_event_fn, &test1, &test2);
88 : 3 : CU_ASSERT(evt != NULL);
89 : :
90 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
91 : :
92 : 3 : spdk_event_call(evt);
93 : :
94 : 3 : reactor = spdk_reactor_get(0);
95 : 3 : CU_ASSERT(reactor != NULL);
96 : :
97 : 3 : CU_ASSERT(event_queue_run_batch(reactor) == 1);
98 : 3 : CU_ASSERT(test1 == 1);
99 : 3 : CU_ASSERT(test2 == 0xFF);
100 : :
101 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
102 : :
103 : 3 : spdk_reactors_fini();
104 : :
105 : 3 : free_cores();
106 : :
107 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
108 : 3 : }
109 : :
110 : : static void
111 : 3 : test_schedule_thread(void)
112 : : {
113 : 3 : struct spdk_cpuset cpuset = {};
114 : : struct spdk_thread *thread;
115 : : struct spdk_reactor *reactor;
116 : : struct spdk_lw_thread *lw_thread;
117 : :
118 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
119 : :
120 : 3 : allocate_cores(5);
121 : :
122 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
123 : :
124 : 3 : spdk_cpuset_set_cpu(&cpuset, 3, true);
125 : 3 : g_next_core = 4;
126 : :
127 : 3 : MOCK_SET(spdk_env_get_current_core, 3);
128 : :
129 : : /* _reactor_schedule_thread() will be called in spdk_thread_create()
130 : : * at its end because it is passed to SPDK thread library by
131 : : * spdk_thread_lib_init().
132 : : */
133 : 3 : thread = spdk_thread_create(NULL, &cpuset);
134 : 3 : CU_ASSERT(thread != NULL);
135 : :
136 : 3 : reactor = spdk_reactor_get(3);
137 : 3 : CU_ASSERT(reactor != NULL);
138 : :
139 : 3 : CU_ASSERT(event_queue_run_batch(reactor) == 1);
140 : :
141 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
142 : :
143 : 3 : lw_thread = TAILQ_FIRST(&reactor->threads);
144 : 3 : CU_ASSERT(lw_thread != NULL);
145 : 3 : CU_ASSERT(spdk_thread_get_from_ctx(lw_thread) == thread);
146 : :
147 [ - + ]: 3 : TAILQ_REMOVE(&reactor->threads, lw_thread, link);
148 : 3 : reactor->thread_count--;
149 : 3 : spdk_set_thread(thread);
150 : 3 : spdk_thread_exit(thread);
151 [ + + ]: 6 : while (!spdk_thread_is_exited(thread)) {
152 : 3 : spdk_thread_poll(thread, 0, 0);
153 : : }
154 : 3 : spdk_thread_destroy(thread);
155 : 3 : spdk_set_thread(NULL);
156 : :
157 : 3 : spdk_reactors_fini();
158 : :
159 : 3 : free_cores();
160 : 3 : }
161 : :
162 : : static void
163 : 3 : test_reschedule_thread(void)
164 : : {
165 : 3 : struct spdk_cpuset cpuset = {};
166 : : struct spdk_thread *thread;
167 : : struct spdk_reactor *reactor;
168 : : struct spdk_lw_thread *lw_thread;
169 : :
170 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
171 : :
172 : 3 : allocate_cores(3);
173 : :
174 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
175 : :
176 : 3 : spdk_cpuset_set_cpu(&g_reactor_core_mask, 0, true);
177 : 3 : spdk_cpuset_set_cpu(&g_reactor_core_mask, 1, true);
178 : 3 : spdk_cpuset_set_cpu(&g_reactor_core_mask, 2, true);
179 : 3 : g_next_core = 0;
180 : :
181 : 3 : MOCK_SET(spdk_env_get_current_core, 1);
182 : : /* Create and schedule the thread to core 1. */
183 : 3 : spdk_cpuset_set_cpu(&cpuset, 1, true);
184 : :
185 : 3 : thread = spdk_thread_create(NULL, &cpuset);
186 : 3 : CU_ASSERT(thread != NULL);
187 : 3 : lw_thread = spdk_thread_get_ctx(thread);
188 : :
189 : 3 : reactor = spdk_reactor_get(1);
190 : 3 : CU_ASSERT(reactor != NULL);
191 : :
192 : 3 : CU_ASSERT(event_queue_run_batch(reactor) == 1);
193 : 3 : CU_ASSERT(TAILQ_FIRST(&reactor->threads) == lw_thread);
194 : :
195 : 3 : spdk_set_thread(thread);
196 : :
197 : : /* Call spdk_thread_set_cpumask() twice with different cpumask values.
198 : : * The cpumask of the 2nd call will be used in reschedule operation.
199 : : */
200 : :
201 : 3 : spdk_cpuset_zero(&cpuset);
202 : 3 : spdk_cpuset_set_cpu(&cpuset, 0, true);
203 : 3 : CU_ASSERT(spdk_thread_set_cpumask(&cpuset) == 0);
204 : :
205 : 3 : spdk_cpuset_zero(&cpuset);
206 : 3 : spdk_cpuset_set_cpu(&cpuset, 2, true);
207 : 3 : CU_ASSERT(spdk_thread_set_cpumask(&cpuset) == 0);
208 : :
209 [ - + ]: 3 : CU_ASSERT(lw_thread->resched == true);
210 : :
211 : 3 : reactor_run(reactor);
212 : :
213 [ - + ]: 3 : CU_ASSERT(lw_thread->resched == false);
214 : 3 : CU_ASSERT(TAILQ_EMPTY(&reactor->threads));
215 : :
216 : 3 : spdk_set_thread(NULL);
217 : :
218 : 3 : reactor = spdk_reactor_get(0);
219 : 3 : CU_ASSERT(reactor != NULL);
220 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
221 : :
222 : 3 : CU_ASSERT(event_queue_run_batch(reactor) == 0);
223 : :
224 : 3 : reactor = spdk_reactor_get(2);
225 : 3 : CU_ASSERT(reactor != NULL);
226 : 3 : MOCK_SET(spdk_env_get_current_core, 2);
227 : :
228 : 3 : CU_ASSERT(event_queue_run_batch(reactor) == 1);
229 : :
230 : 3 : CU_ASSERT(TAILQ_FIRST(&reactor->threads) == lw_thread);
231 : :
232 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
233 : :
234 [ - + ]: 3 : TAILQ_REMOVE(&reactor->threads, lw_thread, link);
235 : 3 : reactor->thread_count--;
236 : 3 : spdk_set_thread(thread);
237 : 3 : spdk_thread_exit(thread);
238 [ + + ]: 6 : while (!spdk_thread_is_exited(thread)) {
239 : 3 : spdk_thread_poll(thread, 0, 0);
240 : : }
241 : 3 : spdk_thread_destroy(thread);
242 : 3 : spdk_set_thread(NULL);
243 : :
244 : 3 : spdk_reactors_fini();
245 : :
246 : 3 : free_cores();
247 : 3 : }
248 : :
249 : : static void
250 : 3 : for_each_reactor_done(void *arg1, void *arg2)
251 : : {
252 : 3 : uint32_t *count = arg1;
253 : 3 : bool *done = arg2;
254 : :
255 : 3 : (*count)++;
256 : 3 : *done = true;
257 : 3 : }
258 : :
259 : : static void
260 : 15 : for_each_reactor_cb(void *arg1, void *arg2)
261 : : {
262 : 15 : uint32_t *count = arg1;
263 : :
264 : 15 : (*count)++;
265 : 15 : }
266 : :
267 : : static void
268 : 3 : test_for_each_reactor(void)
269 : : {
270 : 3 : uint32_t count = 0, i;
271 : 3 : bool done = false;
272 : : struct spdk_reactor *reactor;
273 : :
274 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
275 : :
276 : 3 : allocate_cores(5);
277 : :
278 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
279 : :
280 : 3 : spdk_for_each_reactor(for_each_reactor_cb, &count, &done, for_each_reactor_done);
281 : :
282 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
283 : :
284 : : /* We have not processed any event yet, so count and done should be 0 and false,
285 : : * respectively.
286 : : */
287 : 3 : CU_ASSERT(count == 0);
288 : :
289 : : /* Poll each reactor to verify the event is passed to each */
290 [ + + ]: 18 : for (i = 0; i < 5; i++) {
291 : 15 : reactor = spdk_reactor_get(i);
292 : 15 : CU_ASSERT(reactor != NULL);
293 : 15 : MOCK_SET(spdk_env_get_current_core, i);
294 : :
295 : 15 : event_queue_run_batch(reactor);
296 : 15 : CU_ASSERT(count == (i + 1));
297 [ - + ]: 15 : CU_ASSERT(done == false);
298 [ - - - + ]: 15 : MOCK_CLEAR(spdk_env_get_current_core);
299 : : }
300 : :
301 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
302 : : /* After each reactor is called, the completion calls it one more time. */
303 : 3 : reactor = spdk_reactor_get(0);
304 : 3 : CU_ASSERT(reactor != NULL);
305 : :
306 : 3 : event_queue_run_batch(reactor);
307 : 3 : CU_ASSERT(count == 6);
308 [ - + ]: 3 : CU_ASSERT(done == true);
309 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
310 : :
311 : 3 : spdk_reactors_fini();
312 : :
313 : 3 : free_cores();
314 : 3 : }
315 : :
316 : : static int
317 : 36 : poller_run_idle(void *ctx)
318 : : {
319 : 36 : uint64_t delay_us = (uint64_t)ctx;
320 : :
321 : 36 : spdk_delay_us(delay_us);
322 : :
323 : 36 : return SPDK_POLLER_IDLE;
324 : : }
325 : :
326 : : static int
327 : 24 : poller_run_busy(void *ctx)
328 : : {
329 : 24 : uint64_t delay_us = (uint64_t)ctx;
330 : :
331 : 24 : spdk_delay_us(delay_us);
332 : :
333 : 24 : return SPDK_POLLER_BUSY;
334 : : }
335 : :
336 : : static void
337 : 3 : test_reactor_stats(void)
338 : : {
339 : 3 : struct spdk_cpuset cpuset = {};
340 : : struct spdk_thread *thread1, *thread2;
341 : : struct spdk_reactor *reactor;
342 : 3 : struct spdk_poller *busy1, *idle1, *busy2, *idle2;
343 : 3 : struct spdk_thread_stats stats;
344 : : int rc __attribute__((unused));
345 : :
346 : : /* Test case is the following:
347 : : * Create a reactor on CPU core0.
348 : : * Create thread1 and thread2 simultaneously on reactor0 at TSC = 100.
349 : : * Reactor runs
350 : : * - thread1 for 100 with busy
351 : : * - thread2 for 200 with idle
352 : : * - thread1 for 300 with idle
353 : : * - thread2 for 400 with busy.
354 : : * Then,
355 : : * - both elapsed TSC of thread1 and thread2 should be 1100 (= 100 + 1000).
356 : : * - busy TSC of reactor should be 500 (= 100 + 400).
357 : : * - idle TSC of reactor should be 500 (= 200 + 300).
358 : : *
359 : : * After that reactor0 runs with no threads for 900 TSC.
360 : : * Create thread1 on reactor0 at TSC = 2000.
361 : : * Reactor runs
362 : : * - thread1 for 100 with busy
363 : : * Then,
364 : : * - elapsed TSC of thread1 should be 2100 (= 2000+ 100).
365 : : * - busy TSC of reactor should be 600 (= 500 + 100).
366 : : * - idle TSC of reactor should be 500 (= 500 + 900).
367 : : */
368 : :
369 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
370 : :
371 : 3 : allocate_cores(1);
372 : :
373 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
374 : :
375 : 3 : spdk_cpuset_set_cpu(&cpuset, 0, true);
376 : :
377 : 3 : reactor = spdk_reactor_get(0);
378 [ - + ]: 3 : SPDK_CU_ASSERT_FATAL(reactor != NULL);
379 : :
380 : : /* First reactor_run() sets the tsc_last. */
381 : 3 : MOCK_SET(spdk_get_ticks, 100);
382 : 3 : reactor->tsc_last = spdk_get_ticks();
383 : :
384 : 3 : thread1 = spdk_thread_create(NULL, &cpuset);
385 [ - + ]: 3 : SPDK_CU_ASSERT_FATAL(thread1 != NULL);
386 : :
387 : 3 : thread2 = spdk_thread_create(NULL, &cpuset);
388 [ - + ]: 3 : SPDK_CU_ASSERT_FATAL(thread2 != NULL);
389 : :
390 : 3 : spdk_set_thread(thread1);
391 : 3 : busy1 = spdk_poller_register(poller_run_busy, (void *)100, 0);
392 : 3 : CU_ASSERT(busy1 != NULL);
393 : :
394 : 3 : spdk_set_thread(thread2);
395 : 3 : idle2 = spdk_poller_register(poller_run_idle, (void *)300, 0);
396 : 3 : CU_ASSERT(idle2 != NULL);
397 : :
398 : 3 : spdk_set_thread(NULL);
399 : 3 : _reactor_run(reactor);
400 : :
401 : 3 : spdk_set_thread(thread1);
402 : 3 : CU_ASSERT(spdk_thread_get_last_tsc(thread1) == 200);
403 : 3 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
404 : 3 : CU_ASSERT(stats.busy_tsc == 100);
405 : 3 : CU_ASSERT(stats.idle_tsc == 0);
406 : 3 : spdk_set_thread(thread2);
407 : 3 : CU_ASSERT(spdk_thread_get_last_tsc(thread2) == 500);
408 : 3 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
409 : 3 : CU_ASSERT(stats.busy_tsc == 0);
410 : 3 : CU_ASSERT(stats.idle_tsc == 300);
411 : :
412 : 3 : CU_ASSERT(reactor->busy_tsc == 100);
413 : 3 : CU_ASSERT(reactor->idle_tsc == 300);
414 : :
415 : : /* 100 + 100 + 300 = 500 ticks elapsed */
416 : 3 : CU_ASSERT(reactor->tsc_last == 500);
417 : :
418 : 3 : spdk_set_thread(thread1);
419 : 3 : spdk_poller_unregister(&busy1);
420 : 3 : idle1 = spdk_poller_register(poller_run_idle, (void *)200, 0);
421 : 3 : CU_ASSERT(idle1 != NULL);
422 : :
423 : 3 : spdk_set_thread(thread2);
424 : 3 : spdk_poller_unregister(&idle2);
425 : 3 : busy2 = spdk_poller_register(poller_run_busy, (void *)400, 0);
426 : 3 : CU_ASSERT(busy2 != NULL);
427 : :
428 : 3 : _reactor_run(reactor);
429 : :
430 : 3 : spdk_set_thread(thread1);
431 : 3 : CU_ASSERT(spdk_thread_get_last_tsc(thread1) == 700);
432 : 3 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
433 : 3 : CU_ASSERT(stats.busy_tsc == 100);
434 : 3 : CU_ASSERT(stats.idle_tsc == 200);
435 : 3 : spdk_set_thread(thread2);
436 : 3 : CU_ASSERT(spdk_thread_get_last_tsc(thread2) == 1100);
437 : 3 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
438 : 3 : CU_ASSERT(stats.busy_tsc == 400);
439 : 3 : CU_ASSERT(stats.idle_tsc == 300);
440 : :
441 : 3 : CU_ASSERT(reactor->busy_tsc == 500);
442 : 3 : CU_ASSERT(reactor->idle_tsc == 500);
443 : :
444 : : /* 500 + 200 + 400 = 1100 ticks elapsed */
445 : 3 : CU_ASSERT(reactor->tsc_last == 1100);
446 : :
447 : 3 : spdk_set_thread(thread1);
448 : 3 : spdk_poller_unregister(&idle1);
449 : 3 : spdk_thread_exit(thread1);
450 : :
451 : 3 : spdk_set_thread(thread2);
452 : 3 : spdk_poller_unregister(&busy2);
453 : 3 : spdk_thread_exit(thread2);
454 : :
455 : 3 : _reactor_run(reactor);
456 : :
457 : : /* After 900 ticks new thread is created. */
458 : : /* 1100 + 900 = 2000 ticks elapsed */
459 : 3 : MOCK_SET(spdk_get_ticks, 2000);
460 : 3 : _reactor_run(reactor);
461 : 3 : CU_ASSERT(reactor->tsc_last == 2000);
462 : :
463 : 3 : thread1 = spdk_thread_create(NULL, &cpuset);
464 [ - + ]: 3 : SPDK_CU_ASSERT_FATAL(thread1 != NULL);
465 : :
466 : 3 : spdk_set_thread(thread1);
467 : 3 : busy1 = spdk_poller_register(poller_run_busy, (void *)100, 0);
468 : 3 : CU_ASSERT(busy1 != NULL);
469 : :
470 : 3 : spdk_set_thread(NULL);
471 : 3 : _reactor_run(reactor);
472 : :
473 : 3 : spdk_set_thread(thread1);
474 : 3 : CU_ASSERT(spdk_thread_get_last_tsc(thread1) == 2100);
475 : 3 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
476 : 3 : CU_ASSERT(stats.busy_tsc == 100);
477 : 3 : CU_ASSERT(stats.idle_tsc == 0);
478 : :
479 : 3 : CU_ASSERT(reactor->busy_tsc == 600);
480 : 3 : CU_ASSERT(reactor->idle_tsc == 1400);
481 : :
482 : : /* 2000 + 100 = 2100 ticks elapsed */
483 : 3 : CU_ASSERT(reactor->tsc_last == 2100);
484 : :
485 : 3 : spdk_set_thread(thread1);
486 : 3 : spdk_poller_unregister(&busy1);
487 : 3 : spdk_thread_exit(thread1);
488 : :
489 : 3 : _reactor_run(reactor);
490 : :
491 : 3 : CU_ASSERT(TAILQ_EMPTY(&reactor->threads));
492 : :
493 : : /* No further than 2100 ticks elapsed */
494 : 3 : CU_ASSERT(reactor->tsc_last == 2100);
495 : :
496 : 3 : spdk_reactors_fini();
497 : :
498 : 3 : free_cores();
499 : :
500 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
501 : 3 : }
502 : :
503 : : static uint32_t
504 : 18 : _run_events_till_completion(uint32_t reactor_count)
505 : : {
506 : : struct spdk_reactor *reactor;
507 : : uint32_t i, events;
508 : 18 : uint32_t total_events = 0;
509 : :
510 : : do {
511 : 51 : events = 0;
512 [ + + ]: 171 : for (i = 0; i < reactor_count; i++) {
513 : 120 : reactor = spdk_reactor_get(i);
514 : 120 : CU_ASSERT(reactor != NULL);
515 : 120 : MOCK_SET(spdk_env_get_current_core, i);
516 : 120 : events += event_queue_run_batch(reactor);
517 : :
518 : : /* Some events require scheduling core to run */
519 : 120 : MOCK_SET(spdk_env_get_current_core, g_scheduling_reactor->lcore);
520 : 120 : events += event_queue_run_batch(g_scheduling_reactor);
521 : :
522 [ - - - + ]: 120 : MOCK_CLEAR(spdk_env_get_current_core);
523 : : }
524 : 51 : total_events += events;
525 [ + + ]: 51 : } while (events > 0);
526 : :
527 : 18 : return total_events;
528 : : }
529 : :
530 : : static void
531 : 3 : test_scheduler(void)
532 : : {
533 : 3 : struct spdk_cpuset cpuset = {};
534 : 3 : struct spdk_thread *thread[3];
535 : : struct spdk_reactor *reactor;
536 : 3 : struct spdk_poller *busy, *idle;
537 : 3 : uint64_t reactor_busy_tsc[3], reactor_idle_tsc[3];
538 : 3 : uint64_t thread_busy_tsc[3], thread_idle_tsc[3];
539 : : uint64_t current_time, end_time, busy_time, idle_time;
540 : 3 : struct spdk_thread_stats stats;
541 : : int i;
542 : :
543 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
544 : :
545 : 3 : allocate_cores(3);
546 : :
547 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
548 : :
549 : 3 : spdk_scheduler_set("dynamic");
550 : :
551 [ + + ]: 12 : for (i = 0; i < 3; i++) {
552 : 9 : spdk_cpuset_set_cpu(&g_reactor_core_mask, i, true);
553 : : }
554 : 3 : g_next_core = 0;
555 : :
556 : : /* Create threads. */
557 [ + + ]: 12 : for (i = 0; i < 3; i++) {
558 : 9 : spdk_cpuset_zero(&cpuset);
559 : 9 : spdk_cpuset_set_cpu(&cpuset, i, true);
560 : 9 : thread[i] = spdk_thread_create(NULL, &cpuset);
561 : 9 : CU_ASSERT(thread[i] != NULL);
562 : 9 : thread_busy_tsc[i] = 0;
563 : 9 : thread_idle_tsc[i] = 0;
564 : : }
565 : :
566 [ + + ]: 12 : for (i = 0; i < 3; i++) {
567 : 9 : reactor = spdk_reactor_get(i);
568 : 9 : CU_ASSERT(reactor != NULL);
569 : 9 : MOCK_SET(spdk_env_get_current_core, i);
570 : 9 : event_queue_run_batch(reactor);
571 : 9 : CU_ASSERT(!TAILQ_EMPTY(&reactor->threads));
572 : 9 : reactor_busy_tsc[i] = 0;
573 : 9 : reactor_idle_tsc[i] = 0;
574 : : }
575 : :
576 : 3 : g_reactor_state = SPDK_REACTOR_STATE_RUNNING;
577 : :
578 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
579 : :
580 : : /* Init threads stats (low load) */
581 : : /* Each reactor starts at 100 tsc,
582 : : * ends at 100 + 100 = 200 tsc. */
583 : 3 : current_time = 100;
584 : 3 : idle_time = 100;
585 : 3 : busy_time = 0;
586 : 3 : end_time = current_time + idle_time + busy_time;
587 [ + + ]: 12 : for (i = 0; i < 3; i++) {
588 : 9 : spdk_set_thread(thread[i]);
589 : 9 : idle = spdk_poller_register(poller_run_idle, (void *)idle_time, 0);
590 : 9 : reactor = spdk_reactor_get(i);
591 : 9 : CU_ASSERT(reactor != NULL);
592 : 9 : MOCK_SET(spdk_get_ticks, current_time);
593 : 9 : reactor->tsc_last = spdk_get_ticks();
594 : 9 : _reactor_run(reactor);
595 : 9 : CU_ASSERT(reactor->tsc_last == end_time);
596 : 9 : spdk_poller_unregister(&idle);
597 : :
598 : 9 : CU_ASSERT(spdk_thread_get_last_tsc(thread[i]) == end_time);
599 : 9 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
600 : 9 : CU_ASSERT(stats.busy_tsc == busy_time);
601 : 9 : thread_busy_tsc[i] = stats.busy_tsc;
602 : 9 : CU_ASSERT(stats.idle_tsc == idle_time);
603 : 9 : thread_idle_tsc[i] = stats.idle_tsc;
604 : 9 : CU_ASSERT(reactor->busy_tsc == busy_time);
605 : 9 : reactor_busy_tsc[i] = reactor->busy_tsc;
606 : 9 : CU_ASSERT(reactor->idle_tsc == idle_time);
607 : 9 : reactor_idle_tsc[i] = reactor->idle_tsc;
608 : : }
609 : 3 : CU_ASSERT(spdk_get_ticks() == end_time);
610 : 3 : current_time = 200;
611 : :
612 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
613 : 3 : _reactors_scheduler_gather_metrics(NULL, NULL);
614 : :
615 : 3 : _run_events_till_completion(3);
616 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
617 : :
618 : : /* Threads were idle, so all of them should be placed on core 0.
619 : : * All reactors start and end at 200 tsc, since for this iteration
620 : : * the threads have no pollers (so they consume no idle or busy tsc).
621 : : */
622 [ + + ]: 12 : for (i = 0; i < 3; i++) {
623 : 9 : reactor = spdk_reactor_get(i);
624 : 9 : CU_ASSERT(reactor != NULL);
625 : 9 : MOCK_SET(spdk_get_ticks, current_time);
626 : 9 : _reactor_run(reactor);
627 : 9 : CU_ASSERT(reactor->tsc_last == current_time);
628 : 9 : CU_ASSERT(reactor->busy_tsc == reactor_busy_tsc[i]);
629 : 9 : CU_ASSERT(reactor->idle_tsc == reactor_idle_tsc[i]);
630 : 9 : spdk_set_thread(thread[i]);
631 : 9 : CU_ASSERT(spdk_thread_get_last_tsc(thread[i]) == current_time);
632 : 9 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
633 : 9 : CU_ASSERT(stats.busy_tsc == thread_busy_tsc[i]);
634 : 9 : CU_ASSERT(stats.idle_tsc == thread_idle_tsc[i]);
635 : : }
636 : 3 : CU_ASSERT(spdk_get_ticks() == current_time);
637 : :
638 : : /* 2 threads should be scheduled to core 0 */
639 : 3 : reactor = spdk_reactor_get(0);
640 : 3 : CU_ASSERT(reactor != NULL);
641 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
642 : 3 : spdk_set_thread(NULL);
643 : 3 : event_queue_run_batch(reactor);
644 : :
645 : 3 : reactor = spdk_reactor_get(0);
646 : 3 : CU_ASSERT(reactor != NULL);
647 : 3 : CU_ASSERT(!TAILQ_EMPTY(&reactor->threads));
648 : 3 : reactor = spdk_reactor_get(1);
649 : 3 : CU_ASSERT(reactor != NULL);
650 : 3 : CU_ASSERT(TAILQ_EMPTY(&reactor->threads));
651 : 3 : reactor = spdk_reactor_get(2);
652 : 3 : CU_ASSERT(reactor != NULL);
653 : 3 : CU_ASSERT(TAILQ_EMPTY(&reactor->threads));
654 : :
655 : : /* Make threads busy */
656 : 3 : reactor = spdk_reactor_get(0);
657 : 3 : CU_ASSERT(reactor != NULL);
658 : :
659 : : /* All threads run on single reactor,
660 : : * reactor 0 starts at 200 tsc,
661 : : * ending at 200 + (100 * 3) = 500 tsc. */
662 : 3 : MOCK_SET(spdk_get_ticks, current_time);
663 : 3 : busy_time = 100;
664 : 3 : idle_time = 0;
665 [ + + ]: 12 : for (i = 0; i < 3; i++) {
666 : 9 : spdk_set_thread(thread[i]);
667 : 9 : busy = spdk_poller_register(poller_run_busy, (void *)busy_time, 0);
668 : 9 : _reactor_run(reactor);
669 : 9 : spdk_poller_unregister(&busy);
670 : 9 : current_time += busy_time;
671 : :
672 : 9 : CU_ASSERT(reactor->tsc_last == current_time);
673 : 9 : CU_ASSERT(spdk_thread_get_last_tsc(thread[i]) == current_time);
674 : 9 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
675 : 9 : CU_ASSERT(stats.busy_tsc == thread_busy_tsc[i] + busy_time);
676 : 9 : CU_ASSERT(stats.idle_tsc == thread_idle_tsc[i] + idle_time);;
677 : : }
678 : 3 : CU_ASSERT(reactor->busy_tsc == reactor_busy_tsc[0] + 3 * busy_time);
679 : 3 : CU_ASSERT(reactor->idle_tsc == reactor_idle_tsc[0] + 3 * idle_time);
680 : 3 : CU_ASSERT(spdk_get_ticks() == current_time);
681 : :
682 : : /* Run scheduler again, this time all threads are busy */
683 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
684 : 3 : _reactors_scheduler_gather_metrics(NULL, NULL);
685 : :
686 : 3 : _run_events_till_completion(3);
687 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
688 : :
689 : : /* Threads were busy, 2 will stay on core 0, 1 will move to core 1 */
690 [ + + ]: 12 : for (i = 0; i < 3; i++) {
691 : 9 : MOCK_SET(spdk_env_get_current_core, i);
692 : 9 : reactor = spdk_reactor_get(i);
693 : 9 : CU_ASSERT(reactor != NULL);
694 : 9 : _reactor_run(reactor);
695 : : }
696 : :
697 [ + + ]: 12 : for (i = 0; i < 3; i++) {
698 : 9 : reactor = spdk_reactor_get(i);
699 : 9 : CU_ASSERT(reactor != NULL);
700 : 9 : CU_ASSERT(!TAILQ_EMPTY(&reactor->threads));
701 : : }
702 : :
703 : 3 : g_reactor_state = SPDK_REACTOR_STATE_INITIALIZED;
704 : :
705 : : /* Destroy threads */
706 [ + + ]: 12 : for (i = 0; i < 3; i++) {
707 : 9 : spdk_set_thread(thread[i]);
708 : 9 : spdk_thread_exit(thread[i]);
709 : : }
710 [ + + ]: 12 : for (i = 0; i < 3; i++) {
711 : 9 : reactor = spdk_reactor_get(i);
712 : 9 : CU_ASSERT(reactor != NULL);
713 : 9 : reactor_run(reactor);
714 : : }
715 : :
716 : 3 : spdk_set_thread(NULL);
717 : :
718 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
719 : :
720 : 3 : spdk_reactors_fini();
721 : :
722 : 3 : free_cores();
723 : 3 : }
724 : :
725 : : static void
726 : 3 : test_bind_thread(void)
727 : : {
728 : 3 : struct spdk_cpuset cpuset = {};
729 : 3 : struct spdk_thread *thread[3];
730 : : struct spdk_reactor *reactor;
731 : 3 : struct spdk_poller *idle;
732 : 3 : uint64_t reactor_busy_tsc[3], reactor_idle_tsc[3];
733 : 3 : uint64_t thread_busy_tsc[3], thread_idle_tsc[3];
734 : : uint64_t current_time, end_time, busy_time, idle_time;
735 : 3 : struct spdk_thread_stats stats;
736 : : int i;
737 : :
738 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
739 : :
740 : 3 : allocate_cores(3);
741 : :
742 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
743 : :
744 : 3 : spdk_scheduler_set("dynamic");
745 : :
746 [ + + ]: 12 : for (i = 0; i < 3; i++) {
747 : 9 : spdk_cpuset_set_cpu(&g_reactor_core_mask, i, true);
748 : : }
749 : 3 : g_next_core = 0;
750 : :
751 : : /* Create threads. */
752 [ + + ]: 12 : for (i = 0; i < 3; i++) {
753 : 9 : spdk_cpuset_zero(&cpuset);
754 : 9 : spdk_cpuset_set_cpu(&cpuset, i, true);
755 : 9 : thread[i] = spdk_thread_create(NULL, &cpuset);
756 : 9 : CU_ASSERT(thread[i] != NULL);
757 : 9 : thread_busy_tsc[i] = 0;
758 : 9 : thread_idle_tsc[i] = 0;
759 : : }
760 : :
761 [ + + ]: 12 : for (i = 0; i < 3; i++) {
762 : 9 : reactor = spdk_reactor_get(i);
763 : 9 : CU_ASSERT(reactor != NULL);
764 : 9 : MOCK_SET(spdk_env_get_current_core, i);
765 : 9 : event_queue_run_batch(reactor);
766 : 9 : CU_ASSERT(!TAILQ_EMPTY(&reactor->threads));
767 : 9 : reactor_busy_tsc[i] = 0;
768 : 9 : reactor_idle_tsc[i] = 0;
769 : : }
770 : :
771 : 3 : g_reactor_state = SPDK_REACTOR_STATE_RUNNING;
772 : :
773 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
774 : :
775 : : /* Init threads stats (low load) */
776 : : /* Each reactor starts at 100 tsc,
777 : : * ends at 100 + 100 = 200 tsc. */
778 : 3 : current_time = 100;
779 : 3 : idle_time = 100;
780 : 3 : busy_time = 0;
781 : 3 : end_time = current_time + idle_time + busy_time;
782 [ + + ]: 12 : for (i = 0; i < 3; i++) {
783 : 9 : spdk_set_thread(thread[i]);
784 : 9 : idle = spdk_poller_register(poller_run_idle, (void *)idle_time, 0);
785 : 9 : reactor = spdk_reactor_get(i);
786 : 9 : CU_ASSERT(reactor != NULL);
787 : 9 : MOCK_SET(spdk_get_ticks, current_time);
788 : 9 : reactor->tsc_last = spdk_get_ticks();
789 : 9 : _reactor_run(reactor);
790 : 9 : CU_ASSERT(reactor->tsc_last == end_time);
791 : 9 : spdk_poller_unregister(&idle);
792 : :
793 : 9 : CU_ASSERT(spdk_thread_get_last_tsc(thread[i]) == end_time);
794 : 9 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
795 : 9 : CU_ASSERT(stats.busy_tsc == busy_time);
796 : 9 : thread_busy_tsc[i] = stats.busy_tsc;
797 : 9 : CU_ASSERT(stats.idle_tsc == idle_time);
798 : 9 : thread_idle_tsc[i] = stats.idle_tsc;
799 : 9 : CU_ASSERT(reactor->busy_tsc == busy_time);
800 : 9 : reactor_busy_tsc[i] = reactor->busy_tsc;
801 : 9 : CU_ASSERT(reactor->idle_tsc == idle_time);
802 : 9 : reactor_idle_tsc[i] = reactor->idle_tsc;
803 : : }
804 : 3 : CU_ASSERT(spdk_get_ticks() == end_time);
805 : 3 : current_time = 200;
806 : : /* Bind thread 1 */
807 : 3 : spdk_thread_bind(thread[1], true);
808 : 3 : CU_ASSERT(spdk_thread_is_bound(thread[1]) == true);
809 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
810 : 3 : _reactors_scheduler_gather_metrics(NULL, NULL);
811 : 3 : _run_events_till_completion(3);
812 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
813 : :
814 : : /* Threads were idle, so all of them should be placed on core 0 except thread 1
815 : : * since it has been limited on core 1
816 : : * All reactors start and end at 200 tsc, since for this iteration
817 : : * the threads have no pollers (so they consume no idle or busy tsc).
818 : : */
819 [ + + ]: 12 : for (i = 0; i < 3; i++) {
820 : 9 : reactor = spdk_reactor_get(i);
821 : 9 : CU_ASSERT(reactor != NULL);
822 : 9 : MOCK_SET(spdk_get_ticks, current_time);
823 : 9 : _reactor_run(reactor);
824 : 9 : CU_ASSERT(reactor->tsc_last == current_time);
825 : 9 : CU_ASSERT(reactor->busy_tsc == reactor_busy_tsc[i]);
826 : 9 : CU_ASSERT(reactor->idle_tsc == reactor_idle_tsc[i]);
827 : 9 : spdk_set_thread(thread[i]);
828 : 9 : CU_ASSERT(spdk_thread_get_last_tsc(thread[i]) == current_time);
829 : 9 : CU_ASSERT(spdk_thread_get_stats(&stats) == 0);
830 : 9 : CU_ASSERT(stats.busy_tsc == thread_busy_tsc[i]);
831 : 9 : CU_ASSERT(stats.idle_tsc == thread_idle_tsc[i]);
832 : : }
833 : 3 : CU_ASSERT(spdk_get_ticks() == current_time);
834 : :
835 : 3 : spdk_set_thread(NULL);
836 : :
837 : : /* Thread on core 2 should be scheduled to core 0 */
838 : 3 : reactor = spdk_reactor_get(0);
839 : 3 : CU_ASSERT(reactor != NULL);
840 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
841 : 3 : event_queue_run_batch(reactor);
842 : :
843 : 3 : reactor = spdk_reactor_get(0);
844 : 3 : CU_ASSERT(reactor != NULL);
845 : 3 : CU_ASSERT(!TAILQ_EMPTY(&reactor->threads));
846 : : /* Thread 1 has been limited and stiil on core 0 */
847 : 3 : reactor = spdk_reactor_get(1);
848 : 3 : CU_ASSERT(reactor != NULL);
849 : 3 : CU_ASSERT(!TAILQ_EMPTY(&reactor->threads));
850 : :
851 : 3 : reactor = spdk_reactor_get(2);
852 : 3 : CU_ASSERT(reactor != NULL);
853 : 3 : CU_ASSERT(TAILQ_EMPTY(&reactor->threads));
854 : :
855 : 3 : g_reactor_state = SPDK_REACTOR_STATE_INITIALIZED;
856 : :
857 : : /* Destroy threads */
858 [ + + ]: 12 : for (i = 0; i < 3; i++) {
859 : 9 : spdk_set_thread(thread[i]);
860 : 9 : spdk_thread_exit(thread[i]);
861 : : }
862 [ + + ]: 12 : for (i = 0; i < 3; i++) {
863 : 9 : reactor = spdk_reactor_get(i);
864 : 9 : CU_ASSERT(reactor != NULL);
865 : 9 : reactor_run(reactor);
866 : : }
867 : :
868 : 3 : spdk_set_thread(NULL);
869 : :
870 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
871 : :
872 : 3 : spdk_reactors_fini();
873 : :
874 : 3 : free_cores();
875 : 3 : }
876 : :
877 : : #ifndef __FreeBSD__
878 : : uint8_t g_curr_freq;
879 : :
880 : : static int
881 : 3 : core_freq_up(uint32_t lcore)
882 : : {
883 [ + - ]: 3 : if (g_curr_freq != UINT8_MAX) {
884 : 3 : g_curr_freq++;
885 : : }
886 : :
887 : 3 : return 0;
888 : : }
889 : :
890 : : static int
891 : 3 : core_freq_down(uint32_t lcore)
892 : : {
893 [ + - ]: 3 : if (g_curr_freq != 0) {
894 : 3 : g_curr_freq--;
895 : : }
896 : :
897 : 3 : return 0;
898 : : }
899 : :
900 : : static int
901 : 6 : core_freq_max(uint32_t lcore)
902 : : {
903 : 6 : g_curr_freq = UINT8_MAX;
904 : :
905 : 6 : return 0;
906 : : }
907 : :
908 [ - + ]: 3 : DEFINE_STUB(core_freq_min, int, (uint32_t lcore_id), 0);
909 [ # # ]: 0 : DEFINE_STUB(core_caps, int,
910 : : (uint32_t lcore_id, struct spdk_governor_capabilities *capabilities), 0);
911 [ - + ]: 3 : DEFINE_STUB(governor_init, int, (void), 0);
912 : 0 : DEFINE_STUB_V(governor_deinit, (void));
913 : :
914 : : static struct spdk_governor governor = {
915 : : .name = "dpdk_governor",
916 : : .get_core_curr_freq = NULL,
917 : : .core_freq_up = core_freq_up,
918 : : .core_freq_down = core_freq_down,
919 : : .set_core_freq_max = core_freq_max,
920 : : .set_core_freq_min = core_freq_min,
921 : : .get_core_capabilities = core_caps,
922 : : .init = governor_init,
923 : : .deinit = governor_deinit,
924 : : };
925 : :
926 : : static void
927 : 3 : test_governor(void)
928 : : {
929 : 3 : struct spdk_cpuset cpuset = {};
930 : 3 : struct spdk_thread *thread[2];
931 : : struct spdk_lw_thread *lw_thread;
932 : : struct spdk_reactor *reactor;
933 : 3 : struct spdk_poller *busy, *idle;
934 : 3 : uint8_t last_freq = 100;
935 : : int i;
936 : :
937 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
938 : :
939 : 3 : g_curr_freq = last_freq;
940 : 3 : spdk_governor_register(&governor);
941 : :
942 : 3 : allocate_cores(2);
943 : :
944 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
945 : :
946 : 3 : spdk_scheduler_set("dynamic");
947 : 3 : spdk_governor_set("dpdk_governor");
948 : :
949 [ + + ]: 9 : for (i = 0; i < 2; i++) {
950 : 6 : spdk_cpuset_set_cpu(&g_reactor_core_mask, i, true);
951 : : }
952 : :
953 : : /* Create threads. */
954 [ + + ]: 9 : for (i = 0; i < 2; i++) {
955 : 6 : spdk_cpuset_zero(&cpuset);
956 : 6 : spdk_cpuset_set_cpu(&cpuset, i, true);
957 : 6 : thread[i] = spdk_thread_create(NULL, &cpuset);
958 : 6 : CU_ASSERT(thread[i] != NULL);
959 : : }
960 : :
961 [ + + ]: 9 : for (i = 0; i < 2; i++) {
962 : 6 : reactor = spdk_reactor_get(i);
963 : 6 : CU_ASSERT(reactor != NULL);
964 : 6 : MOCK_SET(spdk_env_get_current_core, i);
965 : 6 : CU_ASSERT(event_queue_run_batch(reactor) == 1);
966 : 6 : CU_ASSERT(!TAILQ_EMPTY(&reactor->threads));
967 : : }
968 : :
969 : 3 : reactor = spdk_reactor_get(0);
970 : 3 : CU_ASSERT(reactor != NULL);
971 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
972 : :
973 : 3 : g_reactor_state = SPDK_REACTOR_STATE_RUNNING;
974 : :
975 : : /* TEST 1 */
976 : : /* Init thread stats (low load) */
977 : 3 : MOCK_SET(spdk_get_ticks, 100);
978 : 3 : reactor->tsc_last = 100;
979 : :
980 [ + + ]: 9 : for (i = 0; i < 2; i++) {
981 : 6 : spdk_set_thread(thread[i]);
982 : 6 : idle = spdk_poller_register(poller_run_idle, (void *)200, 0);
983 : 6 : reactor = spdk_reactor_get(i);
984 : 6 : CU_ASSERT(reactor != NULL);
985 : 6 : MOCK_SET(spdk_env_get_current_core, i);
986 : 6 : _reactor_run(reactor);
987 : 6 : spdk_poller_unregister(&idle);
988 : :
989 : : /* Update last stats so that we don't have to call scheduler twice */
990 : 6 : lw_thread = spdk_thread_get_ctx(thread[i]);
991 : 6 : lw_thread->current_stats.idle_tsc = 1;
992 : : }
993 : :
994 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
995 : 3 : _reactors_scheduler_gather_metrics(NULL, NULL);
996 : :
997 : 3 : CU_ASSERT(_run_events_till_completion(2) == 2);
998 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
999 : :
1000 : : /* Threads were idle, so all of them should be placed on core 0 */
1001 [ + + ]: 9 : for (i = 0; i < 2; i++) {
1002 : 6 : reactor = spdk_reactor_get(i);
1003 : 6 : CU_ASSERT(reactor != NULL);
1004 : 6 : _reactor_run(reactor);
1005 : : }
1006 : :
1007 : : /* 1 thread should be scheduled to core 0 */
1008 : 3 : reactor = spdk_reactor_get(0);
1009 : 3 : CU_ASSERT(reactor != NULL);
1010 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
1011 : 3 : CU_ASSERT(event_queue_run_batch(reactor) == 1);
1012 : :
1013 : : /* Main core should be busy less than 50% time now - frequency should be lowered */
1014 : 3 : CU_ASSERT(g_curr_freq == last_freq - 1);
1015 : :
1016 : 3 : last_freq = g_curr_freq;
1017 : :
1018 : : /* TEST 2 */
1019 : : /* Make first threads busy - both threads will be still on core 0, but frequency will have to be raised */
1020 : 3 : spdk_set_thread(thread[0]);
1021 : 3 : busy = spdk_poller_register(poller_run_busy, (void *)1000, 0);
1022 : 3 : _reactor_run(reactor);
1023 : 3 : spdk_poller_unregister(&busy);
1024 : :
1025 : 3 : spdk_set_thread(thread[1]);
1026 : 3 : idle = spdk_poller_register(poller_run_idle, (void *)100, 0);
1027 : 3 : _reactor_run(reactor);
1028 : 3 : spdk_poller_unregister(&idle);
1029 : :
1030 : : /* Run scheduler again */
1031 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
1032 : 3 : _reactors_scheduler_gather_metrics(NULL, NULL);
1033 : :
1034 : 3 : i = _run_events_till_completion(2);
1035 : : /* Six runs when interrupt mode is supported, two if not. */
1036 [ - + - - ]: 3 : CU_ASSERT(i == 7 || i == 3);
1037 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
1038 : :
1039 : : /* Main core should be busy more than 50% time now - frequency should be raised */
1040 : 3 : CU_ASSERT(g_curr_freq == last_freq + 1);
1041 : :
1042 : : /* TEST 3 */
1043 : : /* Make second thread very busy so that it will be moved to second core */
1044 : 3 : spdk_set_thread(thread[1]);
1045 : 3 : busy = spdk_poller_register(poller_run_busy, (void *)2000, 0);
1046 : 3 : _reactor_run(reactor);
1047 : 3 : spdk_poller_unregister(&busy);
1048 : :
1049 : : /* Update first thread stats */
1050 : 3 : spdk_set_thread(thread[0]);
1051 : 3 : idle = spdk_poller_register(poller_run_idle, (void *)100, 0);
1052 : 3 : _reactor_run(reactor);
1053 : 3 : spdk_poller_unregister(&idle);
1054 : :
1055 : : /* Run scheduler again */
1056 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
1057 : 3 : _reactors_scheduler_gather_metrics(NULL, NULL);
1058 : :
1059 : 3 : i = _run_events_till_completion(2);
1060 : : /* Six runs when interrupt mode is supported, two if not. */
1061 [ - + - - ]: 3 : CU_ASSERT(i == 7 || i == 3);
1062 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
1063 : :
1064 [ + + ]: 9 : for (i = 0; i < 2; i++) {
1065 : 6 : reactor = spdk_reactor_get(i);
1066 : 6 : CU_ASSERT(reactor != NULL);
1067 : 6 : _reactor_run(reactor);
1068 : : }
1069 : :
1070 : : /* Main core frequency should be set to max when we have busy threads on other cores */
1071 : 3 : CU_ASSERT(g_curr_freq == UINT8_MAX);
1072 : :
1073 : 3 : g_reactor_state = SPDK_REACTOR_STATE_INITIALIZED;
1074 : :
1075 : : /* Destroy threads */
1076 [ + + ]: 9 : for (i = 0; i < 2; i++) {
1077 : 6 : spdk_set_thread(thread[i]);
1078 : 6 : spdk_thread_exit(thread[i]);
1079 : : }
1080 [ + + ]: 9 : for (i = 0; i < 2; i++) {
1081 : 6 : reactor = spdk_reactor_get(i);
1082 : 6 : CU_ASSERT(reactor != NULL);
1083 : 6 : reactor_run(reactor);
1084 : : }
1085 : :
1086 : 3 : spdk_set_thread(NULL);
1087 : :
1088 [ - - - + ]: 3 : MOCK_CLEAR(spdk_env_get_current_core);
1089 : :
1090 : 3 : spdk_reactors_fini();
1091 : :
1092 : 3 : free_cores();
1093 : 3 : }
1094 : : #endif
1095 : :
1096 : : static void
1097 : 3 : test_scheduler_set_isolated_core_mask(void)
1098 : : {
1099 : 3 : struct spdk_cpuset isolated_core_mask = {};
1100 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
1101 : :
1102 : 3 : allocate_cores(3);
1103 : :
1104 : 3 : CU_ASSERT(spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE) == 0);
1105 : :
1106 : 3 : spdk_cpuset_set_cpu(&g_reactor_core_mask, 0, true);
1107 : 3 : spdk_cpuset_set_cpu(&g_reactor_core_mask, 1, true);
1108 : 3 : spdk_cpuset_set_cpu(&g_reactor_core_mask, 2, true);
1109 : :
1110 : 3 : spdk_cpuset_set_cpu(&isolated_core_mask, 1, true);
1111 : 3 : spdk_cpuset_set_cpu(&isolated_core_mask, 2, true);
1112 : 3 : CU_ASSERT(scheduler_set_isolated_core_mask(isolated_core_mask) == true);
1113 : :
1114 : 3 : spdk_cpuset_zero(&isolated_core_mask);
1115 : :
1116 : 3 : spdk_cpuset_set_cpu(&isolated_core_mask, 4, true);
1117 : 3 : CU_ASSERT(scheduler_set_isolated_core_mask(isolated_core_mask) == false);
1118 : :
1119 : 3 : spdk_cpuset_zero(&isolated_core_mask);
1120 : :
1121 : 3 : spdk_cpuset_set_cpu(&isolated_core_mask, 0, true);
1122 : 3 : spdk_cpuset_set_cpu(&isolated_core_mask, 1, true);
1123 : 3 : spdk_cpuset_set_cpu(&isolated_core_mask, 4, true);
1124 : 3 : CU_ASSERT(scheduler_set_isolated_core_mask(isolated_core_mask) == false);
1125 : :
1126 : 3 : spdk_reactors_fini();
1127 : 3 : free_cores();
1128 : 3 : }
1129 : :
1130 : : struct workload {
1131 : : struct spdk_thread *thread;
1132 : : uint64_t scheduling_period;
1133 : :
1134 : : uint64_t busy_tsc_per_scheduling_period;
1135 : :
1136 : : uint64_t polling_period_busy_tsc;
1137 : : uint64_t polling_period_idle_tsc;
1138 : : };
1139 : :
1140 : : static int
1141 : 7380 : poller_run_mixed_workload(void *ctx)
1142 : : {
1143 : 7380 : struct workload *workload = (struct workload *)ctx;
1144 [ - + ]: 7380 : uint64_t curr_period_busy_tsc = spdk_thread_get_last_tsc(workload->thread) %
1145 : 7380 : workload->scheduling_period;
1146 : :
1147 [ + + ]: 7380 : if (curr_period_busy_tsc < workload->busy_tsc_per_scheduling_period) {
1148 : 180 : spdk_delay_us(workload->polling_period_busy_tsc);
1149 : 180 : return SPDK_POLLER_BUSY;
1150 : : }
1151 : 7200 : spdk_delay_us(workload->polling_period_idle_tsc);
1152 : 7200 : return SPDK_POLLER_IDLE;
1153 : : }
1154 : :
1155 : : /**
1156 : : * Test that poller_run_mixed_workload correctly mocks real poller behavior by mocking
1157 : : * a mostly idle thread.
1158 : : * The scheduling period is set to 1000 tsc and a total of 200 busy tsc will be
1159 : : * consumed during the scheduling period. Then 800 tsc should be idle.
1160 : : *
1161 : : * Assert that the poller returns busy a few times (20) and that it returns idle
1162 : : * many times (800) because it takes very little time to return when there is no
1163 : : * work to do and longer to return when there is work to do.
1164 : : *
1165 : : */
1166 : : static void
1167 : 3 : test_mixed_workload(void)
1168 : : {
1169 : 3 : struct workload workload = {
1170 : : .thread = NULL,
1171 : : .scheduling_period = 1000,
1172 : : .busy_tsc_per_scheduling_period = 200,
1173 : : .polling_period_busy_tsc = 10,
1174 : : .polling_period_idle_tsc = 1
1175 : : };
1176 : 3 : struct spdk_poller *poller;
1177 : : struct spdk_thread *thread;
1178 : : struct spdk_reactor *reactor;
1179 : : uint64_t i, busy_count, idle_count, rc;
1180 : :
1181 : 3 : allocate_cores(1);
1182 : 3 : spdk_cpuset_set_cpu(&g_reactor_core_mask, 0, true);
1183 : :
1184 : 3 : MOCK_SET(spdk_env_get_current_core, 0);
1185 : 3 : MOCK_SET(spdk_get_ticks, 0);
1186 : 3 : spdk_reactors_init(SPDK_DEFAULT_MSG_MEMPOOL_SIZE);
1187 : 3 : reactor = spdk_reactor_get(0);
1188 : :
1189 : 3 : thread = spdk_thread_create(NULL, &g_reactor_core_mask);
1190 : 3 : workload.thread = thread;
1191 : :
1192 : 3 : _reactor_run(reactor);
1193 : :
1194 : 3 : spdk_set_thread(thread);
1195 : 3 : poller = spdk_poller_register(poller_run_mixed_workload, (void *)&workload, 0);
1196 : :
1197 : 3 : busy_count = 0;
1198 : 3 : idle_count = 0;
1199 : : /* Simulate 3 scheduling periods */
1200 [ + + ]: 12 : for (i = 1; i <= 3; i++) {
1201 [ + + ]: 7389 : while (spdk_thread_get_last_tsc(thread) < i * workload.scheduling_period) {
1202 : 7380 : rc = spdk_thread_poll(thread, 0, spdk_thread_get_last_tsc(thread));
1203 : :
1204 [ + + ]: 7380 : if (rc == SPDK_POLLER_BUSY) {
1205 : 180 : busy_count++;
1206 : : } else {
1207 : 7200 : idle_count++;
1208 : : }
1209 : : }
1210 [ - + ]: 9 : CU_ASSERT(busy_count == workload.busy_tsc_per_scheduling_period / workload.polling_period_busy_tsc);
1211 [ - + ]: 9 : CU_ASSERT(idle_count == (workload.scheduling_period - workload.busy_tsc_per_scheduling_period) /
1212 : : workload.polling_period_idle_tsc);
1213 : 9 : CU_ASSERT(spdk_thread_get_last_tsc(thread) == i * workload.scheduling_period);
1214 : 9 : busy_count = 0;
1215 : 9 : idle_count = 0;
1216 : : }
1217 : :
1218 : 3 : spdk_poller_unregister(&poller);
1219 : 3 : spdk_thread_exit(thread);
1220 : 3 : _reactor_run(reactor);
1221 : 3 : spdk_thread_destroy(thread);
1222 : 3 : spdk_set_thread(NULL);
1223 : 3 : spdk_reactors_fini();
1224 : 3 : free_cores();
1225 : 3 : }
1226 : :
1227 : : int
1228 : 3 : main(int argc, char **argv)
1229 : : {
1230 : 3 : CU_pSuite suite = NULL;
1231 : : unsigned int num_failures;
1232 : :
1233 : 3 : CU_initialize_registry();
1234 : :
1235 : 3 : suite = CU_add_suite("app_suite", NULL, NULL);
1236 : :
1237 : 3 : CU_ADD_TEST(suite, test_create_reactor);
1238 : 3 : CU_ADD_TEST(suite, test_init_reactors);
1239 : 3 : CU_ADD_TEST(suite, test_event_call);
1240 : 3 : CU_ADD_TEST(suite, test_schedule_thread);
1241 : 3 : CU_ADD_TEST(suite, test_reschedule_thread);
1242 : 3 : CU_ADD_TEST(suite, test_bind_thread);
1243 : 3 : CU_ADD_TEST(suite, test_for_each_reactor);
1244 : 3 : CU_ADD_TEST(suite, test_reactor_stats);
1245 : 3 : CU_ADD_TEST(suite, test_scheduler);
1246 : : #ifndef __FreeBSD__
1247 : : /* governor is only supported on Linux, so don't run this specific unit test on FreeBSD */
1248 : 3 : CU_ADD_TEST(suite, test_governor);
1249 : : #endif
1250 : 3 : CU_ADD_TEST(suite, test_scheduler_set_isolated_core_mask);
1251 : 3 : CU_ADD_TEST(suite, test_mixed_workload);
1252 : :
1253 : 3 : num_failures = spdk_ut_run_tests(argc, argv, NULL);
1254 : 3 : CU_cleanup_registry();
1255 : :
1256 : 3 : return num_failures;
1257 : : }
|