| /* |
| * Copyright (c) 2012-2016 Wind River Systems, Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Test kernel mutex APIs |
| * |
| * |
| * This module demonstrates the kernel's priority inheritance algorithm. |
| * A thread that owns a mutex is promoted to the priority level of the |
| * highest-priority thread attempting to lock the mutex. |
| * |
| * In addition, recursive locking capabilities and the use of a private mutex |
| * are also tested. |
| * |
| * This module tests the following mutex routines: |
| * |
| * sys_mutex_lock |
| * sys_mutex_unlock |
| * |
| * Timeline for priority inheritance testing: |
| * - 0.0 sec: thread_05, thread_06, thread_07, thread_08, thread_09, sleep |
| * : main thread takes mutex_1 then sleeps |
| * - 0.0 sec: thread_11 sleeps |
| * - 0.5 sec: thread_09 wakes and waits on mutex_1 |
| * - 1.0 sec: main thread (@ priority 9) takes mutex_2 then sleeps |
| * - 1.5 sec: thread_08 wakes and waits on mutex_2 |
| * - 2.0 sec: main thread (@ priority 8) takes mutex_3 then sleeps |
| * - 2.5 sec: thread_07 wakes and waits on mutex_3 |
| * - 3.0 sec: main thread (@ priority 7) takes mutex_4 then sleeps |
| * - 3.5 sec: thread_05 wakes and waits on mutex_4 |
| * - 3.5 sec: thread_11 wakes and waits on mutex_3 |
| * - 3.75 sec: thread_06 wakes and waits on mutex_4 |
| * - 4.0 sec: main thread wakes (@ priority 5) then sleeps |
| * - 4.5 sec: thread_05 times out |
| * - 5.0 sec: main thread wakes (@ priority 6) then gives mutex_4 |
| * : main thread (@ priority 7) sleeps |
| * - 5.5 sec: thread_07 times out on mutex_3 |
| * - 6.0 sec: main thread (@ priority 8) gives mutex_3 |
| * : main thread (@ priority 8) gives mutex_2 |
| * : main thread (@ priority 9) gives mutex_1 |
| * : main thread (@ priority 10) sleeps |
| */ |
| |
| #include <zephyr/tc_util.h> |
| #include <zephyr/zephyr.h> |
| #include <zephyr/ztest.h> |
| #include <zephyr/sys/mutex.h> |
| |
| #define STACKSIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE) |
| |
| static ZTEST_DMEM int tc_rc = TC_PASS; /* test case return code */ |
| |
| ZTEST_BMEM SYS_MUTEX_DEFINE(private_mutex); |
| |
| |
| ZTEST_BMEM SYS_MUTEX_DEFINE(mutex_1); |
| ZTEST_BMEM SYS_MUTEX_DEFINE(mutex_2); |
| ZTEST_BMEM SYS_MUTEX_DEFINE(mutex_3); |
| ZTEST_BMEM SYS_MUTEX_DEFINE(mutex_4); |
| |
| #ifdef CONFIG_USERSPACE |
| static SYS_MUTEX_DEFINE(no_access_mutex); |
| #endif |
| static ZTEST_BMEM SYS_MUTEX_DEFINE(not_my_mutex); |
| static ZTEST_BMEM SYS_MUTEX_DEFINE(bad_count_mutex); |
| |
| #ifdef CONFIG_USERSPACE |
| #define ZTEST_USER_OR_NOT ZTEST_USER |
| #else |
| #define ZTEST_USER_OR_NOT ZTEST |
| #endif |
| |
| #ifdef CONFIG_USERSPACE |
| #define PARTICIPANT_THREAD_OPTIONS (K_USER | K_INHERIT_PERMS) |
| #else |
| #define PARTICIPANT_THREAD_OPTIONS (0) |
| #endif |
| |
| #define DEFINE_PARTICIPANT_THREAD(id) \ |
| K_THREAD_STACK_DEFINE(thread_##id##_stack_area, STACKSIZE); \ |
| struct k_thread thread_##id##_thread_data; \ |
| k_tid_t thread_##id##_tid; |
| |
| #define CREATE_PARTICIPANT_THREAD(id, pri) \ |
| k_thread_create(&thread_##id##_thread_data, thread_##id##_stack_area, \ |
| K_THREAD_STACK_SIZEOF(thread_##id##_stack_area), \ |
| (k_thread_entry_t)thread_##id, \ |
| NULL, NULL, NULL, \ |
| pri, PARTICIPANT_THREAD_OPTIONS, K_FOREVER); |
| #define START_PARTICIPANT_THREAD(id) k_thread_start(&(thread_##id##_thread_data)); |
| #define JOIN_PARTICIPANT_THREAD(id) k_thread_join(&(thread_##id##_thread_data), K_FOREVER); |
| |
| /** |
| * |
| * thread_05 - |
| * |
| */ |
| |
| void thread_05(void) |
| { |
| int rv; |
| |
| k_sleep(K_MSEC(3500)); |
| |
| /* Wait and boost owner priority to 5 */ |
| rv = sys_mutex_lock(&mutex_4, K_SECONDS(1)); |
| if (rv != -EAGAIN) { |
| tc_rc = TC_FAIL; |
| TC_ERROR("Failed to timeout on mutex %p\n", &mutex_4); |
| return; |
| } |
| } |
| |
| |
| /** |
| * |
| * thread_06 - |
| * |
| */ |
| |
| void thread_06(void) |
| { |
| int rv; |
| |
| k_sleep(K_MSEC(3750)); |
| |
| /* |
| * Wait for the mutex. There is a higher priority level thread waiting |
| * on the mutex, so request will not immediately contribute to raising |
| * the priority of the owning thread (main thread). When thread_05 |
| * times out this thread will become the highest priority waiting |
| * thread. The priority of the owning thread (main thread) will not |
| * drop back to 7, but will instead drop to 6. |
| */ |
| |
| rv = sys_mutex_lock(&mutex_4, K_SECONDS(2)); |
| if (rv != 0) { |
| tc_rc = TC_FAIL; |
| TC_ERROR("Failed to take mutex %p\n", &mutex_4); |
| return; |
| } |
| |
| sys_mutex_unlock(&mutex_4); |
| } |
| |
| /** |
| * |
| * thread_07 - |
| * |
| */ |
| |
| void thread_07(void) |
| { |
| int rv; |
| |
| k_sleep(K_MSEC(2500)); |
| |
| /* |
| * Wait and boost owner priority to 7. While waiting, another thread of |
| * a very low priority level will also wait for the mutex. thread_07 is |
| * expected to time out around the 5.5 second mark. When it times out, |
| * thread_11 will become the only waiting thread for this mutex and the |
| * priority of the owning main thread will drop to 8. |
| */ |
| |
| rv = sys_mutex_lock(&mutex_3, K_SECONDS(3)); |
| if (rv != -EAGAIN) { |
| tc_rc = TC_FAIL; |
| TC_ERROR("Failed to timeout on mutex %p\n", &mutex_3); |
| return; |
| } |
| |
| } |
| |
| /** |
| * |
| * thread_08 - |
| * |
| */ |
| |
| void thread_08(void) |
| { |
| int rv; |
| |
| k_sleep(K_MSEC(1500)); |
| |
| /* Wait and boost owner priority to 8 */ |
| rv = sys_mutex_lock(&mutex_2, K_FOREVER); |
| if (rv != 0) { |
| tc_rc = TC_FAIL; |
| TC_ERROR("Failed to take mutex %p\n", &mutex_2); |
| return; |
| } |
| |
| sys_mutex_unlock(&mutex_2); |
| } |
| |
| /** |
| * |
| * thread_09 - |
| * |
| */ |
| |
| void thread_09(void) |
| { |
| int rv; |
| |
| k_sleep(K_MSEC(500)); /* Allow lower priority thread to run */ |
| |
| /*<mutex_1> is already locked. */ |
| rv = sys_mutex_lock(&mutex_1, K_NO_WAIT); |
| if (rv != -EBUSY) { /* This attempt to lock the mutex */ |
| /* should not succeed. */ |
| tc_rc = TC_FAIL; |
| TC_ERROR("Failed to NOT take locked mutex %p\n", &mutex_1); |
| return; |
| } |
| |
| /* Wait and boost owner priority to 9 */ |
| rv = sys_mutex_lock(&mutex_1, K_FOREVER); |
| if (rv != 0) { |
| tc_rc = TC_FAIL; |
| TC_ERROR("Failed to take mutex %p\n", &mutex_1); |
| return; |
| } |
| |
| sys_mutex_unlock(&mutex_1); |
| } |
| |
| /** |
| * |
| * thread_11 - |
| * |
| */ |
| |
| void thread_11(void) |
| { |
| int rv; |
| |
| k_sleep(K_MSEC(3500)); |
| rv = sys_mutex_lock(&mutex_3, K_FOREVER); |
| if (rv != 0) { |
| tc_rc = TC_FAIL; |
| TC_ERROR("Failed to take mutex %p\n", &mutex_2); |
| return; |
| } |
| sys_mutex_unlock(&mutex_3); |
| } |
| |
| K_THREAD_STACK_DEFINE(thread_12_stack_area, STACKSIZE); |
| struct k_thread thread_12_thread_data; |
| extern void thread_12(void); |
| |
| |
| |
| DEFINE_PARTICIPANT_THREAD(05); |
| DEFINE_PARTICIPANT_THREAD(06); |
| DEFINE_PARTICIPANT_THREAD(07); |
| DEFINE_PARTICIPANT_THREAD(08); |
| DEFINE_PARTICIPANT_THREAD(09); |
| DEFINE_PARTICIPANT_THREAD(11); |
| |
| void create_participant_threads(void) |
| { |
| CREATE_PARTICIPANT_THREAD(05, 5); |
| CREATE_PARTICIPANT_THREAD(06, 6); |
| CREATE_PARTICIPANT_THREAD(07, 7); |
| CREATE_PARTICIPANT_THREAD(08, 8); |
| CREATE_PARTICIPANT_THREAD(09, 9); |
| CREATE_PARTICIPANT_THREAD(11, 11); |
| } |
| |
| void start_participant_threads(void) |
| { |
| START_PARTICIPANT_THREAD(05); |
| START_PARTICIPANT_THREAD(06); |
| START_PARTICIPANT_THREAD(07); |
| START_PARTICIPANT_THREAD(08); |
| START_PARTICIPANT_THREAD(09); |
| START_PARTICIPANT_THREAD(11); |
| } |
| |
| void join_participant_threads(void) |
| { |
| JOIN_PARTICIPANT_THREAD(05); |
| JOIN_PARTICIPANT_THREAD(06); |
| JOIN_PARTICIPANT_THREAD(07); |
| JOIN_PARTICIPANT_THREAD(08); |
| JOIN_PARTICIPANT_THREAD(09); |
| JOIN_PARTICIPANT_THREAD(11); |
| } |
| |
| /** |
| * |
| * @brief Main thread to test thread_mutex_xxx interfaces |
| * |
| * This thread will lock on mutex_1, mutex_2, mutex_3 and mutex_4. It later |
| * recursively locks private_mutex, releases it, then re-locks it. |
| * |
| */ |
| |
| ZTEST_USER_OR_NOT(mutex_complex, test_mutex) |
| { |
| create_participant_threads(); |
| start_participant_threads(); |
| /* |
| * Main thread(test_main) priority was 10 but ztest thread runs at |
| * priority -1. To run the test smoothly make both main and ztest |
| * threads run at same priority level. |
| */ |
| k_thread_priority_set(k_current_get(), 10); |
| |
| int rv; |
| int i; |
| struct sys_mutex *mutexes[4] = { &mutex_1, &mutex_2, &mutex_3, |
| &mutex_4 }; |
| struct sys_mutex *givemutex[3] = { &mutex_3, &mutex_2, &mutex_1 }; |
| int priority[4] = { 9, 8, 7, 5 }; |
| int droppri[3] = { 8, 8, 9 }; |
| |
| TC_START("Test kernel Mutex API"); |
| |
| PRINT_LINE; |
| |
| /* |
| * 1st iteration: Take mutex_1; thread_09 waits on mutex_1 |
| * 2nd iteration: Take mutex_2: thread_08 waits on mutex_2 |
| * 3rd iteration: Take mutex_3; thread_07 waits on mutex_3 |
| * 4th iteration: Take mutex_4; thread_05 waits on mutex_4 |
| */ |
| |
| for (i = 0; i < 4; i++) { |
| rv = sys_mutex_lock(mutexes[i], K_NO_WAIT); |
| zassert_equal(rv, 0, "Failed to lock mutex %p\n", mutexes[i]); |
| k_sleep(K_SECONDS(1)); |
| |
| rv = k_thread_priority_get(k_current_get()); |
| zassert_equal(rv, priority[i], "expected priority %d, not %d\n", |
| priority[i], rv); |
| |
| /* Catch any errors from other threads */ |
| zassert_equal(tc_rc, TC_PASS, NULL); |
| } |
| |
| /* ~ 4 seconds have passed */ |
| |
| TC_PRINT("Done LOCKING! Current priority = %d\n", |
| k_thread_priority_get(k_current_get())); |
| |
| k_sleep(K_SECONDS(1)); /* thread_05 should time out */ |
| |
| /* ~ 5 seconds have passed */ |
| |
| rv = k_thread_priority_get(k_current_get()); |
| zassert_equal(rv, 6, "%s timed out and out priority should drop.\n", |
| "thread_05"); |
| zassert_equal(rv, 6, "Expected priority %d, not %d\n", 6, rv); |
| |
| sys_mutex_unlock(&mutex_4); |
| rv = k_thread_priority_get(k_current_get()); |
| zassert_equal(rv, 7, "Gave %s and priority should drop.\n", "mutex_4"); |
| zassert_equal(rv, 7, "Expected priority %d, not %d\n", 7, rv); |
| |
| k_sleep(K_SECONDS(1)); /* thread_07 should time out */ |
| |
| /* ~ 6 seconds have passed */ |
| |
| for (i = 0; i < 3; i++) { |
| rv = k_thread_priority_get(k_current_get()); |
| zassert_equal(rv, droppri[i], "Expected priority %d, not %d\n", |
| droppri[i], rv); |
| sys_mutex_unlock(givemutex[i]); |
| |
| zassert_equal(tc_rc, TC_PASS, NULL); |
| } |
| |
| rv = k_thread_priority_get(k_current_get()); |
| zassert_equal(rv, 10, "Expected priority %d, not %d\n", 10, rv); |
| |
| k_sleep(K_SECONDS(1)); /* Give thread_11 time to run */ |
| |
| zassert_equal(tc_rc, TC_PASS, NULL); |
| |
| /* test recursive locking using a private mutex */ |
| |
| TC_PRINT("Testing recursive locking\n"); |
| |
| rv = sys_mutex_lock(&private_mutex, K_NO_WAIT); |
| zassert_equal(rv, 0, "Failed to lock private mutex"); |
| |
| rv = sys_mutex_lock(&private_mutex, K_NO_WAIT); |
| zassert_equal(rv, 0, "Failed to recursively lock private mutex"); |
| |
| /* Start thread */ |
| k_thread_create(&thread_12_thread_data, thread_12_stack_area, STACKSIZE, |
| (k_thread_entry_t)thread_12, NULL, NULL, NULL, |
| K_PRIO_PREEMPT(12), PARTICIPANT_THREAD_OPTIONS, K_NO_WAIT); |
| k_sleep(K_MSEC(1)); /* Give thread_12 a chance to block on the mutex */ |
| |
| sys_mutex_unlock(&private_mutex); |
| sys_mutex_unlock(&private_mutex); /* thread_12 should now have lock */ |
| |
| rv = sys_mutex_lock(&private_mutex, K_NO_WAIT); |
| zassert_equal(rv, -EBUSY, "Unexpectedly got lock on private mutex"); |
| |
| rv = sys_mutex_lock(&private_mutex, K_SECONDS(1)); |
| zassert_equal(rv, 0, "Failed to re-obtain lock on private mutex"); |
| |
| sys_mutex_unlock(&private_mutex); |
| join_participant_threads(); |
| TC_PRINT("Recursive locking tests successful\n"); |
| } |
| |
| /* We deliberately disable userspace, even on platforms that |
| * support it, so that the alternate implementation of sys_mutex |
| * (which is just a very thin wrapper to k_mutex) is exercised. |
| * This requires us to not attempt to start the tests in user |
| * mode, as this will otherwise fail an assertion in the thread code. |
| */ |
| ZTEST(mutex_complex, test_supervisor_access) |
| { |
| int rv; |
| |
| #ifdef CONFIG_USERSPACE |
| /* coverage for get_k_mutex checks */ |
| rv = sys_mutex_lock((struct sys_mutex *)NULL, K_NO_WAIT); |
| zassert_true(rv == -EINVAL, "accepted bad mutex pointer"); |
| rv = sys_mutex_lock((struct sys_mutex *)k_current_get(), K_NO_WAIT); |
| zassert_true(rv == -EINVAL, "accepted object that was not a mutex"); |
| rv = sys_mutex_unlock((struct sys_mutex *)NULL); |
| zassert_true(rv == -EINVAL, "accepted bad mutex pointer"); |
| rv = sys_mutex_unlock((struct sys_mutex *)k_current_get()); |
| zassert_true(rv == -EINVAL, "accepted object that was not a mutex"); |
| #endif /* CONFIG_USERSPACE */ |
| |
| rv = sys_mutex_unlock(¬_my_mutex); |
| zassert_true(rv == -EPERM, "unlocked a mutex that wasn't owner"); |
| rv = sys_mutex_unlock(&bad_count_mutex); |
| zassert_true(rv == -EINVAL, "mutex wasn't locked"); |
| } |
| |
| ZTEST_USER_OR_NOT(mutex_complex, test_user_access) |
| { |
| #ifdef CONFIG_USERSPACE |
| int rv; |
| |
| rv = sys_mutex_lock(&no_access_mutex, K_NO_WAIT); |
| zassert_true(rv == -EACCES, "accessed mutex not in memory domain"); |
| rv = sys_mutex_unlock(&no_access_mutex); |
| zassert_true(rv == -EACCES, "accessed mutex not in memory domain"); |
| #else |
| ztest_test_skip(); |
| #endif /* CONFIG_USERSPACE */ |
| } |
| |
| /*test case main entry*/ |
| static void *sys_mutex_tests_setup(void) |
| { |
| int rv; |
| |
| /* We are on the main thread (supervisor thread). |
| * Grant necessary permissions to the main thread. |
| * The ztest thread (user thread) will inherit them. |
| */ |
| #ifdef CONFIG_USERSPACE |
| k_thread_access_grant(k_current_get(), |
| &thread_05_thread_data, &thread_05_stack_area, |
| &thread_06_thread_data, &thread_06_stack_area, |
| &thread_07_thread_data, &thread_07_stack_area, |
| &thread_08_thread_data, &thread_08_stack_area, |
| &thread_09_thread_data, &thread_09_stack_area, |
| &thread_11_thread_data, &thread_11_stack_area, |
| &thread_12_thread_data, &thread_12_stack_area); |
| #endif |
| rv = sys_mutex_lock(¬_my_mutex, K_NO_WAIT); |
| if (rv != 0) { |
| TC_ERROR("Failed to take mutex %p\n", ¬_my_mutex); |
| } |
| return NULL; |
| } |
| |
| ZTEST_SUITE(mutex_complex, NULL, sys_mutex_tests_setup, NULL, NULL, NULL); |