diff options
-rw-r--r-- | cpukit/score/Makefile.am | 1 | ||||
-rw-r--r-- | cpukit/score/include/rtems/score/smplockmcs.h | 262 | ||||
-rw-r--r-- | cpukit/score/preinstall.am | 4 | ||||
-rw-r--r-- | testsuites/smptests/smplock01/init.c | 173 |
4 files changed, 426 insertions, 14 deletions
diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am index 80eafc4085..ac2f0b5cce 100644 --- a/cpukit/score/Makefile.am +++ b/cpukit/score/Makefile.am @@ -129,6 +129,7 @@ include_rtems_score_HEADERS += include/rtems/score/schedulerprioritysmpimpl.h include_rtems_score_HEADERS += include/rtems/score/schedulerpriorityaffinitysmp.h include_rtems_score_HEADERS += include/rtems/score/schedulersimplesmp.h include_rtems_score_HEADERS += include/rtems/score/schedulerstrongapa.h +include_rtems_score_HEADERS += include/rtems/score/smplockmcs.h include_rtems_score_HEADERS += include/rtems/score/smplockstats.h include_rtems_score_HEADERS += include/rtems/score/smplockticket.h endif diff --git a/cpukit/score/include/rtems/score/smplockmcs.h b/cpukit/score/include/rtems/score/smplockmcs.h new file mode 100644 index 0000000000..5a1ad23dc9 --- /dev/null +++ b/cpukit/score/include/rtems/score/smplockmcs.h @@ -0,0 +1,262 @@ +/** + * @file + * + * @ingroup ScoreSMPLock + * + * @brief SMP Lock API + */ + +/* + * Copyright (c) 2016 embedded brains GmbH + * + * 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. + */ + +#ifndef _RTEMS_SCORE_SMPLOCKMCS_H +#define _RTEMS_SCORE_SMPLOCKMCS_H + +#include <rtems/score/cpuopts.h> + +#if defined(RTEMS_SMP) + +#include <rtems/score/atomic.h> +#include <rtems/score/smplockstats.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @addtogroup ScoreSMPLock + * + * @{ + */ + +/** + * @brief SMP Mellor-Crummey and Scott (MCS) lock context. + */ +typedef struct SMP_MCS_lock_Context { + /** + * @brief The next context on the queue if it exists. + */ + union { + /** + * @brief The next context as an atomic unsigned integer pointer value. + */ + Atomic_Uintptr atomic; + + /** + * @brief The next context as a normal pointer. + * + * Only provided for debugging purposes. + */ + struct SMP_MCS_lock_Context *normal; + } next; + + /** + * @brief Indicates if the lock is owned or free in case a previous context + * exits on the queue. + * + * This field is initialized to a non-zero value. The previous lock owner + * (which is the owner of the previous context) will set it to zero during + * its lock release. + */ + Atomic_Uint locked; + +#if defined(RTEMS_PROFILING) + SMP_lock_Stats_context Stats_context; + + unsigned int queue_length; +#endif +} SMP_MCS_lock_Context; + +/** + * @brief SMP Mellor-Crummey and Scott (MCS) lock control. + */ +typedef struct { + /** + * @brief The queue tail context. + * + * The lock is free, in case this field is zero, otherwise it is locked by + * the owner of the queue head. + */ + union { + /** + * @brief The queue tail context as an atomic unsigned integer pointer + * value. + */ + Atomic_Uintptr atomic; + + /** + * @brief The queue tail context as a normal pointer. + * + * Only provided for debugging purposes. + */ + struct SMP_MCS_lock_Context *normal; + } queue; +} SMP_MCS_lock_Control; + +/** + * @brief SMP MCS lock control initializer for static initialization. + */ +#define SMP_MCS_LOCK_INITIALIZER { { ATOMIC_INITIALIZER_UINTPTR( 0 ) } } + +/** + * @brief Initializes an SMP MCS lock. + * + * Concurrent initialization leads to unpredictable results. + * + * @param lock The SMP MCS lock control. + */ +static inline void _SMP_MCS_lock_Initialize( SMP_MCS_lock_Control *lock ) +{ + _Atomic_Init_uintptr( &lock->queue.atomic, 0 ); +} + +/** + * @brief Destroys an SMP MCS lock. + * + * Concurrent destruction leads to unpredictable results. + * + * @param lock The SMP MCS lock control. + */ +static inline void _SMP_MCS_lock_Destroy( SMP_MCS_lock_Control *lock ) +{ + (void) lock; +} + +static inline void _SMP_MCS_lock_Do_acquire( + SMP_MCS_lock_Control *lock, + SMP_MCS_lock_Context *context +#if defined(RTEMS_PROFILING) + , + SMP_lock_Stats *stats +#endif +) +{ + SMP_MCS_lock_Context *previous; +#if defined(RTEMS_PROFILING) + SMP_lock_Stats_acquire_context acquire_context; + + _SMP_lock_Stats_acquire_begin( &acquire_context ); + context->queue_length = 0; +#endif + + _Atomic_Store_uintptr( &context->next.atomic, 0, ATOMIC_ORDER_RELAXED ); + _Atomic_Store_uint( &context->locked, 1, ATOMIC_ORDER_RELAXED ); + + previous = (SMP_MCS_lock_Context *) _Atomic_Exchange_uintptr( + &lock->queue.atomic, + (uintptr_t) context, + ATOMIC_ORDER_ACQ_REL + ); + + if ( previous != NULL ) { + unsigned int locked; + + _Atomic_Store_uintptr( + &previous->next.atomic, + (uintptr_t) context, + ATOMIC_ORDER_RELAXED + ); + + do { + locked = _Atomic_Load_uint( &context->locked, ATOMIC_ORDER_ACQUIRE ); + } while ( locked != 0 ); + } + +#if defined(RTEMS_PROFILING) + _SMP_lock_Stats_acquire_end( + &acquire_context, + stats, + &context->Stats_context, + context->queue_length + ); +#endif +} + +/** + * @brief Acquires an SMP MCS lock. + * + * This function will not disable interrupts. The caller must ensure that the + * current thread of execution is not interrupted indefinite once it obtained + * the SMP MCS lock. + * + * @param lock The SMP MCS lock control. + * @param context The SMP MCS lock context. + * @param stats The SMP lock statistics. + */ +#if defined(RTEMS_PROFILING) + #define _SMP_MCS_lock_Acquire( lock, context, stats ) \ + _SMP_MCS_lock_Do_acquire( lock, context, stats ) +#else + #define _SMP_MCS_lock_Acquire( lock, context, stats ) \ + _SMP_MCS_lock_Do_acquire( lock, context ) +#endif + +/** + * @brief Releases an SMP MCS lock. + * + * @param lock The SMP MCS lock control. + * @param context The SMP MCS lock context. + */ +static inline void _SMP_MCS_lock_Release( + SMP_MCS_lock_Control *lock, + SMP_MCS_lock_Context *context +) +{ + SMP_MCS_lock_Context *next; + + next = (SMP_MCS_lock_Context *) _Atomic_Load_uintptr( + &context->next.atomic, + ATOMIC_ORDER_RELAXED + ); + + if ( next == NULL ) { + uintptr_t expected; + bool success; + + expected = (uintptr_t) context; + success = _Atomic_Compare_exchange_uintptr( + &lock->queue.atomic, + &expected, + 0, + ATOMIC_ORDER_RELEASE, + ATOMIC_ORDER_RELAXED + ); + + if ( success ) { +#if defined(RTEMS_PROFILING) + _SMP_lock_Stats_release_update( &context->Stats_context ); +#endif + /* Nobody waits. So, we are done */ + return; + } + + do { + next = (SMP_MCS_lock_Context *) _Atomic_Load_uintptr( + &context->next.atomic, + ATOMIC_ORDER_RELAXED + ); + } while ( next == NULL ); + } + +#if defined(RTEMS_PROFILING) + next->queue_length = context->queue_length + 1; + _SMP_lock_Stats_release_update( &context->Stats_context ); +#endif + + _Atomic_Store_uint( &next->locked, 0, ATOMIC_ORDER_RELEASE ); +} + +/**@}*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* RTEMS_SMP */ + +#endif /* _RTEMS_SCORE_SMPLOCKMCS_H */ diff --git a/cpukit/score/preinstall.am b/cpukit/score/preinstall.am index 2e89f7a981..94851407d4 100644 --- a/cpukit/score/preinstall.am +++ b/cpukit/score/preinstall.am @@ -451,6 +451,10 @@ $(PROJECT_INCLUDE)/rtems/score/schedulerstrongapa.h: include/rtems/score/schedul $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/schedulerstrongapa.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/schedulerstrongapa.h +$(PROJECT_INCLUDE)/rtems/score/smplockmcs.h: include/rtems/score/smplockmcs.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/smplockmcs.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/smplockmcs.h + $(PROJECT_INCLUDE)/rtems/score/smplockstats.h: include/rtems/score/smplockstats.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/smplockstats.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/smplockstats.h diff --git a/testsuites/smptests/smplock01/init.c b/testsuites/smptests/smplock01/init.c index 8cc10fac6e..5eeb1ca347 100644 --- a/testsuites/smptests/smplock01/init.c +++ b/testsuites/smptests/smplock01/init.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 embedded brains GmbH. All rights reserved. + * Copyright (c) 2013, 2016 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 @@ -17,6 +17,7 @@ #endif #include <rtems/score/smplock.h> +#include <rtems/score/smplockmcs.h> #include <rtems/score/smpbarrier.h> #include <rtems/score/atomic.h> #include <rtems.h> @@ -29,7 +30,7 @@ const char rtems_test_name[] = "SMPLOCK 1"; #define CPU_COUNT 32 -#define TEST_COUNT 5 +#define TEST_COUNT 10 typedef enum { INITIAL, @@ -45,20 +46,33 @@ typedef struct { unsigned long counter[TEST_COUNT]; unsigned long test_counter[TEST_COUNT][CPU_COUNT]; SMP_lock_Control lock; +#if defined(RTEMS_PROFILING) + SMP_lock_Stats mcs_stats; +#endif + SMP_MCS_lock_Control mcs_lock; } global_context; static global_context context = { .state = ATOMIC_INITIALIZER_UINT(INITIAL), .barrier = SMP_BARRIER_CONTROL_INITIALIZER, - .lock = SMP_LOCK_INITIALIZER("global") + .lock = SMP_LOCK_INITIALIZER("global ticket"), +#if defined(RTEMS_PROFILING) + .mcs_stats = SMP_LOCK_STATS_INITIALIZER("global MCS"), +#endif + .mcs_lock = SMP_MCS_LOCK_INITIALIZER }; -static const char *test_names[TEST_COUNT] = { - "aquire global lock with local counter", - "aquire global lock with global counter", - "aquire local lock with local counter", - "aquire local lock with global counter", - "aquire global lock with busy section" +static const char * const test_names[TEST_COUNT] = { + "global ticket lock with local counter", + "global MCS lock with local counter", + "global ticket lock with global counter", + "global MCS lock with global counter", + "local ticket lock with local counter", + "local MCS lock with local counter", + "local ticket lock with global counter", + "local MCS lock with global counter", + "global ticket lock with busy section", + "global MCS lock with busy section" }; static void stop_test_timer(rtems_id timer_id, void *arg) @@ -119,6 +133,26 @@ static void test_1_body( ) { unsigned long counter = 0; + SMP_MCS_lock_Context lock_context; + + while (assert_state(ctx, START_TEST)) { + _SMP_MCS_lock_Acquire(&ctx->mcs_lock, &lock_context, &ctx->mcs_stats); + _SMP_MCS_lock_Release(&ctx->mcs_lock, &lock_context); + ++counter; + } + + ctx->test_counter[test][cpu_self] = counter; +} + +static void test_2_body( + int test, + global_context *ctx, + SMP_barrier_State *bs, + unsigned int cpu_count, + unsigned int cpu_self +) +{ + unsigned long counter = 0; SMP_lock_Context lock_context; while (assert_state(ctx, START_TEST)) { @@ -131,7 +165,28 @@ static void test_1_body( ctx->test_counter[test][cpu_self] = counter; } -static void test_2_body( +static void test_3_body( + int test, + global_context *ctx, + SMP_barrier_State *bs, + unsigned int cpu_count, + unsigned int cpu_self +) +{ + unsigned long counter = 0; + SMP_MCS_lock_Context lock_context; + + while (assert_state(ctx, START_TEST)) { + _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->test_counter[test][cpu_self] = counter; +} + +static void test_4_body( int test, global_context *ctx, SMP_barrier_State *bs, @@ -156,7 +211,37 @@ static void test_2_body( ctx->test_counter[test][cpu_self] = counter; } -static void test_3_body( +static void test_5_body( + int test, + global_context *ctx, + SMP_barrier_State *bs, + unsigned int cpu_count, + unsigned int cpu_self +) +{ + 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 (assert_state(ctx, START_TEST)) { + _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->test_counter[test][cpu_self] = counter; +} + +static void test_6_body( int test, global_context *ctx, SMP_barrier_State *bs, @@ -185,6 +270,40 @@ static void test_3_body( ctx->test_counter[test][cpu_self] = counter; } +static void test_7_body( + int test, + global_context *ctx, + SMP_barrier_State *bs, + unsigned int cpu_count, + unsigned int cpu_self +) +{ + 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 (assert_state(ctx, START_TEST)) { + _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->test_counter[test][cpu_self] = counter; +} + static void busy_section(void) { int i; @@ -194,7 +313,7 @@ static void busy_section(void) } } -static void test_4_body( +static void test_8_body( int test, global_context *ctx, SMP_barrier_State *bs, @@ -215,12 +334,38 @@ static void test_4_body( ctx->test_counter[test][cpu_self] = counter; } +static void test_9_body( + int test, + global_context *ctx, + SMP_barrier_State *bs, + unsigned int cpu_count, + unsigned int cpu_self +) +{ + unsigned long counter = 0; + SMP_MCS_lock_Context lock_context; + + while (assert_state(ctx, START_TEST)) { + _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->test_counter[test][cpu_self] = counter; +} + static const test_body test_bodies[TEST_COUNT] = { test_0_body, test_1_body, test_2_body, test_3_body, - test_4_body + test_4_body, + test_5_body, + test_6_body, + test_7_body, + test_8_body, + test_9_body }; static void run_tests( @@ -299,7 +444,7 @@ static void test(void) } } - ctx->timeout = 10 * rtems_clock_get_ticks_per_second(); + ctx->timeout = 5 * rtems_clock_get_ticks_per_second(); sc = rtems_timer_create(rtems_build_name('T', 'I', 'M', 'R'), &ctx->timer_id); rtems_test_assert(sc == RTEMS_SUCCESSFUL); |