/* Copyright (c) 2021 Intel Corporation. * SPDX-License-Identifier: Apache-2.0 */ #include #include #include /* Experimentally 10ms is enough time to get the second CPU to run on * all known platforms. */ #define CPU_START_DELAY 10000 /* IPIs happen much faster than CPU startup */ #define CPU_IPI_DELAY 1000 BUILD_ASSERT(CONFIG_SMP); BUILD_ASSERT(CONFIG_SMP_BOOT_DELAY); BUILD_ASSERT(CONFIG_MP_MAX_NUM_CPUS > 1); #define STACKSZ 2048 char stack[STACKSZ]; volatile bool mp_flag; struct k_thread cpu_thr; K_THREAD_STACK_DEFINE(thr_stack, STACKSZ); static void thread_fn(void *a, void *b, void *c) { mp_flag = true; } ZTEST(smp_boot_delay, test_smp_boot_delay) { /* Create a thread of lower priority. This could run on * another CPU if it was available, but will not preempt us * unless we block (which we do not). */ k_thread_create(&cpu_thr, thr_stack, K_THREAD_STACK_SIZEOF(thr_stack), thread_fn, NULL, NULL, NULL, 1, 0, K_NO_WAIT); /* Make sure that thread has not run (because the cpu is halted) */ k_busy_wait(CPU_START_DELAY); zassert_false(mp_flag, "CPU1 must not be running yet"); /* Start the second CPU */ k_smp_cpu_start(1, NULL, NULL); /* Verify the thread ran */ k_busy_wait(CPU_START_DELAY); zassert_true(mp_flag, "CPU1 did not start"); k_thread_abort(&cpu_thr); k_thread_join(&cpu_thr, K_FOREVER); /* Spawn the same thread to do the same thing, but this time * expect that the thread is going to run synchronously on the * other CPU as soon as its created. Intended to test whether * IPIs were correctly set up on the runtime-launched CPU. */ mp_flag = false; k_thread_create(&cpu_thr, thr_stack, K_THREAD_STACK_SIZEOF(thr_stack), thread_fn, NULL, NULL, NULL, 1, 0, K_NO_WAIT); k_busy_wait(CPU_IPI_DELAY); k_thread_abort(&cpu_thr); k_thread_join(&cpu_thr, K_FOREVER); zassert_true(mp_flag, "CPU1 did not start thread via IPI"); } volatile bool custom_init_flag; void custom_init_fn(void *arg) { volatile bool *flag = (void *)arg; *flag = true; } ZTEST(smp_boot_delay, test_smp_custom_start) { k_tid_t thr; if (CONFIG_MP_MAX_NUM_CPUS <= 2) { /* CPU#1 has been started in test_smp_boot_delay * so we need another CPU for this test. */ ztest_test_skip(); } mp_flag = false; custom_init_flag = false; /* Create a thread pinned on CPU#2 so that it will not * run on other CPUs. */ thr = k_thread_create(&cpu_thr, thr_stack, K_THREAD_STACK_SIZEOF(thr_stack), thread_fn, NULL, NULL, NULL, 1, 0, K_FOREVER); (void)k_thread_cpu_pin(thr, 2); k_thread_start(thr); /* Make sure that thread has not run (because the cpu is halted) */ k_busy_wait(CPU_START_DELAY); zassert_false(mp_flag, "CPU2 must not be running yet"); /* Start the third CPU */ k_smp_cpu_start(2, custom_init_fn, (void *)&custom_init_flag); /* Verify the thread ran */ k_busy_wait(CPU_START_DELAY); zassert_true(mp_flag, "CPU2 did not start"); /* Verify that the custom init function has been called. */ zassert_true(custom_init_flag, "Custom init function has not been called."); k_thread_abort(&cpu_thr); k_thread_join(&cpu_thr, K_FOREVER); } ZTEST_SUITE(smp_boot_delay, NULL, NULL, NULL, NULL, NULL);