| /* |
| * Copyright (c) 2018 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <zephyr.h> |
| #include <tc_util.h> |
| #include <ztest.h> |
| #include <kernel.h> |
| #include <spinlock.h> |
| |
| BUILD_ASSERT(CONFIG_MP_NUM_CPUS > 1); |
| |
| #define CPU1_STACK_SIZE 1024 |
| |
| K_THREAD_STACK_DEFINE(cpu1_stack, CPU1_STACK_SIZE); |
| struct k_thread cpu1_thread; |
| |
| static struct k_spinlock bounce_lock; |
| |
| volatile int bounce_owner, bounce_done; |
| |
| /** |
| * @brief Tests for spinlock |
| * |
| * @defgroup kernel_spinlock_tests Spinlock Tests |
| * |
| * @ingroup all_tests |
| * |
| * @{ |
| * @} |
| */ |
| |
| /** |
| * @brief Test basic spinlock |
| * |
| * @ingroup kernel_spinlock_tests |
| * |
| * @see k_spin_lock(), k_spin_unlock() |
| */ |
| void test_spinlock_basic(void) |
| { |
| k_spinlock_key_t key; |
| static struct k_spinlock l; |
| |
| zassert_true(!l.locked, "Spinlock initialized to locked"); |
| |
| key = k_spin_lock(&l); |
| |
| zassert_true(l.locked, "Spinlock failed to lock"); |
| |
| k_spin_unlock(&l, key); |
| |
| zassert_true(!l.locked, "Spinlock failed to unlock"); |
| } |
| |
| void bounce_once(int id) |
| { |
| int i, locked; |
| k_spinlock_key_t key; |
| |
| /* Take the lock, check last owner and release if it was us. |
| * Wait for us to get the lock "after" another CPU |
| */ |
| locked = 0; |
| for (i = 0; i < 10000; i++) { |
| key = k_spin_lock(&bounce_lock); |
| |
| if (bounce_owner != id) { |
| locked = 1; |
| break; |
| } |
| |
| k_spin_unlock(&bounce_lock, key); |
| k_busy_wait(100); |
| } |
| |
| if (!locked && bounce_done) { |
| return; |
| } |
| |
| zassert_true(locked, "Other cpu did not get lock in 10000 tries"); |
| |
| /* Mark us as the owner, spin for a while validating that we |
| * never see another owner write to the protected data. |
| */ |
| bounce_owner = id; |
| |
| for (i = 0; i < 100; i++) { |
| zassert_true(bounce_owner == id, "Locked data changed"); |
| } |
| |
| /* Release the lock */ |
| k_spin_unlock(&bounce_lock, key); |
| } |
| |
| void cpu1_fn(void *p1, void *p2, void *p3) |
| { |
| ARG_UNUSED(p1); |
| ARG_UNUSED(p2); |
| ARG_UNUSED(p3); |
| |
| while (1) { |
| bounce_once(4321); |
| } |
| } |
| |
| /** |
| * @brief Test spinlock with bounce |
| * |
| * @ingroup kernel_spinlock_tests |
| * |
| * @see arch_start_cpu() |
| */ |
| void test_spinlock_bounce(void) |
| { |
| int i; |
| |
| k_thread_create(&cpu1_thread, cpu1_stack, CPU1_STACK_SIZE, |
| cpu1_fn, NULL, NULL, NULL, |
| 0, 0, K_NO_WAIT); |
| |
| k_busy_wait(10); |
| |
| for (i = 0; i < 10000; i++) { |
| bounce_once(1234); |
| } |
| |
| bounce_done = 1; |
| } |
| |
| /** |
| * @brief Test basic mutual exclusion using interrupt masking |
| * |
| * @details |
| * - Spinlocks can be initialized at run-time. |
| * - Spinlocks in uniprocessor context should achieve mutual exclusion using |
| * interrupt masking. |
| * |
| * @ingroup kernel_spinlock_tests |
| * |
| * @see k_spin_lock(), k_spin_unlock() |
| */ |
| void test_spinlock_mutual_exclusion(void) |
| { |
| k_spinlock_key_t key; |
| static struct k_spinlock lock_runtime; |
| unsigned int irq_key; |
| |
| (void)memset(&lock_runtime, 0, sizeof(lock_runtime)); |
| |
| key = k_spin_lock(&lock_runtime); |
| |
| zassert_true(lock_runtime.locked, "Spinlock failed to lock"); |
| |
| /* check irq has not locked */ |
| zassert_true(arch_irq_unlocked(key.key), |
| "irq should be first locked!"); |
| |
| /* |
| * We make irq locked nested to check if interrupt |
| * disable happened or not. |
| */ |
| irq_key = arch_irq_lock(); |
| |
| /* check irq has already locked */ |
| zassert_false(arch_irq_unlocked(irq_key), |
| "irq should be already locked!"); |
| |
| arch_irq_unlock(irq_key); |
| |
| k_spin_unlock(&lock_runtime, key); |
| |
| zassert_true(!lock_runtime.locked, "Spinlock failed to unlock"); |
| } |
| |
| |
| extern void test_spinlock_no_recursive(void); |
| extern void test_spinlock_unlock_error(void); |
| extern void test_spinlock_release_error(void); |
| |
| |
| void test_main(void) |
| { |
| ztest_test_suite(spinlock, |
| ztest_unit_test(test_spinlock_basic), |
| ztest_unit_test(test_spinlock_bounce), |
| ztest_unit_test(test_spinlock_mutual_exclusion), |
| ztest_unit_test(test_spinlock_no_recursive), |
| ztest_unit_test(test_spinlock_unlock_error), |
| ztest_unit_test(test_spinlock_release_error)); |
| ztest_run_test_suite(spinlock); |
| } |