1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <autoconf.h> 14#include <sel4test-driver/gen_config.h> 15#include <stdio.h> 16#include <sel4/sel4.h> 17#include <stdlib.h> 18#include <string.h> 19#include <vka/object.h> 20 21#include "../helpers.h" 22 23#define PRIORITY_FUDGE 1 24 25#define MIN_PRIO seL4_MinPrio 26#define MAX_PRIO (OUR_PRIO - 1) 27#define NUM_PRIOS (MAX_PRIO - MIN_PRIO + 1) 28 29#define CHECK_STEP(var, x) do { \ 30 test_check((var) == x); \ 31 ZF_LOGD(#x "..."); \ 32 var = (x) + 1; \ 33 } while (0) 34 35static int counter_func(volatile seL4_Word *counter) 36{ 37 while (1) { 38 (*counter)++; 39 } 40 return 0; 41} 42 43/* 44 * Test that thread suspending works when idling the system. 45 * Note: This test non-deterministically fails. If you find only this test 46 * try re-running the test suite. 47 */ 48static int test_thread_suspend(env_t env) 49{ 50 helper_thread_t t1; 51 volatile seL4_Word counter; 52 ZF_LOGD("test_thread_suspend\n"); 53 create_helper_thread(env, &t1); 54 55 set_helper_priority(env, &t1, 100); 56 start_helper(env, &t1, (helper_fn_t) counter_func, (seL4_Word) &counter, 0, 0, 0); 57 58 sel4test_periodic_start(env, 10 * NS_IN_MS); 59 60 seL4_Word old_counter; 61 62 /* Let the counter thread run. */ 63 sel4test_ntfn_timer_wait(env); 64 65 old_counter = counter; 66 67 /* Let it run again. */ 68 sel4test_ntfn_timer_wait(env); 69 70 /* Now, counter should have moved. */ 71 test_check(counter != old_counter); 72 old_counter = counter; 73 74 /* Suspend the thread, and wait again. */ 75 seL4_TCB_Suspend(get_helper_tcb(&t1)); 76 sel4test_ntfn_timer_wait(env); 77 78 /* Counter should not have moved. */ 79 test_check(counter == old_counter); 80 old_counter = counter; 81 82 /* Check once more for good measure. */ 83 sel4test_ntfn_timer_wait(env); 84 85 /* Counter should not have moved. */ 86 test_check(counter == old_counter); 87 old_counter = counter; 88 89 /* Resume the thread and check it does move. */ 90 seL4_TCB_Resume(get_helper_tcb(&t1)); 91 sel4test_ntfn_timer_wait(env); 92 test_check(counter != old_counter); 93 94 /* Done. */ 95 cleanup_helper(env, &t1); 96 97 return sel4test_get_result(); 98} 99DEFINE_TEST(SCHED0000, "Test suspending and resuming a thread (flaky)", test_thread_suspend, 100 config_set(CONFIG_HAVE_TIMER) 101 && !config_set(CONFIG_ARCH_RISCV)) 102 103/* 104 * Test TCB Resume on self. 105 */ 106static int 107test_resume_self(struct env *env) 108{ 109 ZF_LOGD("Starting test_resume_self\n"); 110 /* Ensure nothing bad happens if we resume ourselves. */ 111 int error = seL4_TCB_Resume(env->tcb); 112 test_error_eq(error, seL4_NoError); 113 ZF_LOGD("Ending test_resume_self\n"); 114 return sel4test_get_result(); 115} 116DEFINE_TEST(SCHED0002, "Test resuming ourselves", test_resume_self, true) 117 118/* 119 * Test TCB Suspend/Resume. 120 */ 121static volatile int suspend_test_step; 122static int suspend_test_helper_2a(seL4_CPtr t1, seL4_CPtr t2a, seL4_CPtr t2b) 123{ 124 /* Helper function that runs at a higher priority. */ 125 126 /* Start here. */ 127 CHECK_STEP(suspend_test_step, 0); 128 129 /* Wait for a timer tick to make 2b run. */ 130 while (suspend_test_step == 1) 131 /* spin */{ 132 ; 133 } 134 135 /* Suspend helper 2b. */ 136 int error = seL4_TCB_Suspend(t2b); 137 test_check(!error); 138 139 CHECK_STEP(suspend_test_step, 2); 140 141 /* Now suspend ourselves, passing control to the low priority process to 142 * resume 2b. */ 143 error = seL4_TCB_Suspend(t2a); 144 test_check(!error); 145 146 return sel4test_get_result(); 147} 148 149static int suspend_test_helper_2b(seL4_CPtr t1, seL4_CPtr t2a, seL4_CPtr t2b) 150{ 151 /* Wait for 2a to get suspend_test_step set to 1. */ 152 test_check(suspend_test_step == 0 || suspend_test_step == 1); 153 while (suspend_test_step == 0) { 154 seL4_Yield(); 155 } 156 157 /* Timer tick should bring us back here. */ 158 CHECK_STEP(suspend_test_step, 1); 159 160 /* Now spin and wait for us to be suspended. */ 161 while (suspend_test_step == 2) { 162 seL4_Yield(); 163 } 164 165 /* When we wake up suspend_test_step should be 4. */ 166 CHECK_STEP(suspend_test_step, 4); 167 168 return sel4test_get_result(); 169} 170 171static int suspend_test_helper_1(seL4_CPtr t1, seL4_CPtr t2a, seL4_CPtr t2b) 172{ 173 CHECK_STEP(suspend_test_step, 3); 174 175 /* Our sole job is to wake up 2b. */ 176 int error = seL4_TCB_Resume(t2b); 177 test_check(!error); 178 179 /* We should have been preempted immediately, so by the time we run again, 180 * the suspend_test_step should be 5. */ 181 182#if 1 // WE HAVE A BROKEN SCHEDULER IN SEL4 183 /* FIXME: The seL4 scheduler is broken, and seL4_TCB_Resume will not 184 * preempt. The only way to get preempted is to force it ourselves (or wait 185 * for a timer tick). */ 186 seL4_Yield(); 187#endif 188 189 CHECK_STEP(suspend_test_step, 5); 190 191 return sel4test_get_result(); 192} 193 194static int test_suspend(struct env *env) 195{ 196 helper_thread_t thread1; 197 helper_thread_t thread2a; 198 helper_thread_t thread2b; 199 200 ZF_LOGD("Starting test_suspend\n"); 201 202 create_helper_thread(env, &thread1); 203 ZF_LOGD("Show me\n"); 204 create_helper_thread(env, &thread2a); 205 206 create_helper_thread(env, &thread2b); 207 208 /* First set all the helper threads to have unique priorities 209 * and then start them in order of priority. This is so when 210 * the 'start_helper' function does an IPC to the helper 211 * thread, it doesn't allow one of the already started helper 212 * threads to run at all */ 213 set_helper_priority(env, &thread1, 0); 214 set_helper_priority(env, &thread2a, 1); 215 set_helper_priority(env, &thread2b, 2); 216 217 start_helper(env, &thread1, (helper_fn_t) suspend_test_helper_1, 218 (seL4_Word) get_helper_tcb(&thread1), 219 (seL4_Word) get_helper_tcb(&thread2a), 220 (seL4_Word) get_helper_tcb(&thread2b), 0); 221 222 start_helper(env, &thread2a, (helper_fn_t) suspend_test_helper_2a, 223 (seL4_Word) get_helper_tcb(&thread1), 224 (seL4_Word) get_helper_tcb(&thread2a), 225 (seL4_Word) get_helper_tcb(&thread2b), 0); 226 227 start_helper(env, &thread2b, (helper_fn_t) suspend_test_helper_2b, 228 (seL4_Word) get_helper_tcb(&thread1), 229 (seL4_Word) get_helper_tcb(&thread2a), 230 (seL4_Word) get_helper_tcb(&thread2b), 0); 231 232 /* Now set their priorities to what we want */ 233 set_helper_priority(env, &thread1, 100); 234 set_helper_priority(env, &thread2a, 101); 235 set_helper_priority(env, &thread2b, 101); 236 237 suspend_test_step = 0; 238 ZF_LOGD(" "); 239 240 wait_for_helper(&thread1); 241 wait_for_helper(&thread2b); 242 243 CHECK_STEP(suspend_test_step, 6); 244 ZF_LOGD("\n"); 245 246 cleanup_helper(env, &thread1); 247 cleanup_helper(env, &thread2a); 248 cleanup_helper(env, &thread2b); 249 250 return sel4test_get_result(); 251} 252DEFINE_TEST(SCHED0003, "Test TCB suspend/resume", test_suspend, !config_set(CONFIG_FT)) 253 254/* 255 * Test threads at all possible priorities, and that they get scheduled in the 256 * correct order. 257 */ 258static int 259prio_test_func(seL4_Word my_prio, volatile seL4_Word *last_prio, seL4_CPtr ep) 260{ 261 COMPILER_MEMORY_FENCE(); 262 test_check(*last_prio - 1 == my_prio); 263 264 *last_prio = my_prio; 265 COMPILER_MEMORY_FENCE(); 266 267 /* Unsuspend the top thread if we are the last one. */ 268 if (my_prio == MIN_PRIO) { 269 seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0); 270 seL4_Send(ep, tag); 271 } 272 return 0; 273} 274 275static int test_all_priorities(struct env *env) 276{ 277 int i; 278 279 helper_thread_t **threads = (helper_thread_t **) malloc(sizeof(helper_thread_t *) * NUM_PRIOS); 280 assert(threads != NULL); 281 282 for (i = 0; i < NUM_PRIOS; i++) { 283 threads[i] = (helper_thread_t *) malloc(sizeof(helper_thread_t)); 284 assert(threads[i]); 285 } 286 287 vka_t *vka = &env->vka; 288 ZF_LOGD("Testing all thread priorities"); 289 volatile seL4_Word last_prio = MAX_PRIO + 1; 290 291 seL4_CPtr ep = vka_alloc_endpoint_leaky(vka); 292 293 for (int prio = MIN_PRIO; prio <= MAX_PRIO; prio++) { 294 int idx = prio - MIN_PRIO; 295 test_check(idx >= 0 && idx < NUM_PRIOS); 296 create_helper_thread(env, threads[idx]); 297 set_helper_priority(env, threads[idx], prio); 298 299 start_helper(env, threads[idx], (helper_fn_t) prio_test_func, 300 prio, (seL4_Word) &last_prio, ep, 0); 301 } 302 303 /* Now block. */ 304 seL4_Word sender_badge = 0; 305 seL4_Wait(ep, &sender_badge); 306 307 /* When we get woken up, last_prio should be MIN_PRIO. */ 308 test_check(last_prio == MIN_PRIO); 309 310 for (int prio = MIN_PRIO; prio <= MAX_PRIO; prio++) { 311 int idx = prio - MIN_PRIO; 312 cleanup_helper(env, threads[idx]); 313 free(threads[idx]); 314 } 315 free(threads); 316 317 return sel4test_get_result(); 318} 319DEFINE_TEST(SCHED0004, "Test threads at all priorities", test_all_priorities, true) 320 321#define SCHED0005_HIGHEST_PRIO (seL4_MaxPrio - 2) 322/* 323 * Test setting the priority of a runnable thread. 324 */ 325static volatile int set_priority_step; 326static int set_priority_helper_1(seL4_CPtr t1, seL4_CPtr t2) 327{ 328 test_check(set_priority_step == 0); 329 ZF_LOGD("0..."); 330 set_priority_step = 1; 331 332 /* 333 * Down our priority. This should force a reschedule and make thread 2 run. 334 */ 335 int error = seL4_TCB_SetPriority(t1, t1, SCHED0005_HIGHEST_PRIO - 4); 336 test_check(!error); 337 338 test_check(set_priority_step == 2); 339 ZF_LOGD("2..."); 340 set_priority_step = 3; 341 342 /* set our priority back up - this should work as we did not down our max priority */ 343 error = seL4_TCB_SetPriority(t1, t1, SCHED0005_HIGHEST_PRIO); 344 test_check(error == seL4_NoError); 345 346 /* now down our max_priority */ 347 error = seL4_TCB_SetMCPriority(t1, t1, SCHED0005_HIGHEST_PRIO - 4); 348 test_check(error == seL4_NoError); 349 350 /* try to set our prio higher than our max prio, but lower than our prio */ 351 error = seL4_TCB_SetPriority(t1, t1, SCHED0005_HIGHEST_PRIO - 3); 352 test_check(error == seL4_RangeError); 353 354 /* try to set our max prio back up */ 355 error = seL4_TCB_SetMCPriority(t1, t1, SCHED0005_HIGHEST_PRIO); 356 test_check(error == seL4_RangeError); 357 358 return sel4test_get_result(); 359} 360 361static int set_priority_helper_2(seL4_CPtr t1, seL4_CPtr t2) 362{ 363 test_check(set_priority_step == 1); 364 ZF_LOGD("1..."); 365 366 /* Raise thread 1 to equal to ours, which should fail. */ 367 int error = seL4_TCB_SetPriority(t1, t2, SCHED0005_HIGHEST_PRIO - 1 + PRIORITY_FUDGE); 368 test_check(error == seL4_RangeError); 369 370 /* Raise thread 1 to just below us. */ 371 error = seL4_TCB_SetPriority(t1, t2, SCHED0005_HIGHEST_PRIO - 2); 372 test_check(!error); 373 374 /* Drop ours to below thread 1. Thread 1 should run. */ 375 set_priority_step = 2; 376 error = seL4_TCB_SetPriority(t2, t2, SCHED0005_HIGHEST_PRIO - 3); 377 test_check(!error); 378 379 /* Once thread 1 exits, we should run. */ 380 test_check(set_priority_step == 3); 381 ZF_LOGD("3..."); 382 set_priority_step = 4; 383 384 return sel4test_get_result(); 385} 386 387#if CONFIG_NUM_PRIORITIES >= 7 388/* The enclosed test relies on the current thread being able to create two 389 * threads of unequal priority, both less than the caller's own priority. For 390 * this we need at least 3 priority levels, assuming that the current thread is 391 * running at the highest priority. 392 */ 393static int test_set_priority(struct env *env) 394{ 395 helper_thread_t thread1; 396 helper_thread_t thread2; 397 ZF_LOGD("test_set_priority starting\n"); 398 create_helper_thread(env, &thread1); 399 set_helper_priority(env, &thread1, SCHED0005_HIGHEST_PRIO); 400 set_helper_mcp(env, &thread1, SCHED0005_HIGHEST_PRIO); 401 402 create_helper_thread(env, &thread2); 403 /* thread2 needs to start at a lower prio than thread1, so that when thread1 sets 404 * its own prio down, this thread runs, but not before. */ 405 set_helper_priority(env, &thread2, SCHED0005_HIGHEST_PRIO - 1); 406 /* thread2 needs mcp SCHED0005_HIGHEST_PRIO - 2 so that it can raise thread1's 407 * priority to that value */ 408 set_helper_mcp(env, &thread2, SCHED0005_HIGHEST_PRIO - 2); 409 410 set_priority_step = 0; 411 ZF_LOGD(" "); 412 413 start_helper(env, &thread1, (helper_fn_t) set_priority_helper_1, 414 (seL4_Word) get_helper_tcb(&thread1), 415 (seL4_Word) get_helper_tcb(&thread2), 0, 0); 416 417 start_helper(env, &thread2, (helper_fn_t) set_priority_helper_2, 418 (seL4_Word) get_helper_tcb(&thread1), 419 (seL4_Word) get_helper_tcb(&thread2), 0, 0); 420 421 wait_for_helper(&thread1); 422 wait_for_helper(&thread2); 423 424 test_check(set_priority_step == 4); 425 426 ZF_LOGD("\n"); 427 cleanup_helper(env, &thread1); 428 cleanup_helper(env, &thread2); 429 return sel4test_get_result(); 430} 431DEFINE_TEST(SCHED0005, "Test set priority", test_set_priority, true) 432#endif 433 434/* 435 * Perform IPC Send operations across priorities and ensure that strict 436 * priority-based scheduling is still observed. 437 */ 438static volatile int ipc_test_step; 439typedef struct ipc_test_data { 440 volatile seL4_CPtr ep0, ep1, ep2, ep3, reply; 441 volatile seL4_Word bounces; 442 volatile seL4_Word spins; 443 seL4_CPtr tcb0, tcb1, tcb2, tcb3; 444} ipc_test_data_t; 445 446static int ipc_test_helper_0(ipc_test_data_t *data) 447{ 448 /* We are a "bouncer" thread. Each time a high priority process actually 449 * wants to wait for a low priority process to execute and block, it does a 450 * call to us. We are the lowest priority process and therefore will run 451 * only after all other higher priority threads are done. 452 */ 453 while (1) { 454 seL4_MessageInfo_t tag; 455 seL4_Word sender_badge = 0; 456 tag = api_recv(data->ep0, &sender_badge, data->reply); 457 data->bounces++; 458 api_reply(data->reply, tag); 459 } 460 461 return sel4test_get_result(); 462} 463 464static int ipc_test_helper_1(ipc_test_data_t *data) 465{ 466 seL4_Word sender_badge = 0; 467 seL4_MessageInfo_t tag; 468 469 /* TEST PART 1 */ 470 /* Receive a pending send. */ 471 CHECK_STEP(ipc_test_step, 1); 472 tag = api_recv(data->ep1, &sender_badge, data->reply); 473 474 /* As soon as the wait is performed, we should be preempted. */ 475 476 /* Thread 3 will give us a chance to check our message. */ 477 CHECK_STEP(ipc_test_step, 3); 478 test_check(seL4_MessageInfo_get_length(tag) == 20); 479 for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) { 480 test_check(seL4_GetMR(i) == i); 481 } 482 483 /* Now we bounce to allow thread 3 control again. */ 484 seL4_MessageInfo_ptr_set_length(&tag, 0); 485 seL4_Call(data->ep0, tag); 486 487 /* TEST PART 2 */ 488 /* Receive a send that is not yet pending. */ 489 CHECK_STEP(ipc_test_step, 5); 490 tag = api_recv(data->ep1, &sender_badge, data->reply); 491 492 CHECK_STEP(ipc_test_step, 8); 493 test_check(seL4_MessageInfo_get_length(tag) == 19); 494 for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) { 495 test_check(seL4_GetMR(i) == i); 496 } 497 498 return sel4test_get_result(); 499} 500 501static int ipc_test_helper_2(ipc_test_data_t *data) 502{ 503 /* We are a spinner thread. Our job is to do spin, and occasionally bounce 504 * to thread 0 to let other code execute. */ 505 while (1) { 506 /* Ensure nothing happens whilst we are busy. */ 507 int last_step = ipc_test_step; 508 for (int i = 0; i < 100000; i++) { 509 asm volatile(""); 510 } 511 test_check(last_step == ipc_test_step); 512 513 data->spins++; 514 515 /* Bounce. */ 516 seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0); 517 seL4_Call(data->ep0, tag); 518 } 519 return sel4test_get_result(); 520} 521 522static int ipc_test_helper_3(ipc_test_data_t *data) 523{ 524 seL4_MessageInfo_t tag; 525 int last_spins, last_bounces; 526 527 /* This test starts here. */ 528 529 /* TEST PART 1 */ 530 /* Perform a send to a thread 1. It is not yet waiting. */ 531 CHECK_STEP(ipc_test_step, 0); 532 seL4_MessageInfo_ptr_new(&tag, 0, 0, 0, 20); 533 for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) { 534 seL4_SetMR(i, i); 535 } 536 last_spins = data->spins; 537 last_bounces = data->bounces; 538 seL4_Send(data->ep1, tag); 539 /* We block, causing thread 2 to spin for a while, before it calls the 540 * bouncer thread 0, which finally lets thread 1 run and reply to us. */ 541 CHECK_STEP(ipc_test_step, 2); 542 test_check(data->spins - last_spins == 1); 543 test_check(data->bounces - last_bounces == 0); 544 545 /* Now bounce ourselves, to ensure that thread 1 can check its stuff. */ 546 seL4_MessageInfo_ptr_set_length(&tag, 0); 547 seL4_Call(data->ep0, tag); 548 549 /* Two bounces - us and thread 1. */ 550 test_check(data->spins - last_spins == 2); 551 test_check(data->bounces - last_bounces == 2); 552 CHECK_STEP(ipc_test_step, 4); 553 554 /* TEST PART 2 */ 555 /* Perform a send to a thread 1, which is already waiting. */ 556 /* Bounce first to let thread prepare. */ 557 last_spins = data->spins; 558 last_bounces = data->bounces; 559 560 seL4_MessageInfo_ptr_set_length(&tag, 0); 561 seL4_Call(data->ep0, tag); 562 CHECK_STEP(ipc_test_step, 6); 563 564 /* Do the send. */ 565 seL4_MessageInfo_ptr_set_length(&tag, 19); 566 for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) { 567 seL4_SetMR(i, i); 568 } 569 seL4_Send(data->ep1, tag); 570 571 CHECK_STEP(ipc_test_step, 7); 572 573 /* Bounce to let thread 1 check again. */ 574 seL4_MessageInfo_ptr_set_length(&tag, 0); 575 seL4_Call(data->ep0, tag); 576 577 CHECK_STEP(ipc_test_step, 9); 578 579 /* Five bounces in total. */ 580 test_check(data->spins - last_spins == 2); 581 test_check(data->bounces - last_bounces == 5); 582 583 return sel4test_get_result(); 584} 585 586static int test_ipc_prios(struct env *env) 587{ 588 vka_t *vka = &env->vka; 589 helper_thread_t thread0; 590 helper_thread_t thread1; 591 helper_thread_t thread2; 592 helper_thread_t thread3; 593 594 ipc_test_data_t data; 595 memset(&data, 0, sizeof(data)); 596 597 data.ep0 = vka_alloc_endpoint_leaky(vka); 598 data.ep1 = vka_alloc_endpoint_leaky(vka); 599 data.ep2 = vka_alloc_endpoint_leaky(vka); 600 data.ep3 = vka_alloc_endpoint_leaky(vka); 601 602 create_helper_thread(env, &thread0); 603 set_helper_priority(env, &thread0, 0); 604 605 create_helper_thread(env, &thread1); 606 set_helper_priority(env, &thread1, 1); 607 608 create_helper_thread(env, &thread2); 609 set_helper_priority(env, &thread2, 2); 610 611 create_helper_thread(env, &thread3); 612 set_helper_priority(env, &thread3, 3); 613 set_helper_mcp(env, &thread3, 3); 614 615 data.tcb0 = get_helper_tcb(&thread0); 616 data.tcb1 = get_helper_tcb(&thread1); 617 data.tcb2 = get_helper_tcb(&thread2); 618 data.tcb3 = get_helper_tcb(&thread3); 619 data.reply = get_helper_reply(&thread0); 620 621 ZF_LOGD(" "); 622 ipc_test_step = 0; 623 624 start_helper(env, &thread0, (helper_fn_t) ipc_test_helper_0, (seL4_Word) &data, 0, 0, 0); 625 start_helper(env, &thread1, (helper_fn_t) ipc_test_helper_1, (seL4_Word) &data, 0, 0, 0); 626 start_helper(env, &thread2, (helper_fn_t) ipc_test_helper_2, (seL4_Word) &data, 0, 0, 0); 627 start_helper(env, &thread3, (helper_fn_t) ipc_test_helper_3, (seL4_Word) &data, 0, 0, 0); 628 629 wait_for_helper(&thread1); 630 wait_for_helper(&thread3); 631 632 CHECK_STEP(ipc_test_step, 10); 633 ZF_LOGD("\n"); 634 635 cleanup_helper(env, &thread0); 636 cleanup_helper(env, &thread1); 637 cleanup_helper(env, &thread2); 638 cleanup_helper(env, &thread3); 639 640 return sel4test_get_result(); 641} 642/* this test does not work on the RT kernel as it relies on FIFO IPC */ 643DEFINE_TEST(SCHED0006, "Test IPC priorities for Send", test_ipc_prios, !config_set(CONFIG_KERNEL_MCS)) 644 645#define SCHED0007_NUM_CLIENTS 5 646#define SCHED0007_PRIO(x) ((seL4_Word)(seL4_MaxPrio - 1 - SCHED0007_NUM_CLIENTS + (x))) 647 648static void 649sched0007_client(seL4_CPtr endpoint, int order) 650{ 651 seL4_SetMR(0, order); 652 seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 1); 653 654 ZF_LOGD("Client %d call", order); 655 info = seL4_Call(endpoint, info); 656} 657 658static int sched0007_server(seL4_CPtr endpoint, seL4_CPtr reply) 659{ 660 seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0); 661 662 api_recv(endpoint, NULL, reply); 663 664 for (int i = SCHED0007_NUM_CLIENTS - 1; i >= 0; i--) { 665 test_eq(SCHED0007_PRIO(i), seL4_GetMR(0)); 666 if (i > 0) { 667 api_reply_recv(endpoint, info, NULL, reply); 668 } 669 } 670 671 return SUCCESS; 672} 673 674static inline void sched0007_start_client(env_t env, helper_thread_t clients[], seL4_CPtr endpoint, int i) 675{ 676 start_helper(env, &clients[i], (helper_fn_t) sched0007_client, endpoint, SCHED0007_PRIO(i), 0, 0); 677} 678 679int test_ipc_ordered(env_t env) 680{ 681 seL4_CPtr endpoint; 682 helper_thread_t server; 683 helper_thread_t clients[SCHED0007_NUM_CLIENTS]; 684 685 endpoint = vka_alloc_endpoint_leaky(&env->vka); 686 test_assert(endpoint != 0); 687 688 /* create clients, smallest prio first */ 689 for (int i = 0; i < SCHED0007_NUM_CLIENTS; i++) { 690 create_helper_thread(env, &clients[i]); 691 692 set_helper_priority(env, &clients[i], SCHED0007_PRIO(i)); 693 } 694 695 /* create the server */ 696 create_helper_thread(env, &server); 697 set_helper_priority(env, &server, seL4_MaxPrio - 1); 698 699 compile_time_assert(sched0007_clients_correct, SCHED0007_NUM_CLIENTS == 5); 700 701 /* start the clients out of order to queue on the endpoint in order */ 702 sched0007_start_client(env, clients, endpoint, 2); 703 sched0007_start_client(env, clients, endpoint, 0); 704 sched0007_start_client(env, clients, endpoint, 4); 705 sched0007_start_client(env, clients, endpoint, 1); 706 sched0007_start_client(env, clients, endpoint, 3); 707 708 /* start the server */ 709 start_helper(env, &server, (helper_fn_t) sched0007_server, endpoint, clients[0].thread.reply.cptr, 0, 0); 710 711 /* server returns success if all requests are processed in order */ 712 return wait_for_helper(&server); 713} 714DEFINE_TEST(SCHED0007, "Test IPC priorities", test_ipc_ordered, config_set(CONFIG_KERNEL_MCS)); 715 716#define SCHED0008_NUM_CLIENTS 5 717 718static NORETURN void sched0008_client(int id, seL4_CPtr endpoint) 719{ 720 while (1) { 721 ZF_LOGD("Client call %d\n", id); 722 seL4_Call(endpoint, seL4_MessageInfo_new(0, 0, 0, 0)); 723 } 724} 725 726static inline int check_receive_ordered(env_t env, seL4_CPtr endpoint, int pos, seL4_CPtr replies[]) 727{ 728 seL4_Word badge; 729 seL4_Word expected_badge = SCHED0008_NUM_CLIENTS - 1; 730 731 /* check we receive messages in expected order */ 732 for (int i = 0; i < SCHED0008_NUM_CLIENTS; i++) { 733 ZF_LOGD("Server wait\n"); 734 api_recv(endpoint, &badge, replies[i]); 735 736 if (pos == i) { 737 ZF_LOGD("Server expecting %d\n", 0); 738 /* client 0 should be in here */ 739 test_eq(badge, (seL4_Word)0); 740 } else { 741 ZF_LOGD("Server expecting %d\n", expected_badge); 742 test_eq(expected_badge, badge); 743 expected_badge--; 744 } 745 } 746 747 /* now reply to all callers */ 748 for (int i = 0; i < SCHED0008_NUM_CLIENTS; i++) { 749 seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0); 750 seL4_Send(replies[i], info); 751 } 752 753 /* let everyone queue up again */ 754 sel4test_sleep(env, 1 * NS_IN_S); 755 return sel4test_get_result(); 756} 757 758int test_change_prio_on_endpoint(env_t env) 759{ 760 int error; 761 helper_thread_t clients[SCHED0008_NUM_CLIENTS]; 762 seL4_CPtr replies[SCHED0008_NUM_CLIENTS]; 763 seL4_CPtr endpoint; 764 seL4_CPtr badged_endpoints[SCHED0008_NUM_CLIENTS]; 765 766 endpoint = vka_alloc_endpoint_leaky(&env->vka); 767 768 int highest = seL4_MaxPrio - 1; 769 int lowest = highest - SCHED0008_NUM_CLIENTS - 2; 770 int prio = highest - SCHED0008_NUM_CLIENTS - 1; 771 int middle = highest - 3; 772 773 assert(highest > lowest && highest > middle && middle > lowest); 774 775 /* set up all the clients */ 776 for (int i = 0; i < SCHED0008_NUM_CLIENTS; i++) { 777 create_helper_thread(env, &clients[i]); 778 set_helper_priority(env, &clients[i], prio); 779 badged_endpoints[i] = get_free_slot(env); 780 error = cnode_mint(env, endpoint, badged_endpoints[i], seL4_AllRights, i); 781 test_eq(error, seL4_NoError); 782 replies[i] = clients[i].thread.reply.cptr; 783 ZF_LOGD("Client %d, prio %d\n", i, prio); 784 prio++; 785 } 786 787 seL4_Word badge; 788 seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0); 789 790 /* first test that changing prio while on an endpoint works */ 791 seL4_CPtr reply = vka_alloc_reply_leaky(&env->vka); 792 test_neq(reply, (seL4_Word)seL4_CapNull); 793 794 /* start one clients so it queue on the endpoint */ 795 start_helper(env, &clients[0], (helper_fn_t) sched0008_client, 0, badged_endpoints[0], 0, 0); 796 /* change its prio down */ 797 set_helper_priority(env, &clients[0], lowest); 798 /* wait for a message */ 799 api_recv(endpoint, &badge, reply); 800 test_eq(badge, (seL4_Word)0); 801 802 /* now send another message */ 803 seL4_Send(reply, info); 804 /* change its prio up */ 805 set_helper_priority(env, &clients[0], lowest + 1); 806 /* get another message */ 807 api_recv(endpoint, &badge, reply); 808 test_eq(badge, (seL4_Word)0); 809 seL4_Send(reply, info); 810 811 /* Now test moving client 0 into all possible places in the endpoint queue */ 812 /* first start the rest */ 813 for (int i = 1; i < SCHED0008_NUM_CLIENTS; i++) { 814 start_helper(env, &clients[i], (helper_fn_t) sched0008_client, i, badged_endpoints[i], 0, 0); 815 } 816 817 /* let everyone queue on endpoint */ 818 sel4test_sleep(env, 1 * US_IN_S); 819 820 ZF_LOGD("lower -> lowest"); 821 ZF_LOGD("Client 0, prio %d\n", lowest); 822 /* move client 0's prio from lower -> lowest*/ 823 set_helper_priority(env, &clients[0], lowest); 824 check_receive_ordered(env, endpoint, 4, replies); 825 826 ZF_LOGD("higher -> middle"); 827 ZF_LOGD("Client 0, prio %d\n", middle); 828 /* higher -> to middle */ 829 set_helper_priority(env, &clients[0], middle); 830 check_receive_ordered(env, endpoint, 2, replies); 831 832 ZF_LOGD("higher -> highest"); 833 ZF_LOGD("Client 0, prio %d\n", highest - 1); 834 /* higher -> to highest */ 835 set_helper_priority(env, &clients[0], highest - 1); 836 check_receive_ordered(env, endpoint, 0, replies); 837 838 ZF_LOGD("higher -> highest"); 839 ZF_LOGD("Client 0, prio %d\n", highest); 840 /* highest -> even higher */ 841 set_helper_priority(env, &clients[0], highest); 842 check_receive_ordered(env, endpoint, 0, replies); 843 844 ZF_LOGD("lower -> highest"); 845 ZF_LOGD("Client 0, prio %d\n", highest - 1); 846 /* lower -> highest */ 847 set_helper_priority(env, &clients[0], highest - 1); 848 check_receive_ordered(env, endpoint, 0, replies); 849 850 ZF_LOGD("lower -> middle"); 851 ZF_LOGD("Client 0, prio %d\n", middle); 852 /* lower -> middle */ 853 set_helper_priority(env, &clients[0], middle); 854 check_receive_ordered(env, endpoint, 2, replies); 855 856 ZF_LOGD("lower -> lowest"); 857 ZF_LOGD("Client 0, prio %d\n", lowest); 858 /* lower -> lowest */ 859 set_helper_priority(env, &clients[0], lowest); 860 check_receive_ordered(env, endpoint, 4, replies); 861 862 return sel4test_get_result(); 863} 864DEFINE_TEST(SCHED0008, "Test changing prio while in endpoint queues results in correct message order", 865 test_change_prio_on_endpoint, config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)) 866 867#define SCHED0009_SERVERS 5 868 869static NORETURN void 870sched0009_server(seL4_CPtr endpoint, int id, seL4_CPtr reply) 871{ 872 /* wait to start */ 873 ZF_LOGD("Server %d: awake", id); 874 seL4_Word badge; 875 seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 1); 876 api_recv(endpoint, &badge, reply); 877 878 while (1) { 879 ZF_LOGD("Server %d: ReplyRecv", id); 880 seL4_SetMR(0, id); 881 api_reply_recv(endpoint, info, &badge, reply); 882 } 883} 884 885static int test_ordered_ipc_fastpath(env_t env) 886{ 887 helper_thread_t threads[SCHED0009_SERVERS]; 888 seL4_CPtr endpoint = vka_alloc_endpoint_leaky(&env->vka); 889 890 /* set up servers */ 891 for (int i = 0; i < SCHED0009_SERVERS; i++) { 892 int prio = seL4_MaxPrio - 1 - SCHED0009_SERVERS + i; 893 ZF_LOGD("Server %d, prio %d\n", i, prio); 894 create_helper_thread(env, &threads[i]); 895 set_helper_priority(env, &threads[i], prio); 896 } 897 898 /* start the first server */ 899 start_helper(env, &threads[0], (helper_fn_t) sched0009_server, endpoint, 0, 900 get_helper_reply(&threads[0]), 0); 901 902 seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 0); 903 ZF_LOGD("Client Call\n"); 904 seL4_Call(endpoint, info); 905 test_eq(seL4_GetMR(0), (seL4_Word)0); 906 907 /* resume all other servers */ 908 for (int i = 1; i < SCHED0009_SERVERS; i++) { 909 start_helper(env, &threads[i], (helper_fn_t) sched0009_server, endpoint, i, 910 get_helper_reply(&threads[i]), 0); 911 /* sleep and allow it to run */ 912 sel4test_sleep(env, 1 * NS_IN_S); 913 /* since we resume a higher prio server each time this should work */ 914 seL4_Call(endpoint, info); 915 test_eq(seL4_GetMR(0), (seL4_Word)i); 916 } 917 918 /* At this point all servers are queued on the endpoint */ 919 /* now we will call and the highest prio server should reply each time */ 920 for (int i = 0; i < SCHED0009_SERVERS; i++) { 921 seL4_Call(endpoint, info); 922 test_eq(seL4_GetMR(0), (seL4_Word)(SCHED0009_SERVERS - 1)); 923 } 924 925 /* suspend each server in reverse prio order, should get next message from lower prio server */ 926 for (int i = SCHED0009_SERVERS - 1; i >= 0; i--) { 927 seL4_TCB_Suspend(threads[i].thread.tcb.cptr); 928 /* don't call on the endpoint once all servers are suspended */ 929 if (i > 0) { 930 seL4_Call(endpoint, info); 931 test_eq(seL4_GetMR(0), (seL4_Word)(i - 1)); 932 } 933 } 934 935 return sel4test_get_result(); 936} 937DEFINE_TEST(SCHED0009, "Test ordered ipc on reply wait fastpath", test_ordered_ipc_fastpath, 938 config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)) 939 940int 941sched0010_fn(volatile int *state) 942{ 943 state++; 944 return 0; 945} 946 947int test_resume_empty_or_no_sched_context(env_t env) 948{ 949 /* resuming a thread with empty or no scheduling context should work (it puts the thread in a runnable state) 950 * but the thread cannot run until it receives a scheduling context */ 951 952 sel4utils_thread_t thread; 953 seL4_Word data = api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits); 954 sel4utils_thread_config_t config = thread_config_default(&env->simple, env->cspace_root, 955 data, 0, OUR_PRIO - 1); 956 957 int error = sel4utils_configure_thread_config(&env->vka, &env->vspace, &env->vspace, 958 config, &thread); 959 assert(error == 0); 960 961 error = api_sc_unbind(thread.sched_context.cptr); 962 test_eq(error, 0); 963 964 volatile int state = 0; 965 error = sel4utils_start_thread(&thread, (void *) sched0010_fn, (void *) &state, NULL, 1); 966 test_eq(error, seL4_NoError); 967 968 error = seL4_TCB_Resume(thread.tcb.cptr); 969 test_eq(error, seL4_NoError); 970 971 /* let the thread 'run' */ 972 sel4test_sleep(env, 10 * NS_IN_MS); 973 test_eq(state, 0); 974 975 /* nuke the sc */ 976 error = cnode_delete(env, thread.sched_context.cptr); 977 test_eq(error, seL4_NoError); 978 979 /* resume it */ 980 error = seL4_TCB_Resume(thread.tcb.cptr); 981 test_eq(error, seL4_NoError); 982 983 /* let the thread 'run' */ 984 sel4test_sleep(env, 10 * NS_IN_MS); 985 test_eq(state, 0); 986 987 return sel4test_get_result(); 988} 989DEFINE_TEST(SCHED0010, "Test resuming a thread with empty or missing scheduling context", 990 test_resume_empty_or_no_sched_context, 991 (config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER))) 992 993void 994sched0011_helper(void) 995{ 996 while (1); 997} 998 999int test_scheduler_accuracy(env_t env) 1000{ 1001 /* 1002 * Start a thread with a 1s timeslice at our priority, and make sure it 1003 * runs for that long 1004 */ 1005 helper_thread_t helper; 1006 1007 create_helper_thread(env, &helper); 1008 uint64_t period = 100 * US_IN_MS; 1009 set_helper_sched_params(env, &helper, period, period, 0); 1010 start_helper(env, &helper, (helper_fn_t) sched0011_helper, 0, 0, 0, 0); 1011 set_helper_priority(env, &helper, OUR_PRIO); 1012 seL4_Yield(); 1013 for (int i = 0; i < 11; i++) { 1014 uint64_t start = sel4test_timestamp(env); 1015 seL4_Yield(); 1016 uint64_t end = sel4test_timestamp(env); 1017 /* calculate diff in ns */ 1018 uint64_t diff = (end - start); 1019 uint64_t period_ns = period * NS_IN_US; 1020 if (i > 0) { 1021 test_geq(diff, period_ns - 2 * NS_IN_MS); 1022 test_leq(diff, period_ns + 2 * NS_IN_MS); 1023 if (diff > period_ns) { 1024 ZF_LOGD("Too late: by %llu us", diff - period_ns); 1025 } else if (diff < period_ns) { 1026 ZF_LOGD("Too soon: by %llu us", period_ns - diff); 1027 } 1028 } 1029 } 1030 1031 return sel4test_get_result(); 1032} 1033DEFINE_TEST(SCHED0011, "Test scheduler accuracy", 1034 test_scheduler_accuracy, config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)) 1035 1036/* used by sched0012, 0013, 0014 */ 1037static void 1038periodic_thread(int id, volatile unsigned long *counters) 1039{ 1040 counters[id] = 0; 1041 1042 while (1) { 1043 counters[id]++; 1044 test_leq(counters[id], (unsigned long) 10000); 1045 printf("Tick\n"); 1046 seL4_Yield(); 1047 } 1048} 1049 1050int test_one_periodic_thread(env_t env) 1051{ 1052 helper_thread_t helper; 1053 volatile unsigned long counter; 1054 int error; 1055 1056 /* set priority down so we can run the helper(s) at a higher prio */ 1057 error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority - 1); 1058 test_eq(error, seL4_NoError); 1059 1060 create_helper_thread(env, &helper); 1061 set_helper_priority(env, &helper, env->priority); 1062 error = set_helper_sched_params(env, &helper, 0.2 * US_IN_S, US_IN_S, 0); 1063 test_eq(error, seL4_NoError); 1064 1065 start_helper(env, &helper, (helper_fn_t) periodic_thread, 0, (seL4_Word) &counter, 0, 0); 1066 1067 while (counter < 10) { 1068 printf("Tock %ld\n", counter); 1069 sel4test_sleep(env, NS_IN_S); 1070 } 1071 1072 return sel4test_get_result(); 1073} 1074DEFINE_TEST(SCHED0012, "Test one periodic thread", test_one_periodic_thread, 1075 config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)) 1076 1077int 1078test_two_periodic_threads(env_t env) 1079{ 1080 const int num_threads = 2; 1081 helper_thread_t helpers[num_threads]; 1082 volatile unsigned long counters[num_threads]; 1083 1084 /* set priority down so we can run the helper(s) at a higher prio */ 1085 int error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority - 1); 1086 test_eq(error, seL4_NoError); 1087 1088 for (int i = 0; i < num_threads; i++) { 1089 create_helper_thread(env, &helpers[i]); 1090 set_helper_priority(env, &helpers[i], env->priority); 1091 } 1092 1093 set_helper_sched_params(env, &helpers[0], 0.1 * US_IN_S, 2 * US_IN_S, 0); 1094 set_helper_sched_params(env, &helpers[1], 0.1 * US_IN_S, 3 * US_IN_S, 0); 1095 1096 for (int i = 0; i < num_threads; i++) { 1097 start_helper(env, &helpers[i], (helper_fn_t) periodic_thread, i, (seL4_Word) counters, 0, 0); 1098 } 1099 1100 while (counters[0] < 3 && counters[1] < 3) { 1101 sel4test_sleep(env, NS_IN_S); 1102 } 1103 1104 return sel4test_get_result(); 1105} 1106DEFINE_TEST(SCHED0013, "Test two periodic threads", test_two_periodic_threads, 1107 config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)); 1108 1109int test_ordering_periodic_threads(env_t env) 1110{ 1111 /* 1112 * Set up 3 periodic threads with different budgets. 1113 * All 3 threads increment global counters, 1114 * check their increments are inline with their budgets. 1115 */ 1116 1117 const int num_threads = 3; 1118 helper_thread_t helpers[num_threads]; 1119 volatile unsigned long counters[num_threads]; 1120 1121 /* set priority down so we can run the helper(s) at a higher prio */ 1122 int error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority - 1); 1123 test_eq(error, seL4_NoError); 1124 1125 /* sleep for a bit first - collect any waiting timer irqs */ 1126 sel4test_sleep(env, 50 * NS_IN_MS); 1127 1128 for (int i = 0; i < num_threads; i++) { 1129 create_helper_thread(env, &helpers[i]); 1130 set_helper_priority(env, &helpers[i], env->priority); 1131 } 1132 1133 set_helper_sched_params(env, &helpers[0], 10 * US_IN_MS, 100 * US_IN_MS, 0); 1134 set_helper_sched_params(env, &helpers[1], 10 * US_IN_MS, 200 * US_IN_MS, 0); 1135 set_helper_sched_params(env, &helpers[2], 10 * US_IN_MS, 800 * US_IN_MS, 0); 1136 1137 for (int i = 0; i < num_threads; i++) { 1138 start_helper(env, &helpers[i], (helper_fn_t) periodic_thread, i, (seL4_Word) counters, 0, 0); 1139 } 1140 1141 /* stop once 2 reaches 11 increments */ 1142 const unsigned long limit = 11u; 1143 while (counters[2] < limit) { 1144 sel4test_sleep(env, NS_IN_S); 1145 } 1146 1147 ZF_LOGD("O: %lu\n1: %lu\n2: %lu\n", counters[0], counters[1], counters[2]); 1148 1149 /* zero should have run 8 times as much as 2 */ 1150 test_geq(counters[0], (limit - 1) * 8); 1151 /* one should have run 4 times as much as 2 */ 1152 test_geq(counters[1], (limit - 1) * 4); 1153 1154 return sel4test_get_result(); 1155} 1156DEFINE_TEST(SCHED0014, "Test periodic thread ordering", test_ordering_periodic_threads, 1157 config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)) 1158 1159static void 1160sched0015_helper(int id, env_t env, volatile unsigned long long *counters) 1161{ 1162 counters[id] = 0; 1163 1164 uint64_t prev = 0; 1165 prev = sel4test_timestamp(env); 1166 while (1) { 1167 uint64_t now = sel4test_timestamp(env); 1168 uint64_t diff = now - prev; 1169 if (diff < 10 * NS_IN_US) { 1170 counters[id]++; 1171 } 1172 prev = now; 1173 } 1174} 1175 1176int test_budget_overrun(env_t env) 1177{ 1178 /* Run two periodic threads that do not yeild but count the approximate 1179 * amount of time that they run for in 10's of nanoseconds. 1180 * 1181 * Each thread has a different share of the processor. 1182 * Both threads are higher prio than the test runner thread. 1183 * Make sure the test runner thread gets to run, and that 1184 * the two threads run roughly according to their budgets 1185 */ 1186 volatile unsigned long long counters[2]; 1187 helper_thread_t thirty, fifty; 1188 int error; 1189 1190 /* set priority down so we can run the helper(s) at a higher prio */ 1191 error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority - 1); 1192 test_eq(error, seL4_NoError); 1193 1194 create_helper_thread(env, &thirty); 1195 create_helper_thread(env, &fifty); 1196 1197 set_helper_priority(env, &thirty, env->priority); 1198 set_helper_priority(env, &fifty, env->priority); 1199 1200 set_helper_sched_params(env, &fifty, 0.1 * US_IN_S, 0.2 * US_IN_S, 0); 1201 set_helper_sched_params(env, &thirty, 0.1 * US_IN_S, 0.3 * US_IN_S, 0); 1202 1203 start_helper(env, &fifty, (helper_fn_t) sched0015_helper, 1, (seL4_Word)env, 1204 (seL4_Word) counters, 0); 1205 start_helper(env, &thirty, (helper_fn_t) sched0015_helper, 0, (seL4_Word)env, 1206 (seL4_Word) counters, 0); 1207 1208 uint64_t ticks = 0; 1209 while (counters[1] < 10000000) { 1210 sel4test_sleep(env, US_IN_S); 1211 ticks++; 1212 ZF_LOGD("Tick %llu", counters[1]); 1213 } 1214 error = seL4_TCB_Suspend(thirty.thread.tcb.cptr); 1215 test_eq(error, 0); 1216 test_geq(counters[0], 0llu); 1217 1218 error = seL4_TCB_Suspend(fifty.thread.tcb.cptr); 1219 test_eq(error, 0); 1220 test_geq(counters[1], 0llu); 1221 1222 /* we should have run in the 20% of time left by thirty and fifty threads */ 1223 test_geq(ticks, 0llu); 1224 /* fifty should have run more than thirty */ 1225 test_geq(counters[1], counters[0]); 1226 1227 ZF_LOGD("Result: 30%% incremented %llu, 50%% incremened %llu\n", 1228 counters[0], counters[1]); 1229 1230 return sel4test_get_result(); 1231} 1232 1233static void sched0016_helper(volatile int *state) 1234{ 1235 while (1) { 1236 printf("Hello\n"); 1237 *state = *state + 1; 1238 seL4_Yield(); 1239 } 1240 1241 ZF_LOGF("Should not get here!"); 1242} 1243 1244int test_resume_no_overflow(env_t env) 1245{ 1246 /* test thread cannot exceed its budget by being suspended and resumed */ 1247 helper_thread_t helper; 1248 volatile int state = 0; 1249 int error = 0; 1250 1251 /* set priority down so we can run the helper(s) at a higher prio */ 1252 error = seL4_TCB_SetPriority(env->tcb, env->tcb, env->priority); 1253 test_eq(error, seL4_NoError); 1254 1255 create_helper_thread(env, &helper); 1256 set_helper_priority(env, &helper, env->priority); 1257 1258 /* this thread only runs for 1 second every 10 minutes */ 1259 set_helper_sched_params(env, &helper, 1 * US_IN_S, 10 * SEC_IN_MINUTE * US_IN_S, 0); 1260 1261 start_helper(env, &helper, (helper_fn_t) sched0016_helper, (seL4_Word) &state, 1262 0, 0, 0); 1263 seL4_Yield(); 1264 test_eq(state, 1); 1265 1266 for (int i = 0; i < 10; i++) { 1267 error = seL4_TCB_Suspend(helper.thread.tcb.cptr); 1268 test_eq(error, 0); 1269 1270 error = seL4_TCB_Resume(helper.thread.tcb.cptr); 1271 test_eq(error, 0); 1272 1273 seL4_Yield(); 1274 1275 test_eq(state, 1); 1276 } 1277 1278 return sel4test_get_result(); 1279} 1280DEFINE_TEST(SCHED0016, "Test resume cannot be used to exceed budget", test_resume_no_overflow, 1281 config_set(CONFIG_KERNEL_MCS)); 1282 1283#ifdef CONFIG_KERNEL_MCS 1284void sched0017_helper_fn(seL4_CPtr sc, volatile seL4_SchedContext_YieldTo_t *ret) 1285{ 1286 ZF_LOGD("Yield To"); 1287 *ret = seL4_SchedContext_YieldTo(sc); 1288} 1289 1290int test_yieldTo_errors(env_t env) 1291{ 1292 volatile seL4_SchedContext_YieldTo_t ret; 1293 1294 /* can't yieldTo self */ 1295 ret = seL4_SchedContext_YieldTo(simple_get_sc(&env->simple)); 1296 test_eq(ret.error, seL4_IllegalOperation); 1297 1298 /* can't yield to unbound sched context */ 1299 seL4_CPtr sched_context = vka_alloc_sched_context_leaky(&env->vka); 1300 ret = seL4_SchedContext_YieldTo(sched_context); 1301 test_eq(ret.error, seL4_IllegalOperation); 1302 1303 /* yield to unrunnable thread (permitted, but should return immediately) */ 1304 helper_thread_t helper; 1305 create_helper_thread(env, &helper); 1306 ret = seL4_SchedContext_YieldTo(helper.thread.sched_context.cptr); 1307 test_eq(ret.error, seL4_NoError); 1308 test_eq(ret.consumed, 0llu); 1309 1310 /* start the thread and have it try to yield to us - but fail as 1311 * we have a higher mcp 1312 */ 1313 ZF_LOGD("Yield to MCP check\n"); 1314 set_helper_mcp(env, &helper, 0); 1315 start_helper(env, &helper, (helper_fn_t) sched0017_helper_fn, simple_get_sc(&env->simple), 1316 (seL4_Word) &ret, 0, 0); 1317 1318 ZF_LOGD("Wait for helper\n"); 1319 wait_for_helper(&helper); 1320 test_eq(ret.error, seL4_IllegalOperation); 1321 1322 return sel4test_get_result(); 1323} 1324DEFINE_TEST(SCHED0017, "Test seL4_SchedContext_YieldTo errors", test_yieldTo_errors, config_set(CONFIG_KERNEL_MCS)); 1325 1326int sched0018_to_fn(void) 1327{ 1328 while (1) { 1329 ZF_LOGD("Running"); 1330 } 1331} 1332 1333int test_yieldTo_cleanup(env_t env) 1334{ 1335 int error; 1336 helper_thread_t to, from; 1337 volatile seL4_SchedContext_YieldTo_t ret; 1338 1339 create_helper_thread(env, &to); 1340 create_helper_thread(env, &from); 1341 1342 start_helper(env, &to, (helper_fn_t) sched0018_to_fn, 0, 0, 0, 0); 1343 start_helper(env, &from, (helper_fn_t) sched0017_helper_fn, to.thread.sched_context.cptr, (seL4_Word) &ret, 0, 0); 1344 1345 set_helper_mcp(env, &to, seL4_MaxPrio); 1346 set_helper_mcp(env, &from, seL4_MaxPrio); 1347 error = set_helper_sched_params(env, &to, 500 * US_IN_MS, 500 * US_IN_MS, 0); 1348 test_eq(error, seL4_NoError); 1349 error = set_helper_sched_params(env, &from, 500 * US_IN_MS, 500 * US_IN_MS, 0); 1350 test_eq(error, seL4_NoError); 1351 1352 /* wait for them to execute */ 1353 ZF_LOGD("Sleep\n"); 1354 sel4test_sleep(env, NS_IN_S); 1355 1356 ZF_LOGD("suspend to\n"); 1357 /* suspend yielded to thread */ 1358 error = seL4_TCB_Suspend(to.thread.tcb.cptr); 1359 test_eq(error, seL4_NoError); 1360 1361 ZF_LOGD("Wait for from\n"); 1362 wait_for_helper(&from); 1363 test_eq(ret.error, seL4_NoError); 1364 test_gt(ret.consumed, 0llu); 1365 1366 ret.consumed = 0; 1367 1368 /* restart threads */ 1369 cleanup_helper(env, &from); 1370 cleanup_helper(env, &to); 1371 1372 create_helper_thread(env, &from); 1373 create_helper_thread(env, &to); 1374 set_helper_mcp(env, &to, seL4_MaxPrio); 1375 set_helper_mcp(env, &from, seL4_MaxPrio); 1376 start_helper(env, &to, (helper_fn_t) sched0018_to_fn, 0, 0, 0, 0); 1377 start_helper(env, &from, (helper_fn_t) sched0017_helper_fn, to.thread.sched_context.cptr, 1378 (seL4_Word) &ret, 0, 0); 1379 1380 /* let them run */ 1381 ZF_LOGD("Sleep\n"); 1382 sel4test_sleep(env, NS_IN_S); 1383 1384 /* delete yielded to thread */ 1385 ZF_LOGD("Delete yielded to\n"); 1386 cleanup_helper(env, &to); 1387 1388 ZF_LOGD("Wait for from\n"); 1389 wait_for_helper(&from); 1390 test_eq(ret.error, seL4_NoError); 1391 test_gt(ret.consumed, 0llu); 1392 1393 /* restart threads */ 1394 cleanup_helper(env, &from); 1395 1396 create_helper_thread(env, &from); 1397 create_helper_thread(env, &to); 1398 start_helper(env, &to, (helper_fn_t) sched0018_to_fn, 0, 0, 0, 0); 1399 start_helper(env, &from, (helper_fn_t) sched0017_helper_fn, to.thread.sched_context.cptr, 1400 (seL4_Word) &ret, 0, 0); 1401 set_helper_mcp(env, &to, seL4_MaxPrio); 1402 set_helper_mcp(env, &from, seL4_MaxPrio); 1403 /* wait for them to execute */ 1404 ZF_LOGD("sleep\n"); 1405 sel4test_sleep(env, NS_IN_S); 1406 1407 /* delete yielded from thread */ 1408 /* delete yielded from thread */ 1409 ZF_LOGD("delete from\n"); 1410 cleanup_helper(env, &from); 1411 1412 return sel4test_get_result(); 1413} 1414DEFINE_TEST(SCHED0018, "Test clean up cases after seL4_SchedContext_YieldTo", test_yieldTo_cleanup, 1415 config_set(CONFIG_KERNEL_MCS) &&config_set(CONFIG_HAVE_TIMER)); 1416 1417 1418int test_yieldTo(env_t env) 1419{ 1420 int error; 1421 helper_thread_t to, from; 1422 volatile seL4_SchedContext_YieldTo_t ret; 1423 1424 create_helper_thread(env, &to); 1425 create_helper_thread(env, &from); 1426 1427 start_helper(env, &to, (helper_fn_t) sched0018_to_fn, 0, 0, 0, 0); 1428 start_helper(env, &from, (helper_fn_t) sched0017_helper_fn, to.thread.sched_context.cptr, (seL4_Word) &ret, 0, 0); 1429 1430 set_helper_mcp(env, &to, seL4_MaxPrio); 1431 set_helper_mcp(env, &from, seL4_MaxPrio); 1432 error = set_helper_sched_params(env, &to, 500 * US_IN_MS, 500 * US_IN_MS, 0); 1433 test_eq(error, seL4_NoError); 1434 error = set_helper_sched_params(env, &from, 500 * US_IN_MS, 500 * US_IN_MS, 0); 1435 test_eq(error, seL4_NoError); 1436 1437 ZF_LOGD("Wait for from\n"); 1438 wait_for_helper(&from); 1439 test_eq(ret.error, seL4_NoError); 1440 test_geq(ret.consumed, 0llu); 1441 1442 return sel4test_get_result(); 1443} 1444DEFINE_TEST(SCHED0019, "Test seL4_SchedContext_YieldTo", test_yieldTo, config_set(CONFIG_KERNEL_MCS)); 1445 1446#endif /* CONFIG_KERNEL_MCS */ 1447 1448void set_higher_prio_helper(volatile int *state) 1449{ 1450 /* Yield incase the scheduler picked us before the 1451 * test set our priority higher */ 1452 seL4_Yield(); 1453 *state = 2; 1454} 1455 1456static int test_set_higher_prio(struct env *env) 1457{ 1458 helper_thread_t thread; 1459 1460 /* set our priority down */ 1461 int error = seL4_TCB_SetPriority(env->tcb, env->tcb, OUR_PRIO - 1); 1462 test_eq(error, seL4_NoError); 1463 1464 /* start helper at highest prio */ 1465 volatile int state = 0; 1466 1467 /* start helper - it will run at the same prio as us */ 1468 create_helper_thread(env, &thread); 1469 start_helper(env, &thread, (helper_fn_t) set_higher_prio_helper, (seL4_Word) &state, 0, 0, 0); 1470 /* check it didn't update state yet - even if the scheduler picks the helper 1471 * it will yield back to us first */ 1472 test_eq(state, 0); 1473 1474 set_helper_priority(env, &thread, OUR_PRIO); 1475 1476 /* helper should run and set state to 2 */ 1477 test_eq(state, 2); 1478 1479 return sel4test_get_result(); 1480} 1481DEFINE_TEST(SCHED0020, "test set prio to a higher prio runs higher prio thread", test_set_higher_prio, true); 1482 1483#define PREEMPTION_THREADS 4 1484 1485#ifdef CONFIG_KERNEL_MCS 1486/* Under MCS each round-robin thread will run for a dedicated time slice 1487 * before being pre-empted at the end. This should ensure full 1488 * utilisation of the entire slice for each thread. */ 1489#define MIN_THREAD_SLICE (CONFIG_BOOT_THREAD_TIME_SLICE * NS_IN_MS) 1490#define MAX_THREAD_SLICE MIN_THREAD_SLICE 1491#else 1492/* With the non-mcs scheduler timer ticks occur periodically and may 1493 * even include some amount of drift. Whichever thread is executing when 1494 * a tick occurs is charged an entire tick from its time slice. A thread 1495 * executing continually may therefore only actually execute for one 1496 * less than the allocated number of ticks */ 1497#define MIN_THREAD_SLICE (CONFIG_TIMER_TICK_MS * (CONFIG_TIME_SLICE - 1) * NS_IN_MS) 1498#define MAX_THREAD_SLICE (CONFIG_TIMER_TICK_MS * CONFIG_TIME_SLICE * NS_IN_MS) 1499#endif 1500 1501#define TIME_SCALE(time, dividend, divisor) (((time) * (dividend)) / (divisor)) 1502 1503/* This test should at least run through the minimal time for each 1504 * concurrent thread updating the data. Allow for 2% speedup */ 1505#define MIN_TIME TIME_SCALE(MIN_THREAD_SLICE * PREEMPTION_THREADS, 98, 100) 1506/* This thread should run for no longer that tne maximum time for all 1507 * threads to execute plus the time to execute monitor thread. Allow for 1508 * 2% slowdown. */ 1509#define MAX_TIME TIME_SCALE(MAX_THREAD_SLICE * (PREEMPTION_THREADS + 1), 102, 100) 1510 1511volatile unsigned int preemption_thread_data[PREEMPTION_THREADS]; 1512volatile unsigned int test_simple_preempt_start = 0; 1513 1514static inline uint64_t time_now(struct env *env) 1515{ 1516 if (config_set(CONFIG_HAVE_TIMER)) { 1517 return sel4test_timestamp(env); 1518 } else { 1519 return 0; 1520 } 1521} 1522 1523void test_simple_preempt_runner(size_t thread_id) 1524{ 1525 ssize_t next = (thread_id - 1) % PREEMPTION_THREADS; 1526 while (test_simple_preempt_start == 0) { 1527 ZF_LOGD("#%zu", thread_id); 1528 } 1529 1530 /* Get the count for the previous thread */ 1531 unsigned int next_data = preemption_thread_data[next]; 1532 1533 /* We only stay in this loop until we get back to the monitor to 1534 * ensure that we don't loop again if it takes too long to check all 1535 * the threads. */ 1536 while (test_simple_preempt_start) { 1537 /* Signal test thread */ 1538 unsigned int data = preemption_thread_data[thread_id] + 1; 1539 preemption_thread_data[thread_id] = data; 1540 1541 ZF_LOGD("#%zu = %u", thread_id, data); 1542 1543 /* Wait for next thread to progress */ 1544 while (preemption_thread_data[next] <= next_data); 1545 next_data = preemption_thread_data[next]; 1546 } 1547} 1548 1549 1550/* 1551 * Checks that ticks preempt threads of equal priority, and 1552 * all threads get run RR exactly once 1553 */ 1554static int test_simple_preempt(struct env *env) 1555{ 1556#ifdef CONFIG_DEBUG_BUILD 1557 seL4_DebugNameThread(env->tcb, "Pre-empt monitor"); 1558#endif 1559 1560 uint64_t total = 0; 1561 uint64_t prev = time_now(env); 1562 for (size_t i = 0; i < 10; i++) { 1563 uint64_t next = time_now(env); 1564 total += next - prev; 1565 prev = next; 1566 } 1567 ZF_LOGD("Average to read time %lluns", total / 10); 1568 1569 helper_thread_t threads[PREEMPTION_THREADS] = {}; 1570 1571 ZF_LOGD("%zu threads, %lluus time slice", (size_t)PREEMPTION_THREADS, (uint64_t)(MAX_THREAD_SLICE / NS_IN_US)); 1572 1573 /* Configure all of the threads */ 1574 for (size_t thread = 0; thread < PREEMPTION_THREADS; thread += 1) { 1575 /* Ensure the thread data starts at 0 */ 1576 preemption_thread_data[thread] = 0; 1577 1578 create_helper_thread(env, &threads[thread]); 1579 1580 /* Thread must run at same prio as monitor */ 1581 set_helper_priority(env, &threads[thread], OUR_PRIO); 1582 1583#ifdef CONFIG_DEBUG_BUILD 1584 char name[32] = ""; 1585 snprintf(name, 32, "Pre-empt #%zu", thread); 1586 seL4_DebugNameThread(threads[thread].thread.tcb.cptr, name); 1587#endif 1588 1589 /* Start the thread (will yield until we're ready) */ 1590 ZF_LOGD("Start thread %u", thread); 1591 start_helper(env, &threads[thread], (helper_fn_t) test_simple_preempt_runner, thread, 0, 0, 0); 1592 ZF_LOGD("Started thread %u", thread); 1593 } 1594 1595 /* Set a timeout for the test. 1596 * Each thread should be run for one tick */ 1597 uint64_t start = time_now(env); 1598 uint64_t now = start; 1599 1600 /* Start executing other threads */ 1601 ZF_LOGD("Releasing Threads"); 1602 test_simple_preempt_start = 1; 1603 /* Yield should cause all other threads to execute before returning 1604 * to the current thread. */ 1605 seL4_Yield(); 1606 test_simple_preempt_start = 0; 1607 1608 /* Get the total time taken to synchronise */ 1609 now = time_now(env); 1610 uint64_t duration = now - start; 1611 1612 for (size_t thread = 0; thread < PREEMPTION_THREADS; thread += 1) { 1613 cleanup_helper(env, &threads[thread]); 1614 1615 /* Each thread should only have been run once */ 1616 test_eq(preemption_thread_data[thread], 1); 1617 } 1618 1619 /* Only check duration where a user timer is available */ 1620 if (config_set(CONFIG_HAVE_TIMER)) { 1621 /* Show total time */ 1622 ZF_LOGI("Finished in %lluus", (now - start) / NS_IN_US); 1623 1624 /* If the timeout was exceeded this test has failed */ 1625 test_geq(now, start); 1626 test_geq(duration, MIN_TIME); 1627 test_lt(duration, MAX_TIME); 1628 } 1629 1630 1631 return sel4test_get_result(); 1632} 1633DEFINE_TEST(SCHED0021, "Test for pre-emption during running of many threads with equal prio", test_simple_preempt, 1634 true); 1635 1636int sched0022_to_fn(struct env *env, helper_thread_t *thread, seL4_CPtr ep) 1637{ 1638 seL4_MessageInfo_t tag = {0}; 1639 seL4_MessageInfo_ptr_set_length(&tag, 2); 1640 1641 /* change to core 1 */ 1642 seL4_Error error = api_sched_ctrl_configure(simple_get_sched_ctrl(&env->simple, 1), 1643 thread->thread.sched_context.cptr, 1644 10000, 1645 10000, 1646 1, 1647 0); 1648 seL4_SetMR(0, error); 1649 /* and back to core 0 */ 1650 error = api_sched_ctrl_configure(simple_get_sched_ctrl(&env->simple, 0), 1651 thread->thread.sched_context.cptr, 1652 10000, 1653 10000, 1654 1, 1655 0); 1656 1657 seL4_SetMR(1, error); 1658 seL4_Send(ep, tag); 1659} 1660 1661/* Test that a helper thread can move itself back from another core. 1662 * Save the return values and check them in test thread. 1663 */ 1664static int test_changing_affinity(struct env *env) 1665{ 1666 int error; 1667 helper_thread_t t0; 1668 seL4_MessageInfo_t tag; 1669 seL4_CPtr reply, ep; 1670 seL4_Word sender_badge = 0; 1671 1672 ep = vka_alloc_endpoint_leaky(&env->vka); 1673 1674 create_helper_thread(env, &t0); 1675 1676 reply = get_helper_reply(&t0); 1677 1678 start_helper(env, &t0, (helper_fn_t) sched0022_to_fn, (seL4_Word) env, (seL4_Word) &t0, ep, 0); 1679 1680 tag = api_recv(ep, &sender_badge, reply); 1681 1682 /* Check error codes */ 1683 for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) { 1684 test_assert(!seL4_GetMR(i)); 1685 } 1686 1687 cleanup_helper(env, &t0); 1688 1689 return sel4test_get_result(); 1690} 1691DEFINE_TEST(SCHED0022, "test changing a helper threads core", test_changing_affinity, 1692 (config_set(CONFIG_KERNEL_MCS) &&(CONFIG_MAX_NUM_NODES > 1))); 1693