[PATCH] smp: Fix timeout for MrsP semaphores
Gedare Bloom
gedare at rtems.org
Wed Dec 17 15:26:43 UTC 2014
This also changes the synchronization of the MRSP_Rival::status to use
interrupt critical section for writing instead of atomic operations. I
guess it is ok?
-Gedare
On Wed, Dec 17, 2014 at 10:14 AM, Sebastian Huber
<sebastian.huber at embedded-brains.de> wrote:
> The previous timeout handling was flawed. In case a waiting thread
> helped out the owner could use the scheduler node indefinitely long.
> Update the resource tree in _MRSP_Timeout() to avoid this issue.
>
> Bug reported by Luca Bonato.
> ---
> cpukit/score/include/rtems/score/mrsp.h | 35 ++++++--
> cpukit/score/include/rtems/score/mrspimpl.h | 101 +++++++++++------------
> testsuites/smptests/smpmrsp01/init.c | 120 ++++++++++++++++++++--------
> 3 files changed, 167 insertions(+), 89 deletions(-)
>
> diff --git a/cpukit/score/include/rtems/score/mrsp.h b/cpukit/score/include/rtems/score/mrsp.h
> index c31d5f6..589ee9d 100644
> --- a/cpukit/score/include/rtems/score/mrsp.h
> +++ b/cpukit/score/include/rtems/score/mrsp.h
> @@ -19,8 +19,8 @@
>
> #if defined(RTEMS_SMP)
>
> -#include <rtems/score/atomic.h>
> #include <rtems/score/chain.h>
> +#include <rtems/score/scheduler.h>
> #include <rtems/score/thread.h>
>
> #ifdef __cplusplus
> @@ -66,7 +66,13 @@ typedef enum {
> MRSP_INCORRECT_STATE = 14,
> MRSP_INVALID_PRIORITY = 19,
> MRSP_NOT_OWNER_OF_RESOURCE = 23,
> - MRSP_NO_MEMORY = 26
> + MRSP_NO_MEMORY = 26,
> +
> + /**
> + * @brief Internal state used for MRSP_Rival::status to indicate that this
> + * rival waits for resource ownership.
> + */
> + MRSP_WAIT_FOR_OWNERSHIP = 255
> } MRSP_Status;
>
> /**
> @@ -89,13 +95,28 @@ typedef struct {
> Thread_Control *thread;
>
> /**
> - * @brief The rival state.
> + * @brief The initial priority of the thread.
> + *
> + * Used to restore the priority after a release of this resource or timeout.
> + */
> + Priority_Control initial_priority;
> +
> + /**
> + * @brief The previous help state of the thread.
> + *
> + * Used to restore this state after a timeout.
> + */
> + Scheduler_Help_state previous_help_state;
> +
> + /**
> + * @brief The rival status.
> *
> - * Initially no state bits are set (MRSP_RIVAL_STATE_WAITING). The rival
> - * will busy wait until a state change happens. This can be
> - * MRSP_RIVAL_STATE_NEW_OWNER or MRSP_RIVAL_STATE_TIMEOUT.
> + * Initially the status is set to MRSP_WAIT_FOR_OWNERSHIP. The rival will
> + * busy wait until a status change happens. This can be MRSP_SUCCESSFUL or
> + * MRSP_TIMEOUT. State changes are protected by the Giant lock and disabled
> + * interrupts.
> */
> - Atomic_Uint state;
> + volatile MRSP_Status status;
> } MRSP_Rival;
>
> /**
> diff --git a/cpukit/score/include/rtems/score/mrspimpl.h b/cpukit/score/include/rtems/score/mrspimpl.h
> index 1571594..57121de 100644
> --- a/cpukit/score/include/rtems/score/mrspimpl.h
> +++ b/cpukit/score/include/rtems/score/mrspimpl.h
> @@ -36,12 +36,6 @@ extern "C" {
> * @{
> */
>
> -#define MRSP_RIVAL_STATE_WAITING 0x0U
> -
> -#define MRSP_RIVAL_STATE_NEW_OWNER 0x1U
> -
> -#define MRSP_RIVAL_STATE_TIMEOUT 0x2U
> -
> RTEMS_INLINE_ROUTINE void _MRSP_Elevate_priority(
> MRSP_Control *mrsp,
> Thread_Control *new_owner,
> @@ -52,9 +46,8 @@ RTEMS_INLINE_ROUTINE void _MRSP_Elevate_priority(
> }
>
> RTEMS_INLINE_ROUTINE void _MRSP_Restore_priority(
> - const MRSP_Control *mrsp,
> - Thread_Control *thread,
> - Priority_Control initial_priority
> + Thread_Control *thread,
> + Priority_Control initial_priority
> )
> {
> /*
> @@ -134,24 +127,34 @@ RTEMS_INLINE_ROUTINE void _MRSP_Set_ceiling_priority(
> mrsp->ceiling_priorities[ scheduler_index ] = ceiling_priority;
> }
>
> -RTEMS_INLINE_ROUTINE void _MRSP_Add_state(
> - MRSP_Rival *rival,
> - unsigned int state
> -)
> -{
> - _Atomic_Fetch_or_uint( &rival->state, state, ATOMIC_ORDER_RELEASE );
> -}
> -
> RTEMS_INLINE_ROUTINE void _MRSP_Timeout(
> Objects_Id id,
> void *arg
> )
> {
> MRSP_Rival *rival = arg;
> + Thread_Control *thread = rival->thread;
> + ISR_Level level;
>
> (void) id;
>
> - _MRSP_Add_state( rival, MRSP_RIVAL_STATE_TIMEOUT );
> + _ISR_Disable( level );
> +
> + if ( rival->status == MRSP_WAIT_FOR_OWNERSHIP ) {
> + _Chain_Extract_unprotected( &rival->Node );
> +
> + _ISR_Enable( level );
> +
> + rival->status = MRSP_TIMEOUT;
> +
> + _Resource_Node_extract( &thread->Resource_node );
> + _Resource_Node_set_dependency( &thread->Resource_node, NULL );
> + _Scheduler_Thread_change_help_state( thread, rival->previous_help_state );
> + _Scheduler_Thread_change_resource_root( thread, thread );
> + _MRSP_Restore_priority( thread, rival->initial_priority );
> + } else {
> + _ISR_Enable( level );
> + }
> }
>
> RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Wait_for_ownership(
> @@ -166,18 +169,18 @@ RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Wait_for_ownership(
> MRSP_Status status;
> MRSP_Rival rival;
> bool previous_life_protection;
> - unsigned int state;
> - Scheduler_Help_state previous_help_state;
> +
> + rival.thread = executing;
> + rival.initial_priority = initial_priority;
> + rival.previous_help_state =
> + _Scheduler_Thread_change_help_state( executing, SCHEDULER_HELP_ACTIVE_RIVAL );
> + rival.status = MRSP_WAIT_FOR_OWNERSHIP;
>
> _MRSP_Elevate_priority( mrsp, executing, ceiling_priority );
>
> - rival.thread = executing;
> - _Atomic_Init_uint( &rival.state, MRSP_RIVAL_STATE_WAITING );
> _Chain_Append_unprotected( &mrsp->Rivals, &rival.Node );
> _Resource_Add_rival( &mrsp->Resource, &executing->Resource_node );
> _Resource_Node_set_dependency( &executing->Resource_node, &mrsp->Resource );
> - previous_help_state =
> - _Scheduler_Thread_change_help_state( executing, SCHEDULER_HELP_ACTIVE_RIVAL );
>
> _Scheduler_Thread_change_resource_root(
> executing,
> @@ -199,12 +202,10 @@ RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Wait_for_ownership(
>
> _Assert( _Debug_Is_thread_dispatching_allowed() );
>
> - while (
> - _Atomic_Load_uint( &rival.state, ATOMIC_ORDER_ACQUIRE )
> - == MRSP_RIVAL_STATE_WAITING
> - ) {
> - /* Wait for state change */
> - }
> + /* Wait for state change */
> + do {
> + status = rival.status;
> + } while ( status == MRSP_WAIT_FOR_OWNERSHIP );
>
> _Thread_Disable_dispatch();
> _Thread_Set_life_protection( previous_life_protection );
> @@ -213,22 +214,6 @@ RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Wait_for_ownership(
> _Watchdog_Remove( &executing->Timer );
> }
>
> - _Chain_Extract_unprotected( &rival.Node );
> - state = _Atomic_Load_uint( &rival.state, ATOMIC_ORDER_RELAXED );
> -
> - if ( ( state & MRSP_RIVAL_STATE_NEW_OWNER ) != 0 ) {
> - mrsp->initial_priority_of_owner = initial_priority;
> - status = MRSP_SUCCESSFUL;
> - } else {
> - _Resource_Node_extract( &executing->Resource_node );
> - _Resource_Node_set_dependency( &executing->Resource_node, NULL );
> - _Scheduler_Thread_change_help_state( executing, previous_help_state );
> - _Scheduler_Thread_change_resource_root( executing, executing );
> - _MRSP_Restore_priority( mrsp, executing, initial_priority );
> -
> - status = MRSP_TIMEOUT;
> - }
> -
> return status;
> }
>
> @@ -289,6 +274,8 @@ RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Release(
> Thread_Control *executing
> )
> {
> + ISR_Level level;
> +
> if ( _Resource_Get_owner( &mrsp->Resource ) != &executing->Resource_node ) {
> return MRSP_NOT_OWNER_OF_RESOURCE;
> }
> @@ -303,21 +290,35 @@ RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Release(
> }
>
> _Resource_Extract( &mrsp->Resource );
> - _MRSP_Restore_priority( mrsp, executing, mrsp->initial_priority_of_owner );
> + _MRSP_Restore_priority( executing, mrsp->initial_priority_of_owner );
> +
> + _ISR_Disable( level );
>
> if ( _Chain_Is_empty( &mrsp->Rivals ) ) {
> + _ISR_Enable( level );
> +
> _Resource_Set_owner( &mrsp->Resource, NULL );
> } else {
> - MRSP_Rival *rival = (MRSP_Rival *) _Chain_First( &mrsp->Rivals );
> - Thread_Control *new_owner = rival->thread;
> + MRSP_Rival *rival = (MRSP_Rival *)
> + _Chain_Get_first_unprotected( &mrsp->Rivals );
> + Thread_Control *new_owner;
> +
> + /*
> + * This must be inside the critical section since the status prevents a
> + * potential double extraction in _MRSP_Timeout().
> + */
> + rival->status = MRSP_SUCCESSFUL;
> +
> + _ISR_Enable( level );
>
> + new_owner = rival->thread;
> + mrsp->initial_priority_of_owner = rival->initial_priority;
> _Resource_Node_extract( &new_owner->Resource_node );
> _Resource_Node_set_dependency( &new_owner->Resource_node, NULL );
> _Resource_Node_add_resource( &new_owner->Resource_node, &mrsp->Resource );
> _Resource_Set_owner( &mrsp->Resource, &new_owner->Resource_node );
> _Scheduler_Thread_change_help_state( new_owner, SCHEDULER_HELP_ACTIVE_OWNER );
> _Scheduler_Thread_change_resource_root( new_owner, new_owner );
> - _MRSP_Add_state( rival, MRSP_RIVAL_STATE_NEW_OWNER );
> }
>
> if ( !_Resource_Node_owns_resources( &executing->Resource_node ) ) {
> diff --git a/testsuites/smptests/smpmrsp01/init.c b/testsuites/smptests/smpmrsp01/init.c
> index 71aa844..224cea3 100644
> --- a/testsuites/smptests/smpmrsp01/init.c
> +++ b/testsuites/smptests/smpmrsp01/init.c
> @@ -208,6 +208,15 @@ static void print_switch_events(test_context *ctx)
> }
> }
>
> +static void run_task(rtems_task_argument arg)
> +{
> + volatile bool *run = (volatile bool *) arg;
> +
> + while (true) {
> + *run = true;
> + }
> +}
> +
> static void obtain_and_release_worker(rtems_task_argument arg)
> {
> test_context *ctx = &test_instance;
> @@ -216,7 +225,7 @@ static void obtain_and_release_worker(rtems_task_argument arg)
>
> ctx->worker_task = _Thread_Get_executing();
>
> - assert_prio(RTEMS_SELF, 3);
> + assert_prio(RTEMS_SELF, 4);
>
> /* Obtain with timeout (A) */
> barrier(ctx, &barrier_state);
> @@ -224,7 +233,7 @@ static void obtain_and_release_worker(rtems_task_argument arg)
> sc = rtems_semaphore_obtain(ctx->mrsp_ids[0], RTEMS_WAIT, 4);
> rtems_test_assert(sc == RTEMS_TIMEOUT);
>
> - assert_prio(RTEMS_SELF, 3);
> + assert_prio(RTEMS_SELF, 4);
>
> /* Obtain with priority change and timeout (B) */
> barrier(ctx, &barrier_state);
> @@ -232,7 +241,7 @@ static void obtain_and_release_worker(rtems_task_argument arg)
> sc = rtems_semaphore_obtain(ctx->mrsp_ids[0], RTEMS_WAIT, 4);
> rtems_test_assert(sc == RTEMS_TIMEOUT);
>
> - assert_prio(RTEMS_SELF, 1);
> + assert_prio(RTEMS_SELF, 2);
>
> /* Restore priority (C) */
> barrier(ctx, &barrier_state);
> @@ -243,14 +252,25 @@ static void obtain_and_release_worker(rtems_task_argument arg)
> sc = rtems_semaphore_obtain(ctx->mrsp_ids[0], RTEMS_WAIT, RTEMS_NO_TIMEOUT);
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
>
> - assert_prio(RTEMS_SELF, 2);
> + assert_prio(RTEMS_SELF, 3);
>
> sc = rtems_semaphore_release(ctx->mrsp_ids[0]);
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
>
> - assert_prio(RTEMS_SELF, 3);
> + assert_prio(RTEMS_SELF, 4);
>
> - /* Worker done (E) */
> + /* Obtain and help with timeout (E) */
> + barrier(ctx, &barrier_state);
> +
> + sc = rtems_semaphore_obtain(ctx->mrsp_ids[0], RTEMS_WAIT, 4);
> + rtems_test_assert(sc == RTEMS_TIMEOUT);
> +
> + assert_prio(RTEMS_SELF, 4);
> +
> + sc = rtems_task_suspend(ctx->high_task_id[0]);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + /* Worker done (H) */
> barrier(ctx, &barrier_state);
>
> rtems_task_suspend(RTEMS_SELF);
> @@ -266,7 +286,21 @@ static void test_mrsp_obtain_and_release(test_context *ctx)
>
> puts("test MrsP obtain and release");
>
> - change_prio(RTEMS_SELF, 2);
> + change_prio(RTEMS_SELF, 3);
> +
> + reset_switch_events(ctx);
> +
> + ctx->high_run[0] = false;
> +
> + sc = rtems_task_create(
> + rtems_build_name('H', 'I', 'G', '0'),
> + 1,
> + RTEMS_MINIMUM_STACK_SIZE,
> + RTEMS_DEFAULT_MODES,
> + RTEMS_DEFAULT_ATTRIBUTES,
> + &ctx->high_task_id[0]
> + );
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
>
> /* Check executing task parameters */
>
> @@ -282,17 +316,17 @@ static void test_mrsp_obtain_and_release(test_context *ctx)
> 1,
> RTEMS_MULTIPROCESSOR_RESOURCE_SHARING
> | RTEMS_BINARY_SEMAPHORE,
> - 1,
> + 2,
> &ctx->mrsp_ids[0]
> );
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
>
> - assert_prio(RTEMS_SELF, 2);
> + assert_prio(RTEMS_SELF, 3);
>
> sc = rtems_semaphore_obtain(ctx->mrsp_ids[0], RTEMS_WAIT, RTEMS_NO_TIMEOUT);
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
>
> - assert_prio(RTEMS_SELF, 1);
> + assert_prio(RTEMS_SELF, 2);
>
> /*
> * The ceiling priority values per scheduler are equal to the value specified
> @@ -307,11 +341,11 @@ static void test_mrsp_obtain_and_release(test_context *ctx)
> &prio
> );
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> - rtems_test_assert(prio == 1);
> + rtems_test_assert(prio == 2);
>
> /* Check the old value and set a new ceiling priority for scheduler B */
>
> - prio = 2;
> + prio = 3;
> sc = rtems_semaphore_set_priority(
> ctx->mrsp_ids[0],
> ctx->scheduler_ids[1],
> @@ -319,7 +353,7 @@ static void test_mrsp_obtain_and_release(test_context *ctx)
> &prio
> );
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> - rtems_test_assert(prio == 1);
> + rtems_test_assert(prio == 2);
>
> /* Check the ceiling priority values */
>
> @@ -331,7 +365,7 @@ static void test_mrsp_obtain_and_release(test_context *ctx)
> &prio
> );
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> - rtems_test_assert(prio == 1);
> + rtems_test_assert(prio == 2);
>
> prio = RTEMS_CURRENT_PRIORITY;
> sc = rtems_semaphore_set_priority(
> @@ -341,13 +375,13 @@ static void test_mrsp_obtain_and_release(test_context *ctx)
> &prio
> );
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> - rtems_test_assert(prio == 2);
> + rtems_test_assert(prio == 3);
>
> /* Check that a thread waiting to get ownership remains executing */
>
> sc = rtems_task_create(
> rtems_build_name('W', 'O', 'R', 'K'),
> - 3,
> + 4,
> RTEMS_MINIMUM_STACK_SIZE,
> RTEMS_DEFAULT_MODES,
> RTEMS_DEFAULT_ATTRIBUTES,
> @@ -364,37 +398,68 @@ static void test_mrsp_obtain_and_release(test_context *ctx)
> /* Obtain with timeout (A) */
> barrier_and_delay(ctx, &barrier_state);
>
> - assert_prio(ctx->worker_ids[0], 2);
> + assert_prio(ctx->worker_ids[0], 3);
> assert_executing_worker(ctx);
>
> /* Obtain with priority change and timeout (B) */
> barrier_and_delay(ctx, &barrier_state);
>
> - assert_prio(ctx->worker_ids[0], 2);
> - change_prio(ctx->worker_ids[0], 1);
> + assert_prio(ctx->worker_ids[0], 3);
> + change_prio(ctx->worker_ids[0], 2);
> assert_executing_worker(ctx);
>
> /* Restore priority (C) */
> barrier(ctx, &barrier_state);
>
> - assert_prio(ctx->worker_ids[0], 1);
> - change_prio(ctx->worker_ids[0], 3);
> + assert_prio(ctx->worker_ids[0], 2);
> + change_prio(ctx->worker_ids[0], 4);
>
> /* Obtain without timeout (D) */
> barrier_and_delay(ctx, &barrier_state);
>
> - assert_prio(ctx->worker_ids[0], 2);
> + assert_prio(ctx->worker_ids[0], 3);
> assert_executing_worker(ctx);
>
> sc = rtems_semaphore_release(ctx->mrsp_ids[0]);
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
>
> - /* Worker done (E) */
> + /* Check that a timeout works in case the waiting thread actually helps */
> +
> + sc = rtems_semaphore_obtain(ctx->mrsp_ids[0], RTEMS_WAIT, RTEMS_NO_TIMEOUT);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + /* Obtain and help with timeout (E) */
> + barrier_and_delay(ctx, &barrier_state);
> +
> + sc = rtems_task_start(
> + ctx->high_task_id[0],
> + run_task,
> + (rtems_task_argument) &ctx->high_run[0]
> + );
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + rtems_test_assert(rtems_get_current_processor() == 1);
> +
> + while (rtems_get_current_processor() != 0) {
> + /* Wait */
> + }
> +
> + rtems_test_assert(ctx->high_run[0]);
> +
> + sc = rtems_semaphore_release(ctx->mrsp_ids[0]);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + print_switch_events(ctx);
> +
> + /* Worker done (H) */
> barrier(ctx, &barrier_state);
>
> sc = rtems_task_delete(ctx->worker_ids[0]);
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
>
> + sc = rtems_task_delete(ctx->high_task_id[0]);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> sc = rtems_semaphore_delete(ctx->mrsp_ids[0]);
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> }
> @@ -727,15 +792,6 @@ static void test_mrsp_multiple_obtain(void)
> rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> }
>
> -static void run_task(rtems_task_argument arg)
> -{
> - volatile bool *run = (volatile bool *) arg;
> -
> - while (true) {
> - *run = true;
> - }
> -}
> -
> static void ready_unlock_worker(rtems_task_argument arg)
> {
> test_context *ctx = &test_instance;
> --
> 1.8.4.5
>
> _______________________________________________
> devel mailing list
> devel at rtems.org
> http://lists.rtems.org/mailman/listinfo/devel
More information about the devel
mailing list