From 65dc344122c99b048eed7644fa4c1c6c439bff8a Mon Sep 17 00:00:00 2001 From: ouyangxiangzhen Date: Wed, 4 Feb 2026 20:10:50 +0800 Subject: [PATCH 1/6] ostest/spinlock: Fix the affinity side-affect. This commit created a new pthread to fix the side-affect introduced by sched_setaffinity. Assisted-By: mimo-v2.6-pro Signed-off-by: ouyangxiangzhen --- testing/ostest/spinlock.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/testing/ostest/spinlock.c b/testing/ostest/spinlock.c index b3df0829413..d47f16d9eba 100644 --- a/testing/ostest/spinlock.c +++ b/testing/ostest/spinlock.c @@ -135,7 +135,7 @@ void run_test_thread(void *lock, FAR void *(*thread_func)(FAR void *arg), /* Set affinity to CPU0 */ #ifdef CONFIG_SMP - if (OK != sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpu_set)) + if (OK != sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set)) { printf("spinlock_test: ERROR: nxsched_set_affinity failed"); ASSERT(false); @@ -154,6 +154,14 @@ void run_test_thread(void *lock, FAR void *(*thread_func)(FAR void *arg), ASSERT(false); } + status = sched_getparam(getpid(), &sparam); + if (status != 0) + { + printf("spinlock_test: ERROR: " + "sched_getparam failed, status=%d\n", status); + ASSERT(false); + } + sparam.sched_priority = SCHED_PRIORITY_MAX; status = pthread_attr_setschedparam(&attr, &sparam); if (status != OK) @@ -219,7 +227,7 @@ void run_test_thread(void *lock, FAR void *(*thread_func)(FAR void *arg), { printf("%s: Test Results:\n", lock_type); printf("%s: Final counter: %" PRIu32 "\n", lock_type, pub.counter); - assert(pub.counter == thread_num * LOOP_TIMES); + ASSERT(pub.counter == thread_num * LOOP_TIMES); printf("%s: Average throughput : %" PRIu64 " op/s\n", lock_type, (uint64_t)NSEC_PER_SEC * LOOP_TIMES * thread_num / total_ns); printf("%s: Total execution time: %" PRIu64 " ns\n \n", @@ -248,7 +256,6 @@ static void spinlock_test_thread_num(unsigned thread_num) seqcount_t seqcount; } lock; - printf("Start Lock test:\n"); printf("Thread num: %u, Loop times: %d\n\n", thread_num, LOOP_TIMES); spin_lock_init(&lock.spinlock); @@ -261,7 +268,7 @@ static void spinlock_test_thread_num(unsigned thread_num) run_test_thread(&lock, seqcount_t_test_thread, thread_num, "seqcount"); } -void spinlock_test(void) +static void *spinlock_test_thread(void *arg) { unsigned tnr; @@ -269,4 +276,26 @@ void spinlock_test(void) { spinlock_test_thread_num(tnr); } + + return NULL; +} + +void spinlock_test(void) +{ + pthread_t tid; + pthread_attr_t attr; + struct sched_param sparam; + + ASSERT(0 == pthread_attr_init(&attr)); + + ASSERT(0 == sched_getparam(getpid(), &sparam)); + sparam.sched_priority = SCHED_PRIORITY_MAX; + ASSERT(0 == pthread_attr_setschedparam(&attr, &sparam)); + + printf("spinlock_test: start......\n"); + + ASSERT(0 == pthread_create(&tid, &attr, spinlock_test_thread, NULL)); + ASSERT(0 == pthread_join(tid, NULL)); + + printf("spinlock_test: end......\n"); } From 20b6f927563587c396fb0122011c39f2b85991bd Mon Sep 17 00:00:00 2001 From: ouyangxiangzhen Date: Thu, 5 Feb 2026 09:55:20 +0800 Subject: [PATCH 2/6] ostest/spinlock: Update the comments. This commit updated the comments. Assisted-By: mimo-v2.6-pro Signed-off-by: ouyangxiangzhen --- testing/ostest/spinlock.c | 211 ++++++++++++++++++++++++++++++++------ 1 file changed, 181 insertions(+), 30 deletions(-) diff --git a/testing/ostest/spinlock.c b/testing/ostest/spinlock.c index d47f16d9eba..2dec05bde8d 100644 --- a/testing/ostest/spinlock.c +++ b/testing/ostest/spinlock.c @@ -38,68 +38,137 @@ * Preprocessor Definitions ****************************************************************************/ +/* Maximum number of test threads - typically one per CPU core */ + #define MAX_THREAD_NUM (CONFIG_SMP_NCPUS) + +/* Number of lock/unlock iterations per thread */ + + #define LOOP_TIMES (CONFIG_TEST_LOOP_SCALE * 100000) +/**************************************************************************** + * Type Definitions + ****************************************************************************/ + +/* Shared arguments for all test threads. + * This structure contains data that all threads need to access. + */ + aligned_data(64) struct spinlock_pub_args_s { - FAR void *lock; - volatile uint32_t counter; - atomic_t barrier; - uint32_t thread_num; + FAR void *lock; /* Pointer to the lock being tested */ + volatile uint32_t counter; /* Shared counter protected by the lock */ + atomic_t barrier; /* Synchronization barrier for thread startup */ + uint32_t thread_num; /* Total number of threads participating */ }; +/* Per-thread arguments. + * Each thread has its own instance of this structure. + */ + struct spinlock_thread_args_s { - uint64_t delta; - FAR struct spinlock_pub_args_s *pub; + uint64_t delta; /* Execution time for this thread (nanoseconds) */ + FAR struct spinlock_pub_args_s *pub; /* Pointer to shared arguments */ }; /**************************************************************************** * Private Functions ****************************************************************************/ -/* Helper functions for timespec calculating */ +/**************************************************************************** + * Name: calc_diff + * + * Description: + * Calculate time difference between two timespec structures. + * This function computes the elapsed time between start and end times, + * handling the case where the nanoseconds component of the end time + * is less than the start time (requiring a borrow from seconds). + * + * Input Parameters: + * start - Pointer to the starting timespec + * end - Pointer to the ending timespec + * + * Returned Value: + * Time difference in nanoseconds + * + ****************************************************************************/ static inline uint64_t calc_diff(FAR struct timespec *start, FAR struct timespec *end) { - uint64_t diff_sec = end->tv_sec - start->tv_sec; - long diff_nsec = end->tv_nsec - start->tv_nsec; - if (diff_nsec < 0) + int64_t sec_diff = end->tv_sec - start->tv_sec; + int64_t nsec_diff = end->tv_nsec - start->tv_nsec; + + /* Handle negative nanoseconds by borrowing from seconds */ + + if (nsec_diff < 0) { - diff_sec -= 1; - diff_nsec += 1000000000L; + sec_diff--; + nsec_diff += 1000000000L; } - return diff_sec * 1000000000ULL + diff_nsec; + return sec_diff * 1000000000ULL + nsec_diff; } +/**************************************************************************** + * Name: LOCK_TEST_FUNC + * + * Description: + * Test thread function for different lock types. + * This macro generates a test thread function for a specific lock type. + * Each thread performs LOOP_TIMES lock/unlock operations and measures + * the execution time. + * + * Input Parameters: + * lock_type - Type of lock to test (e.g., spinlock_t, rspinlock_t) + * lock_func - Lock acquisition function + * unlock_func - Lock release function + * + * Returned Value: + * None + * + ****************************************************************************/ + #define LOCK_TEST_FUNC(lock_type, lock_func, unlock_func) \ FAR static void * lock_type##_test_thread(FAR void *arg) \ { \ struct spinlock_thread_args_s *param = \ (struct spinlock_thread_args_s *)arg; \ - irqstate_t flags; \ + irqstate_t flags; \ struct timespec start; \ struct timespec end; \ - uint32_t i; \ + uint32_t idx; \ FAR struct spinlock_pub_args_s *pub = param->pub; \ - FAR lock_type *l = (FAR lock_type *)pub->lock; \ + FAR lock_type *lock = (FAR lock_type *)pub->lock; \ + \ + /* Synchronize thread startup using atomic barrier */ \ + \ atomic_add(&pub->barrier, 1); \ while (atomic_read(&pub->barrier) != pub->thread_num) \ { \ sched_yield(); \ } \ + \ + /* Start timing */ \ + \ clock_gettime(CLOCK_REALTIME, &start); \ - for (i = 0; i < LOOP_TIMES; i++) \ + \ + /* Perform lock/unlock operations */ \ + \ + for (idx = 0; idx < LOOP_TIMES; idx++) \ { \ - flags = lock_func(l); \ + flags = lock_func(lock); \ pub->counter++; \ - unlock_func(l, flags); \ + unlock_func(lock, flags); \ } \ + \ + /* Stop timing and calculate duration */ \ + \ clock_gettime(CLOCK_REALTIME, &end); \ param->delta = calc_diff(&start, &end); \ + \ return NULL; \ } @@ -107,21 +176,41 @@ LOCK_TEST_FUNC(spinlock_t, spin_lock_irqsave, spin_unlock_irqrestore) LOCK_TEST_FUNC(rspinlock_t, rspin_lock_irqsave, rspin_unlock_irqrestore) LOCK_TEST_FUNC(seqcount_t, write_seqlock_irqsave, write_sequnlock_irqrestore) +/**************************************************************************** + * Name: run_test_thread + * + * Description: + * Run a multi-threaded lock performance test. + * This function creates multiple threads that concurrently perform + * lock/unlock operations on the same lock. It measures the total + * execution time and calculates throughput. + * + * Input Parameters: + * lock - Pointer to the lock being tested + * thread_func - Thread function that performs lock operations + * thread_num - Number of threads to create + * lock_type - String describing the lock type (for output) + * + * Returned Value: + * None + * + ****************************************************************************/ + static inline void run_test_thread(void *lock, FAR void *(*thread_func)(FAR void *arg), uint32_t thread_num, const char *lock_type) { - pthread_t tid[MAX_THREAD_NUM]; - struct spinlock_pub_args_s pub; - pthread_attr_t attr; - struct sched_param sparam; + struct spinlock_pub_args_s pub; + pthread_attr_t attr; + struct sched_param sparam; + pthread_t tid[MAX_THREAD_NUM]; struct spinlock_thread_args_s param[MAX_THREAD_NUM]; - struct timespec stime; - struct timespec etime; - cpu_set_t cpu_set = 1u; - uint64_t total_ns = 0u; - int status; - int i; + struct timespec stime; + struct timespec etime; + int status; + int i; + cpu_set_t cpu_set = 1u; + uint64_t total_ns = 0u; /* Initialize the public parameters. */ @@ -204,6 +293,8 @@ void run_test_thread(void *lock, FAR void *(*thread_func)(FAR void *arg), } } + /* Wait for all threads to complete */ + for (i = 0; i < thread_num; i++) { status = pthread_join(tid[i], NULL); @@ -244,7 +335,21 @@ void run_test_thread(void *lock, FAR void *(*thread_func)(FAR void *arg), ****************************************************************************/ /**************************************************************************** - * Name: spinlock_test + * Name: spinlock_test_thread_num + * + * Description: + * Run lock performance tests for a specific number of threads. + * This function tests three different lock types: + * - spinlock_t: Basic spinlock + * - rspinlock_t: Recursive spinlock + * - seqcount_t: Sequence counter lock + * + * Input Parameters: + * thread_num - Number of threads to use in the test + * + * Returned Value: + * None + * ****************************************************************************/ static void spinlock_test_thread_num(unsigned thread_num) @@ -258,16 +363,39 @@ static void spinlock_test_thread_num(unsigned thread_num) printf("Thread num: %u, Loop times: %d\n\n", thread_num, LOOP_TIMES); + /* Test basic spinlock */ + spin_lock_init(&lock.spinlock); run_test_thread(&lock, spinlock_t_test_thread, thread_num, "spinlock"); + /* Test read-write spinlock */ + rspin_lock_init(&lock.rspinlock); run_test_thread(&lock, rspinlock_t_test_thread, thread_num, "rspinlock"); + /* Test sequence counter lock */ + seqlock_init(&lock.seqcount); run_test_thread(&lock, seqcount_t_test_thread, thread_num, "seqcount"); } +/**************************************************************************** + * Name: spinlock_test_thread + * + * Description: + * Main test thread function that runs tests with varying thread counts. + * This function iterates through different thread counts (1 to + * MAX_THREAD_NUM) to test lock performance under different concurrency + * levels. + * + * Input Parameters: + * arg - Unused argument + * + * Returned Value: + * Always returns NULL + * + ****************************************************************************/ + static void *spinlock_test_thread(void *arg) { unsigned tnr; @@ -280,20 +408,43 @@ static void *spinlock_test_thread(void *arg) return NULL; } +/**************************************************************************** + * Name: spinlock_test + * + * Description: + * Entry point for the spinlock performance test suite. + * This function creates a test thread with reduced priority to run + * the lock performance tests. The tests measure throughput and + * execution time for different lock types under varying thread counts. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + void spinlock_test(void) { pthread_t tid; pthread_attr_t attr; struct sched_param sparam; + /* Initialize thread attributes */ + ASSERT(0 == pthread_attr_init(&attr)); + /* Set thread priority to default - 1 */ + ASSERT(0 == sched_getparam(getpid(), &sparam)); sparam.sched_priority = SCHED_PRIORITY_MAX; ASSERT(0 == pthread_attr_setschedparam(&attr, &sparam)); printf("spinlock_test: start......\n"); + /* Create and run the test thread */ + ASSERT(0 == pthread_create(&tid, &attr, spinlock_test_thread, NULL)); ASSERT(0 == pthread_join(tid, NULL)); From b92e572f2ae17f0e9a69d4f3be5d9a4e1d7a673b Mon Sep 17 00:00:00 2001 From: tengshuangshuang Date: Wed, 1 Apr 2026 21:45:03 +0800 Subject: [PATCH 3/6] ostest/spinlock: Add Kconfig option for test thread priority The spinlock test threads were created with SCHED_PRIORITY_MAX (255), which starves the watchdog feeding task (BSW_TASK_100MS, priority 191) on BMP systems with WdgM enabled. The WdgM satellite alive counter expires on secondary cores, triggering a WarmReset via SafetyErrMgr and powering off the system through semihosting exit. Add CONFIG_TESTING_OSTEST_SPINLOCK_PRIORITY Kconfig option (default 255) to allow platforms with watchdog monitoring to configure a lower priority. Use CONFIG_TESTING_OSTEST_SPINLOCK_PRIORITY in both spinlock_test() and run_test_thread() instead of hardcoded priority values. Assisted-By: mimo-v2.6-pro Signed-off-by: tengshuangshuang --- testing/ostest/Kconfig | 11 +++++++++++ testing/ostest/spinlock.c | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/testing/ostest/Kconfig b/testing/ostest/Kconfig index c714d179d43..73ec752bff7 100644 --- a/testing/ostest/Kconfig +++ b/testing/ostest/Kconfig @@ -127,6 +127,17 @@ config TESTING_OSTEST_SPINLOCK_THREADS default 2 range 1 32 +config TESTING_OSTEST_SPINLOCK_PRIORITY + int "Priority of spinlock test threads" + default 255 + range 1 255 + ---help--- + Priority for spinlock test threads. Default is 255 + (SCHED_PRIORITY_MAX). On systems with watchdog monitoring + (e.g., BMP with WdgM), using 255 may starve the watchdog + feeding task and trigger a system reset. Set this below + the watchdog task priority if needed. + config TESTING_OSTEST_MULTIUSER bool "Multi-user identity and permission tests" default n diff --git a/testing/ostest/spinlock.c b/testing/ostest/spinlock.c index 2dec05bde8d..af3f1851638 100644 --- a/testing/ostest/spinlock.c +++ b/testing/ostest/spinlock.c @@ -251,7 +251,7 @@ void run_test_thread(void *lock, FAR void *(*thread_func)(FAR void *arg), ASSERT(false); } - sparam.sched_priority = SCHED_PRIORITY_MAX; + sparam.sched_priority = CONFIG_TESTING_OSTEST_SPINLOCK_PRIORITY; status = pthread_attr_setschedparam(&attr, &sparam); if (status != OK) { @@ -438,7 +438,7 @@ void spinlock_test(void) /* Set thread priority to default - 1 */ ASSERT(0 == sched_getparam(getpid(), &sparam)); - sparam.sched_priority = SCHED_PRIORITY_MAX; + sparam.sched_priority = CONFIG_TESTING_OSTEST_SPINLOCK_PRIORITY; ASSERT(0 == pthread_attr_setschedparam(&attr, &sparam)); printf("spinlock_test: start......\n"); From 6af34fa79419fc40886c430ae611f63bbc1a5075 Mon Sep 17 00:00:00 2001 From: ouyangxiangzhen Date: Tue, 28 Apr 2026 14:22:55 +0800 Subject: [PATCH 4/6] testing/ostest: fix spinlock test loop count to match Kconfig description The LOOP_TIMES macro used CONFIG_TEST_LOOP_SCALE * 100000, but the Kconfig description states "N x 10,000". With the default scale of 100, this resulted in 10,000,000 iterations per lock type instead of the intended 1,000,000, causing ostest to exceed its 800-second timeout on QEMU targets with KASAN instrumentation enabled. Align the multiplier with the Kconfig description by changing 100000 to 10000. Assisted-By: mimo-v2.6-pro Signed-off-by: ouyangxiangzhen --- testing/ostest/spinlock.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testing/ostest/spinlock.c b/testing/ostest/spinlock.c index af3f1851638..0cbb1fd74d4 100644 --- a/testing/ostest/spinlock.c +++ b/testing/ostest/spinlock.c @@ -44,8 +44,7 @@ /* Number of lock/unlock iterations per thread */ - -#define LOOP_TIMES (CONFIG_TEST_LOOP_SCALE * 100000) +#define LOOP_TIMES (CONFIG_TEST_LOOP_SCALE * 10000) /**************************************************************************** * Type Definitions From c17841d6a2a6d57130c4390747497aec1ebad72f Mon Sep 17 00:00:00 2001 From: ouyangxiangzhen Date: Tue, 28 Apr 2026 13:27:09 +0800 Subject: [PATCH 5/6] testing/ostest: lower spinlock loop scale under MM_KASAN The spinlock/rspinlock/seqcount performance test executes LOOP_TIMES = CONFIG_TEST_LOOP_SCALE * 100000 lock/unlock pairs per thread per lock type. With the default scale of 100 that is 10M operations per lock type, or 30M in total. When CONFIG_MM_KASAN is enabled every spin_lock_irqsave goes through kasan_check_report, which itself takes the global kasan spinlock (g_lock). On single-core QEMU/emulator builds (e.g. goldfish-miwear with CONFIG_SMP_NCPUS=1 + CONFIG_MM_KASAN_GENERIC=y) this serializes each op through one extra spin + atomic RMW. A live GDB snapshot on goldfish-miwear measured ~10K ops/sec, putting the full spinlock_test well past any sensible test-runner timeout and causing ostest to miss the "Exiting with status" pattern. Add a MM_KASAN-conditional default of 5 (= 500K ops, ~150s total for the three lock types on the same board) so instrumented builds finish in time, while non-KASAN builds keep the original 100 scale and retain meaningful throughput numbers. Users can still override via menuconfig/defconfig when needed. Root cause: KASAN per-op overhead made the default LOOP_TIMES infeasible on instrumented QEMU/emulator builds. Fix: add `default 5 if MM_KASAN` to CONFIG_TEST_LOOP_SCALE and document the rationale in ---help---. Assisted-By: mimo-v2.6-pro Signed-off-by: ouyangxiangzhen --- testing/ostest/Kconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/testing/ostest/Kconfig b/testing/ostest/Kconfig index 73ec752bff7..1b0d067b66c 100644 --- a/testing/ostest/Kconfig +++ b/testing/ostest/Kconfig @@ -155,6 +155,16 @@ config TESTING_OSTEST_MULTIUSER config TEST_LOOP_SCALE int "Loop scale of spinlock test (N x 10,000)" + default 5 if MM_KASAN default 100 + ---help--- + Number of iterations per spinlock test thread, expressed as + scale * 100000 (see LOOP_TIMES in spinlock.c). When KASAN is + enabled the per-lock kasan_check_report overhead serializes + through the global kasan spinlock, slowing each op by orders + of magnitude on single-core QEMU/emulator builds. Use a much + smaller default under MM_KASAN so the test completes within + test-runner timeouts. Non-KASAN builds keep the original 100 + scale for meaningful throughput measurement. endif # TESTING_OSTEST From bd9629357fb38c66855c68bb3206675ec721fd4c Mon Sep 17 00:00:00 2001 From: zhangyuan29 Date: Fri, 8 May 2026 20:45:03 +0800 Subject: [PATCH 6/6] testing/ostest: resolve Tasking warning W597 alignment Remove aligned_data(64) from stack-local union since spinlock_t/ rspinlock_t/seqcount_t are all <= 8 bytes (power-of-2), naturally fitting within a single cache line without explicit alignment. Warning: ctc W597: alignment of automatic object limited to 4 File: apps/testing/ostest/spinlock.c Line: 365 Assisted-By: mimo-v2.6-pro Signed-off-by: zhangyuan29 --- testing/ostest/spinlock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/ostest/spinlock.c b/testing/ostest/spinlock.c index 0cbb1fd74d4..e4da0e4cbc8 100644 --- a/testing/ostest/spinlock.c +++ b/testing/ostest/spinlock.c @@ -353,7 +353,7 @@ void run_test_thread(void *lock, FAR void *(*thread_func)(FAR void *arg), static void spinlock_test_thread_num(unsigned thread_num) { - aligned_data(64) union + union { spinlock_t spinlock; rspinlock_t rspinlock;