[PATCH] smp: Fix timeout for MrsP semaphores
Sebastian Huber
sebastian.huber at embedded-brains.de
Wed Dec 17 15:14:18 UTC 2014
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
More information about the devel
mailing list