[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