/* * Copyright (c) 2013, 2016 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 * 82178 Puchheim * Germany * * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "tmacros.h" const char rtems_test_name[] = "SMPLOCK 1"; #define TASK_PRIORITY 1 #define CPU_COUNT 32 #define TEST_COUNT 13 typedef struct { rtems_test_parallel_context base; unsigned long counter[TEST_COUNT]; unsigned long local_counter[CPU_COUNT][TEST_COUNT][CPU_COUNT]; SMP_lock_Control lock RTEMS_ALIGNED(CPU_CACHE_LINE_BYTES); Atomic_Uint flag RTEMS_ALIGNED(CPU_CACHE_LINE_BYTES); SMP_MCS_lock_Control mcs_lock RTEMS_ALIGNED(CPU_CACHE_LINE_BYTES); #if defined(RTEMS_PROFILING) SMP_lock_Stats mcs_stats; #endif SMP_sequence_lock_Control seq_lock RTEMS_ALIGNED(CPU_CACHE_LINE_BYTES); int a RTEMS_ALIGNED(CPU_CACHE_LINE_BYTES); int b RTEMS_ALIGNED(CPU_CACHE_LINE_BYTES); } test_context; static test_context test_instance = { .lock = SMP_LOCK_INITIALIZER("global ticket"), #if defined(RTEMS_PROFILING) .mcs_stats = SMP_LOCK_STATS_INITIALIZER("global MCS"), #endif .flag = ATOMIC_INITIALIZER_UINT(0), .mcs_lock = SMP_MCS_LOCK_INITIALIZER, .seq_lock = SMP_SEQUENCE_LOCK_INITIALIZER }; static rtems_interval test_duration(void) { return rtems_clock_get_ticks_per_second(); } static rtems_interval test_init( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { return test_duration(); } static void test_fini( test_context *ctx, const char *name, size_t test, size_t active_workers ) { unsigned long sum = 0; unsigned long n = active_workers; unsigned long i; printf(" <%s activeWorker=\"%lu\">\n", name, n); for (i = 0; i < n; ++i) { unsigned long local_counter = ctx->local_counter[active_workers - 1][test][i]; sum += local_counter; printf( " %lu\n", i, local_counter ); } printf( " %lu\n" " %lu\n" " \n", ctx->counter[test], sum, name ); } static void test_0_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 0; unsigned long counter = 0; SMP_lock_Context lock_context; while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_lock_Acquire(&ctx->lock, &lock_context); _SMP_lock_Release(&ctx->lock, &lock_context); ++counter; } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_0_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "GlobalTicketLockWithLocalCounter", 0, active_workers ); } static void test_1_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 1; unsigned long counter = 0; SMP_MCS_lock_Context lock_context; while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_MCS_lock_Acquire(&ctx->mcs_lock, &lock_context, &ctx->mcs_stats); _SMP_MCS_lock_Release(&ctx->mcs_lock, &lock_context); ++counter; } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_1_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "GlobalMCSLockWithLocalCounter", 1, active_workers ); } static void test_2_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 2; unsigned long counter = 0; SMP_lock_Context lock_context; while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_lock_Acquire(&ctx->lock, &lock_context); ++ctx->counter[test]; _SMP_lock_Release(&ctx->lock, &lock_context); ++counter; } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_2_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "GlobalTicketLockWithGlobalCounter", 2, active_workers ); } static void test_3_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 3; unsigned long counter = 0; SMP_MCS_lock_Context lock_context; while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_MCS_lock_Acquire(&ctx->mcs_lock, &lock_context, &ctx->mcs_stats); ++ctx->counter[test]; _SMP_MCS_lock_Release(&ctx->mcs_lock, &lock_context); ++counter; } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_3_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "GlobalMCSLockWithGlobalCounter", 3, active_workers ); } static void test_4_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 4; unsigned long counter = 0; SMP_lock_Control lock; SMP_lock_Context lock_context; _SMP_lock_Initialize(&lock, "local"); while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_lock_Acquire(&lock, &lock_context); _SMP_lock_Release(&lock, &lock_context); ++counter; } _SMP_lock_Destroy(&lock); ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_4_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "LocalTicketLockWithLocalCounter", 4, active_workers ); } static void test_5_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 5; unsigned long counter = 0; #if defined(RTEMS_PROFILING) SMP_lock_Stats stats; #endif SMP_MCS_lock_Control lock; SMP_MCS_lock_Context lock_context; _SMP_lock_Stats_initialize(&stats, "local"); _SMP_MCS_lock_Initialize(&lock); while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_MCS_lock_Acquire(&lock, &lock_context, &stats); _SMP_MCS_lock_Release(&lock, &lock_context); ++counter; } _SMP_MCS_lock_Destroy(&lock); _SMP_lock_Stats_destroy(&stats); ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_5_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "LocalMCSLockWithLocalCounter", 5, active_workers ); } static void test_6_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 6; unsigned long counter = 0; SMP_lock_Control lock; SMP_lock_Context lock_context; _SMP_lock_Initialize(&lock, "local"); while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_lock_Acquire(&lock, &lock_context); /* The counter value is not interesting, only the access to it */ ++ctx->counter[test]; _SMP_lock_Release(&lock, &lock_context); ++counter; } _SMP_lock_Destroy(&lock); ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_6_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "LocalTicketLockWithGlobalCounter", 6, active_workers ); } static void test_7_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 7; unsigned long counter = 0; #if defined(RTEMS_PROFILING) SMP_lock_Stats stats; #endif SMP_MCS_lock_Control lock; SMP_MCS_lock_Context lock_context; _SMP_lock_Stats_initialize(&stats, "local"); _SMP_MCS_lock_Initialize(&lock); while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_MCS_lock_Acquire(&lock, &lock_context, &stats); /* The counter value is not interesting, only the access to it */ ++ctx->counter[test]; _SMP_MCS_lock_Release(&lock, &lock_context); ++counter; } _SMP_MCS_lock_Destroy(&lock); _SMP_lock_Stats_destroy(&stats); ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_7_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "LocalMCSLockWithGlobalCounter", 7, active_workers ); } static void busy_section(void) { int i; for (i = 0; i < 101; ++i) { RTEMS_COMPILER_MEMORY_BARRIER(); } } static void test_8_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 8; unsigned long counter = 0; SMP_lock_Context lock_context; while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_lock_Acquire(&ctx->lock, &lock_context); busy_section(); _SMP_lock_Release(&ctx->lock, &lock_context); ++counter; } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_8_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "GlobalTicketLockWithBusySection", 8, active_workers ); } static void test_9_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 9; unsigned long counter = 0; SMP_MCS_lock_Context lock_context; while (!rtems_test_parallel_stop_job(&ctx->base)) { _SMP_MCS_lock_Acquire(&ctx->mcs_lock, &lock_context, &ctx->mcs_stats); busy_section(); _SMP_MCS_lock_Release(&ctx->mcs_lock, &lock_context); ++counter; } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_9_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "GlobalMCSLockWithBusySection", 9, active_workers ); } static void test_10_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 10; unsigned long counter = 0; unsigned long seq; if (rtems_test_parallel_is_master_worker(worker_index)) { while (!rtems_test_parallel_stop_job(&ctx->base)) { seq = _SMP_sequence_lock_Write_begin(&ctx->seq_lock); ctx->a = counter; ctx->b = counter; _SMP_sequence_lock_Write_end(&ctx->seq_lock, seq); ++counter; } } else { while (!rtems_test_parallel_stop_job(&ctx->base)) { unsigned long a; unsigned long b; do { seq = _SMP_sequence_lock_Read_begin(&ctx->seq_lock); a = ctx->a; b = ctx->b; } while (_SMP_sequence_lock_Read_retry(&ctx->seq_lock, seq)); ++counter; rtems_test_assert(a == b); } } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_10_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "SequenceLock", 10, active_workers ); } static void test_11_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 11; unsigned long counter = 0; while (!rtems_test_parallel_stop_job(&ctx->base)) { while (_Atomic_Exchange_uint(&ctx->flag, 1, ATOMIC_ORDER_ACQUIRE) != 0) { /* Wait */ } _Atomic_Store_uint(&ctx->flag, 0, ATOMIC_ORDER_RELEASE); ++counter; } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_11_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "GlobalTASLockWithLocalCounter", 11, active_workers ); } static void test_12_body( rtems_test_parallel_context *base, void *arg, size_t active_workers, size_t worker_index ) { test_context *ctx = (test_context *) base; size_t test = 12; unsigned long counter = 0; while (!rtems_test_parallel_stop_job(&ctx->base)) { while (_Atomic_Exchange_uint(&ctx->flag, 1, ATOMIC_ORDER_ACQUIRE) != 0) { while (_Atomic_Load_uint(&ctx->flag, ATOMIC_ORDER_RELAXED) != 0) { /* Wait */ } } _Atomic_Store_uint(&ctx->flag, 0, ATOMIC_ORDER_RELEASE); ++counter; } ctx->local_counter[active_workers - 1][test][worker_index] = counter; } static void test_12_fini( rtems_test_parallel_context *base, void *arg, size_t active_workers ) { test_context *ctx = (test_context *) base; test_fini( ctx, "GlobalTTASLockWithLocalCounter", 12, active_workers ); } static const rtems_test_parallel_job test_jobs[TEST_COUNT] = { { .init = test_init, .body = test_0_body, .fini = test_0_fini, .cascade = true }, { .init = test_init, .body = test_1_body, .fini = test_1_fini, .cascade = true }, { .init = test_init, .body = test_2_body, .fini = test_2_fini, .cascade = false }, { .init = test_init, .body = test_3_body, .fini = test_3_fini, .cascade = false }, { .init = test_init, .body = test_4_body, .fini = test_4_fini, .cascade = true }, { .init = test_init, .body = test_5_body, .fini = test_5_fini, .cascade = true }, { .init = test_init, .body = test_6_body, .fini = test_6_fini, .cascade = false }, { .init = test_init, .body = test_7_body, .fini = test_7_fini, .cascade = false }, { .init = test_init, .body = test_8_body, .fini = test_8_fini, .cascade = false }, { .init = test_init, .body = test_9_body, .fini = test_9_fini, .cascade = false }, { .init = test_init, .body = test_10_body, .fini = test_10_fini, .cascade = false }, { .init = test_init, .body = test_11_body, .fini = test_11_fini, .cascade = true }, { .init = test_init, .body = test_12_body, .fini = test_12_fini, .cascade = true } }; static void test(void) { test_context *ctx = &test_instance; const char *test = "SMPLock01"; printf("<%s>\n", test); rtems_test_parallel(&ctx->base, NULL, &test_jobs[0], TEST_COUNT); printf("\n", test); } static void Init(rtems_task_argument arg) { TEST_BEGIN(); test(); TEST_END(); rtems_test_exit(0); } #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER #define CONFIGURE_MAXIMUM_PROCESSORS CPU_COUNT #define CONFIGURE_MAXIMUM_TASKS CPU_COUNT #define CONFIGURE_MAXIMUM_SEMAPHORES 1 #define CONFIGURE_MAXIMUM_TIMERS 1 #define CONFIGURE_INIT_TASK_PRIORITY TASK_PRIORITY #define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES #define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_DEFAULT_ATTRIBUTES #define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION #define CONFIGURE_RTEMS_INIT_TASKS_TABLE #define CONFIGURE_INIT #include