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 #include "common/lib/test_env.c" 38 #include "event/reactor.c" 39 #include "spdk_internal/thread.h" 40 #include "event/scheduler_static.c" 41 42 static void 43 test_create_reactor(void) 44 { 45 struct spdk_reactor reactor = {}; 46 47 g_reactors = &reactor; 48 49 reactor_construct(&reactor, 0); 50 51 CU_ASSERT(spdk_reactor_get(0) == &reactor); 52 53 spdk_ring_free(reactor.events); 54 g_reactors = NULL; 55 } 56 57 static void 58 test_init_reactors(void) 59 { 60 uint32_t core; 61 62 allocate_cores(3); 63 64 CU_ASSERT(spdk_reactors_init() == 0); 65 66 CU_ASSERT(g_reactor_state == SPDK_REACTOR_STATE_INITIALIZED); 67 for (core = 0; core < 3; core++) { 68 CU_ASSERT(spdk_reactor_get(core) != NULL); 69 } 70 71 spdk_reactors_fini(); 72 73 free_cores(); 74 } 75 76 static void 77 ut_event_fn(void *arg1, void *arg2) 78 { 79 uint8_t *test1 = arg1; 80 uint8_t *test2 = arg2; 81 82 *test1 = 1; 83 *test2 = 0xFF; 84 } 85 86 static void 87 test_event_call(void) 88 { 89 uint8_t test1 = 0, test2 = 0; 90 struct spdk_event *evt; 91 struct spdk_reactor *reactor; 92 93 allocate_cores(1); 94 95 CU_ASSERT(spdk_reactors_init() == 0); 96 97 evt = spdk_event_allocate(0, ut_event_fn, &test1, &test2); 98 CU_ASSERT(evt != NULL); 99 100 spdk_event_call(evt); 101 102 reactor = spdk_reactor_get(0); 103 CU_ASSERT(reactor != NULL); 104 105 CU_ASSERT(event_queue_run_batch(reactor) == 1); 106 CU_ASSERT(test1 == 1); 107 CU_ASSERT(test2 == 0xFF); 108 109 spdk_reactors_fini(); 110 111 free_cores(); 112 } 113 114 static void 115 test_schedule_thread(void) 116 { 117 struct spdk_cpuset cpuset = {}; 118 struct spdk_thread *thread; 119 struct spdk_reactor *reactor; 120 struct spdk_lw_thread *lw_thread; 121 122 allocate_cores(5); 123 124 CU_ASSERT(spdk_reactors_init() == 0); 125 126 spdk_cpuset_set_cpu(&cpuset, 3, true); 127 g_next_core = 4; 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 thread = spdk_thread_create(NULL, &cpuset); 134 CU_ASSERT(thread != NULL); 135 136 reactor = spdk_reactor_get(3); 137 CU_ASSERT(reactor != NULL); 138 139 MOCK_SET(spdk_env_get_current_core, 3); 140 141 CU_ASSERT(event_queue_run_batch(reactor) == 1); 142 143 MOCK_CLEAR(spdk_env_get_current_core); 144 145 lw_thread = TAILQ_FIRST(&reactor->threads); 146 CU_ASSERT(lw_thread != NULL); 147 CU_ASSERT(spdk_thread_get_from_ctx(lw_thread) == thread); 148 149 TAILQ_REMOVE(&reactor->threads, lw_thread, link); 150 reactor->thread_count--; 151 spdk_set_thread(thread); 152 spdk_thread_exit(thread); 153 while (!spdk_thread_is_exited(thread)) { 154 spdk_thread_poll(thread, 0, 0); 155 } 156 spdk_thread_destroy(thread); 157 spdk_set_thread(NULL); 158 159 spdk_reactors_fini(); 160 161 free_cores(); 162 } 163 164 static void 165 test_reschedule_thread(void) 166 { 167 struct spdk_cpuset cpuset = {}; 168 struct spdk_thread *thread; 169 struct spdk_reactor *reactor; 170 struct spdk_lw_thread *lw_thread; 171 172 allocate_cores(3); 173 174 CU_ASSERT(spdk_reactors_init() == 0); 175 176 spdk_cpuset_set_cpu(&g_reactor_core_mask, 0, true); 177 spdk_cpuset_set_cpu(&g_reactor_core_mask, 1, true); 178 spdk_cpuset_set_cpu(&g_reactor_core_mask, 2, true); 179 g_next_core = 0; 180 181 /* Create and schedule the thread to core 1. */ 182 spdk_cpuset_set_cpu(&cpuset, 1, true); 183 184 thread = spdk_thread_create(NULL, &cpuset); 185 CU_ASSERT(thread != NULL); 186 lw_thread = spdk_thread_get_ctx(thread); 187 188 reactor = spdk_reactor_get(1); 189 CU_ASSERT(reactor != NULL); 190 MOCK_SET(spdk_env_get_current_core, 1); 191 192 CU_ASSERT(event_queue_run_batch(reactor) == 1); 193 CU_ASSERT(TAILQ_FIRST(&reactor->threads) == lw_thread); 194 195 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 spdk_cpuset_zero(&cpuset); 202 spdk_cpuset_set_cpu(&cpuset, 0, true); 203 CU_ASSERT(spdk_thread_set_cpumask(&cpuset) == 0); 204 205 spdk_cpuset_zero(&cpuset); 206 spdk_cpuset_set_cpu(&cpuset, 2, true); 207 CU_ASSERT(spdk_thread_set_cpumask(&cpuset) == 0); 208 209 CU_ASSERT(lw_thread->resched == true); 210 211 reactor_run(reactor); 212 213 CU_ASSERT(lw_thread->resched == false); 214 CU_ASSERT(TAILQ_EMPTY(&reactor->threads)); 215 216 reactor = spdk_reactor_get(0); 217 CU_ASSERT(reactor != NULL); 218 MOCK_SET(spdk_env_get_current_core, 0); 219 220 CU_ASSERT(event_queue_run_batch(reactor) == 0); 221 222 reactor = spdk_reactor_get(2); 223 CU_ASSERT(reactor != NULL); 224 MOCK_SET(spdk_env_get_current_core, 2); 225 226 CU_ASSERT(event_queue_run_batch(reactor) == 1); 227 228 CU_ASSERT(TAILQ_FIRST(&reactor->threads) == lw_thread); 229 230 MOCK_CLEAR(spdk_env_get_current_core); 231 232 TAILQ_REMOVE(&reactor->threads, lw_thread, link); 233 reactor->thread_count--; 234 spdk_set_thread(thread); 235 spdk_thread_exit(thread); 236 while (!spdk_thread_is_exited(thread)) { 237 spdk_thread_poll(thread, 0, 0); 238 } 239 spdk_thread_destroy(thread); 240 spdk_set_thread(NULL); 241 242 spdk_reactors_fini(); 243 244 free_cores(); 245 } 246 247 static void 248 for_each_reactor_done(void *arg1, void *arg2) 249 { 250 uint32_t *count = arg1; 251 bool *done = arg2; 252 253 (*count)++; 254 *done = true; 255 } 256 257 static void 258 for_each_reactor_cb(void *arg1, void *arg2) 259 { 260 uint32_t *count = arg1; 261 262 (*count)++; 263 } 264 265 static void 266 test_for_each_reactor(void) 267 { 268 uint32_t count = 0, i; 269 bool done = false; 270 struct spdk_reactor *reactor; 271 272 allocate_cores(5); 273 274 CU_ASSERT(spdk_reactors_init() == 0); 275 276 MOCK_SET(spdk_env_get_current_core, 0); 277 278 spdk_for_each_reactor(for_each_reactor_cb, &count, &done, for_each_reactor_done); 279 280 MOCK_CLEAR(spdk_env_get_current_core); 281 282 /* We have not processed any event yet, so count and done should be 0 and false, 283 * respectively. 284 */ 285 CU_ASSERT(count == 0); 286 287 /* Poll each reactor to verify the event is passed to each */ 288 for (i = 0; i < 5; i++) { 289 reactor = spdk_reactor_get(i); 290 CU_ASSERT(reactor != NULL); 291 292 event_queue_run_batch(reactor); 293 CU_ASSERT(count == (i + 1)); 294 CU_ASSERT(done == false); 295 } 296 297 /* After each reactor is called, the completion calls it one more time. */ 298 reactor = spdk_reactor_get(0); 299 CU_ASSERT(reactor != NULL); 300 301 event_queue_run_batch(reactor); 302 CU_ASSERT(count == 6); 303 CU_ASSERT(done == true); 304 305 spdk_reactors_fini(); 306 307 free_cores(); 308 } 309 310 static int 311 poller_run_idle(void *ctx) 312 { 313 uint64_t delay_us = (uint64_t)ctx; 314 315 spdk_delay_us(delay_us); 316 317 return 0; 318 } 319 320 static int 321 poller_run_busy(void *ctx) 322 { 323 uint64_t delay_us = (uint64_t)ctx; 324 325 spdk_delay_us(delay_us); 326 327 return 1; 328 } 329 330 static void 331 test_reactor_stats(void) 332 { 333 struct spdk_cpuset cpuset = {}; 334 struct spdk_thread *thread1, *thread2; 335 struct spdk_reactor *reactor; 336 struct spdk_poller *busy1, *idle1, *busy2, *idle2; 337 int rc __attribute__((unused)); 338 339 /* Test case is the following: 340 * Create a reactor on CPU core0. 341 * Create thread1 and thread2 simultaneously on reactor0 at TSC = 100. 342 * Reactor runs 343 * - thread1 for 100 with busy 344 * - thread2 for 200 with idle 345 * - thread1 for 300 with idle 346 * - thread2 for 400 with busy. 347 * Then, 348 * - both elapsed TSC of thread1 and thread2 should be 1000 (= 100 + 900). 349 * - busy TSC of reactor should be 500 (= 100 + 400). 350 * - idle TSC of reactor should be 500 (= 200 + 300). 351 */ 352 353 allocate_cores(1); 354 355 CU_ASSERT(spdk_reactors_init() == 0); 356 357 spdk_cpuset_set_cpu(&cpuset, 0, true); 358 359 MOCK_SET(spdk_env_get_current_core, 0); 360 MOCK_SET(spdk_get_ticks, 100); 361 362 thread1 = spdk_thread_create(NULL, &cpuset); 363 SPDK_CU_ASSERT_FATAL(thread1 != NULL); 364 365 thread2 = spdk_thread_create(NULL, &cpuset); 366 SPDK_CU_ASSERT_FATAL(thread2 != NULL); 367 368 reactor = spdk_reactor_get(0); 369 SPDK_CU_ASSERT_FATAL(reactor != NULL); 370 371 reactor->tsc_last = 100; 372 373 spdk_set_thread(thread1); 374 busy1 = spdk_poller_register(poller_run_busy, (void *)100, 0); 375 CU_ASSERT(busy1 != NULL); 376 377 spdk_set_thread(thread2); 378 idle2 = spdk_poller_register(poller_run_idle, (void *)300, 0); 379 CU_ASSERT(idle2 != NULL); 380 381 _reactor_run(reactor); 382 383 CU_ASSERT(thread1->tsc_last == 200); 384 CU_ASSERT(thread1->stats.busy_tsc == 100); 385 CU_ASSERT(thread1->stats.idle_tsc == 0); 386 CU_ASSERT(thread2->tsc_last == 500); 387 CU_ASSERT(thread2->stats.busy_tsc == 0); 388 CU_ASSERT(thread2->stats.idle_tsc == 300); 389 390 CU_ASSERT(reactor->busy_tsc == 100); 391 CU_ASSERT(reactor->idle_tsc == 300); 392 393 spdk_set_thread(thread1); 394 spdk_poller_unregister(&busy1); 395 idle1 = spdk_poller_register(poller_run_idle, (void *)200, 0); 396 CU_ASSERT(idle1 != NULL); 397 398 spdk_set_thread(thread2); 399 spdk_poller_unregister(&idle2); 400 busy2 = spdk_poller_register(poller_run_busy, (void *)400, 0); 401 CU_ASSERT(busy2 != NULL); 402 403 _reactor_run(reactor); 404 405 CU_ASSERT(thread1->tsc_last == 700); 406 CU_ASSERT(thread1->stats.busy_tsc == 100); 407 CU_ASSERT(thread1->stats.idle_tsc == 200); 408 CU_ASSERT(thread2->tsc_last == 1100); 409 CU_ASSERT(thread2->stats.busy_tsc == 400); 410 CU_ASSERT(thread2->stats.idle_tsc == 300); 411 412 CU_ASSERT(reactor->busy_tsc == 500); 413 CU_ASSERT(reactor->idle_tsc == 500); 414 415 spdk_set_thread(thread1); 416 spdk_poller_unregister(&idle1); 417 spdk_thread_exit(thread1); 418 419 spdk_set_thread(thread2); 420 spdk_poller_unregister(&busy2); 421 spdk_thread_exit(thread2); 422 423 _reactor_run(reactor); 424 425 CU_ASSERT(TAILQ_EMPTY(&reactor->threads)); 426 427 spdk_reactors_fini(); 428 429 free_cores(); 430 } 431 432 int 433 main(int argc, char **argv) 434 { 435 CU_pSuite suite = NULL; 436 unsigned int num_failures; 437 438 CU_set_error_action(CUEA_ABORT); 439 CU_initialize_registry(); 440 441 suite = CU_add_suite("app_suite", NULL, NULL); 442 443 CU_ADD_TEST(suite, test_create_reactor); 444 CU_ADD_TEST(suite, test_init_reactors); 445 CU_ADD_TEST(suite, test_event_call); 446 CU_ADD_TEST(suite, test_schedule_thread); 447 CU_ADD_TEST(suite, test_reschedule_thread); 448 CU_ADD_TEST(suite, test_for_each_reactor); 449 CU_ADD_TEST(suite, test_reactor_stats); 450 451 CU_basic_set_mode(CU_BRM_VERBOSE); 452 CU_basic_run_tests(); 453 num_failures = CU_get_number_of_failures(); 454 CU_cleanup_registry(); 455 456 return num_failures; 457 } 458