tests/kernel/common: Extend nested_irq_offload case to do a context switch

Bug #45779 discovered an edge case with nested interrupts on Xtensa
where they might select an incorrect thread context to return to
instead of the (mandatory!) return to the outer interrupt context.

Cleverly adjust the nested_irq_offload to exercise this.  It now
creates a thread that it knows it will interrupt, then suspends that
thread from within the inner/nested interrupt.  This guarantees that
_current will be different on exit from the second interrupt, which is
the case that tripped up Xtensa.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
Andy Ross 2022-05-19 11:05:55 -07:00 committed by Carles Cufí
commit 4b9a8a8471
2 changed files with 30 additions and 3 deletions

View file

@ -20,6 +20,9 @@
volatile uint32_t sentinel;
#define SENTINEL_VALUE 0xDEADBEEF
K_THREAD_STACK_DEFINE(offload_stack, 384 + CONFIG_TEST_EXTRA_STACK_SIZE);
struct k_thread offload_thread;
static void offload_function(const void *param)
{
uint32_t x = POINTER_TO_INT(param);
@ -198,6 +201,9 @@ static bool timer_executed, nested_executed;
void nestoff_offload(const void *parameter)
{
/* Suspend the thread we interrupted so we context switch, see below */
k_thread_suspend(&offload_thread);
nested_executed = true;
}
@ -214,6 +220,14 @@ static void nestoff_timer_fn(struct k_timer *timer)
timer_executed = true;
}
static void offload_thread_fn(void *p0, void *p1, void *p2)
{
k_timer_start(&nestoff_timer, K_TICKS(1), K_FOREVER);
while (true) {
zassert_false(timer_executed, "should not return to this thread");
}
}
/* Invoke irq_offload() from an interrupt and verify that the
* resulting nested interrupt doesn't explode
@ -224,14 +238,27 @@ void test_nested_irq_offload(void)
ztest_test_skip();
}
k_thread_priority_set(k_current_get(), 1);
k_timer_init(&nestoff_timer, nestoff_timer_fn, NULL);
zassert_false(timer_executed, "timer ran too soon");
zassert_false(nested_executed, "nested irq_offload ran too soon");
k_timer_start(&nestoff_timer, K_TICKS(1), K_FOREVER);
k_timer_status_sync(&nestoff_timer);
/* Do this in a thread to exercise a regression case: the
* offload handler will suspend the thread it interrupted,
* ensuring that the interrupt returns back to this thread and
* effects a context switch of of the nested interrupt (see
* #45779). Requires that this be a 1cpu test case,
* obviously.
*/
k_thread_create(&offload_thread,
offload_stack, K_THREAD_STACK_SIZEOF(offload_stack),
offload_thread_fn, NULL, NULL, NULL,
0, 0, K_NO_WAIT);
zassert_true(timer_executed, "timer did not run");
zassert_true(nested_executed, "nested irq_offload did not run");
k_thread_abort(&offload_thread);
}

View file

@ -126,7 +126,7 @@ void test_main(void)
ztest_test_suite(common,
ztest_unit_test(test_bootdelay),
ztest_unit_test(test_irq_offload),
ztest_unit_test(test_nested_irq_offload),
ztest_1cpu_unit_test(test_nested_irq_offload),
ztest_unit_test(test_byteorder_memcpy_swap),
ztest_unit_test(test_byteorder_mem_swap),
ztest_unit_test(test_sys_get_be64),