[PATCH 4/4] score: Implement priority boosting

Sebastian Huber sebastian.huber at embedded-brains.de
Thu Sep 3 12:01:23 UTC 2015


---
 cpukit/score/include/rtems/score/threadimpl.h  | 31 +++++++++++++
 cpukit/score/include/rtems/score/threadqimpl.h | 25 +++++++++++
 cpukit/score/src/coremutexseize.c              |  2 +-
 cpukit/score/src/coremutexsurrender.c          |  1 +
 cpukit/score/src/mutex.c                       | 15 ++++---
 cpukit/score/src/threadchangepriority.c        | 34 ++++++++++++++
 cpukit/score/src/threadqops.c                  | 22 ++++++++++
 doc/user/sem.t                                 |  5 +++
 doc/user/smp.t                                 |  6 ++-
 testsuites/smptests/smpmutex01/init.c          | 61 +++++++++++++++++++++++---
 10 files changed, 189 insertions(+), 13 deletions(-)

diff --git a/cpukit/score/include/rtems/score/threadimpl.h b/cpukit/score/include/rtems/score/threadimpl.h
index 4656881..1092b65 100644
--- a/cpukit/score/include/rtems/score/threadimpl.h
+++ b/cpukit/score/include/rtems/score/threadimpl.h
@@ -437,6 +437,37 @@ void _Thread_Raise_priority(
 );
 
 /**
+ * @brief Inherit the priority of a thread.
+ *
+ * It changes the current priority of the inheritor thread to the current priority
+ * of the ancestor thread if it is higher than the current priority of the inheritor
+ * thread.  In this case the inheritor thread is appended to its new priority group
+ * in its scheduler instance.
+ *
+ * On SMP configurations, the priority is changed to PRIORITY_PSEUDO_ISR in
+ * case the own schedulers of the inheritor and ancestor thread differ (priority
+ * boosting).
+ *
+ * @param[in] inheritor The thread to inherit the priority.
+ * @param[in] ancestor The thread to bequeath its priority to the inheritor
+ *   thread.
+ */
+#if defined(RTEMS_SMP)
+void _Thread_Inherit_priority(
+  Thread_Control *inheritor,
+  Thread_Control *ancestor
+);
+#else
+RTEMS_INLINE_ROUTINE void _Thread_Inherit_priority(
+  Thread_Control *inheritor,
+  Thread_Control *ancestor
+)
+{
+  _Thread_Raise_priority( inheritor, ancestor->current_priority );
+}
+#endif
+
+/**
  * @brief Sets the current to the real priority of a thread.
  *
  * Sets the priority restore hint to false.
diff --git a/cpukit/score/include/rtems/score/threadqimpl.h b/cpukit/score/include/rtems/score/threadqimpl.h
index bf01eb7..510f886 100644
--- a/cpukit/score/include/rtems/score/threadqimpl.h
+++ b/cpukit/score/include/rtems/score/threadqimpl.h
@@ -522,6 +522,31 @@ RTEMS_INLINE_ROUTINE void _Thread_queue_Destroy(
 }
 
 /**
+ * @brief Boosts the priority of the thread if threads of another scheduler
+ * instance are enqueued on the thread queue.
+ *
+ * The thread queue must use the priority waiting discipline.
+ *
+ * @param[in] queue The actual thread queue.
+ * @param[in] the_thread The thread to boost the priority if necessary.
+ */
+#if defined(RTEMS_SMP)
+void _Thread_queue_Boost_priority(
+  Thread_queue_Queue *queue,
+  Thread_Control     *the_thread
+);
+#else
+RTEMS_INLINE_ROUTINE void _Thread_queue_Boost_priority(
+  Thread_queue_Queue *queue,
+  Thread_Control     *the_thread
+)
+{
+  (void) queue;
+  (void) the_thread;
+}
+#endif
+
+/**
  * @brief Compare two thread's priority for RBTree Insertion.
  *
  * @param[in] left points to the left thread's RBnode
diff --git a/cpukit/score/src/coremutexseize.c b/cpukit/score/src/coremutexseize.c
index ddc5d6b..8059659 100644
--- a/cpukit/score/src/coremutexseize.c
+++ b/cpukit/score/src/coremutexseize.c
@@ -75,7 +75,7 @@ void _CORE_mutex_Seize_interrupt_blocking(
     _Thread_queue_Release( &the_mutex->Wait_queue, lock_context );
 #endif
 
-    _Thread_Raise_priority( holder, executing->current_priority );
+    _Thread_Inherit_priority( holder, executing );
 
 #if !defined(RTEMS_SMP)
     _Thread_queue_Acquire( &the_mutex->Wait_queue, lock_context );
diff --git a/cpukit/score/src/coremutexsurrender.c b/cpukit/score/src/coremutexsurrender.c
index d3f965d..7d9c57f 100644
--- a/cpukit/score/src/coremutexsurrender.c
+++ b/cpukit/score/src/coremutexsurrender.c
@@ -209,6 +209,7 @@ CORE_mutex_Status _CORE_mutex_Surrender(
         case CORE_MUTEX_DISCIPLINES_PRIORITY_INHERIT:
           _CORE_mutex_Push_priority( the_mutex, the_thread );
           the_thread->resource_count++;
+          _Thread_queue_Boost_priority( &the_mutex->Wait_queue.Queue, the_thread );
           break;
         case CORE_MUTEX_DISCIPLINES_PRIORITY_CEILING:
           _CORE_mutex_Push_priority( the_mutex, the_thread );
diff --git a/cpukit/score/src/mutex.c b/cpukit/score/src/mutex.c
index ae637dd..f03bab7 100644
--- a/cpukit/score/src/mutex.c
+++ b/cpukit/score/src/mutex.c
@@ -112,9 +112,7 @@ static void _Mutex_Acquire_slow(
   ISR_lock_Context  *lock_context
 )
 {
-  /* Priority inheritance */
-  _Thread_Raise_priority( owner, executing->current_priority );
-
+  _Thread_Inherit_priority( owner, executing );
   _Thread_queue_Enqueue_critical(
     &mutex->Queue.Queue,
     MUTEX_TQ_OPERATIONS,
@@ -136,15 +134,22 @@ static void _Mutex_Release_slow(
 {
   if (heads != NULL) {
     const Thread_queue_Operations *operations;
-    Thread_Control *first;
+    Thread_Control                *first;
+    bool                           unblock;
 
     operations = MUTEX_TQ_OPERATIONS;
     first = ( *operations->first )( heads );
 
     mutex->owner = first;
-    _Thread_queue_Extract_critical(
+    unblock = _Thread_queue_Extract_locked(
       &mutex->Queue.Queue,
       operations,
+      first
+    );
+    _Thread_queue_Boost_priority( &mutex->Queue.Queue, first );
+    _Thread_queue_Unblock_critical(
+      unblock,
+      &mutex->Queue.Queue,
       first,
       lock_context
     );
diff --git a/cpukit/score/src/threadchangepriority.c b/cpukit/score/src/threadchangepriority.c
index 8f5d14f..35e5e5b 100644
--- a/cpukit/score/src/threadchangepriority.c
+++ b/cpukit/score/src/threadchangepriority.c
@@ -110,6 +110,40 @@ void _Thread_Raise_priority(
   );
 }
 
+#if defined(RTEMS_SMP)
+static bool _Thread_Inherit_priority_filter(
+  Thread_Control   *inheritor,
+  Priority_Control *new_priority,
+  void             *arg
+)
+{
+  Thread_Control *ancestor = arg;
+
+  if ( _Scheduler_Get_own( inheritor ) == _Scheduler_Get_own( ancestor ) ) {
+    *new_priority = ancestor->current_priority;
+  }
+
+  return _Thread_Priority_less_than(
+    inheritor->current_priority,
+    *new_priority
+  );
+}
+
+void _Thread_Inherit_priority(
+  Thread_Control *inheritor,
+  Thread_Control *ancestor
+)
+{
+  _Thread_Change_priority(
+    inheritor,
+    PRIORITY_PSEUDO_ISR,
+    ancestor,
+    _Thread_Inherit_priority_filter,
+    false
+  );
+}
+#endif
+
 static bool _Thread_Restore_priority_filter(
   Thread_Control   *the_thread,
   Priority_Control *new_priority,
diff --git a/cpukit/score/src/threadqops.c b/cpukit/score/src/threadqops.c
index 07473f5..7b01779 100644
--- a/cpukit/score/src/threadqops.c
+++ b/cpukit/score/src/threadqops.c
@@ -293,6 +293,28 @@ static Thread_Control *_Thread_queue_Priority_first(
   return THREAD_RBTREE_NODE_TO_THREAD( first );
 }
 
+#if defined(RTEMS_SMP)
+void _Thread_queue_Boost_priority(
+  Thread_queue_Queue *queue,
+  Thread_Control     *the_thread
+)
+{
+  Thread_queue_Heads *heads = queue->heads;
+
+  if (
+    heads != NULL
+      && (
+        !_Chain_Has_only_one_node( &heads->Heads.Fifo )
+          || _RBTree_Is_empty(
+            &_Thread_queue_Priority_queue( heads, the_thread )->Queue
+          )
+      )
+  ) {
+    _Thread_Raise_priority( the_thread, PRIORITY_PSEUDO_ISR );
+  }
+}
+#endif
+
 const Thread_queue_Operations _Thread_queue_Operations_default = {
   .priority_change = _Thread_queue_Do_nothing_priority_change,
   .extract = _Thread_queue_Do_nothing_extract
diff --git a/doc/user/sem.t b/doc/user/sem.t
index 6bd22dd..8fbac93 100644
--- a/doc/user/sem.t
+++ b/doc/user/sem.t
@@ -115,6 +115,11 @@ for that resource.  Each time a task blocks attempting to obtain
 the resource, the task holding the resource may have its
 priority increased.
 
+On SMP configurations, in case the task holding the resource and the task that
+blocks attempting to obtain the resource are in different scheduler instances,
+the priority of the holder is raised to the pseudo interrupt priority (priority
+boosting).  The pseudo interrupt priority is the highest priority.
+
 RTEMS supports priority inheritance for local, binary
 semaphores that use the priority task wait queue blocking
 discipline.   When a task of higher priority than the task
diff --git a/doc/user/smp.t b/doc/user/smp.t
index 53fd7b64..2ab9aaf 100644
--- a/doc/user/smp.t
+++ b/doc/user/smp.t
@@ -164,11 +164,13 @@ and instruction caches. With clustered/partitioned scheduling it is possible to
 honour the cache topology of a system and thus avoid expensive cache
 synchronization traffic.  It is easy to implement.  The problem is to provide
 synchronization primitives for inter-partition synchronization. In RTEMS there
-are currently three means available
+are currently four means available
 
 @itemize @bullet
 @item events,
- at item message queues, and
+ at item message queues,
+ at item semaphores using the @ref{Semaphore Manager Priority Inheritance}
+protocol (priority boosting), and
 @item semaphores using the @ref{Semaphore Manager Multiprocessor Resource
 Sharing Protocol} (MrsP).
 @end itemize
diff --git a/testsuites/smptests/smpmutex01/init.c b/testsuites/smptests/smpmutex01/init.c
index 1b2a189..80b16fe 100644
--- a/testsuites/smptests/smpmutex01/init.c
+++ b/testsuites/smptests/smpmutex01/init.c
@@ -26,7 +26,7 @@ const char rtems_test_name[] = "SMPMUTEX 1";
 
 #define PART_COUNT 2
 
-#define TASK_COUNT 8
+#define TASK_COUNT 9
 
 typedef enum {
   REQ_WAKE_UP_MASTER = RTEMS_EVENT_0,
@@ -43,7 +43,8 @@ typedef enum {
   B_4,
   B_5_0,
   B_5_1,
-  H,
+  H_A,
+  H_B,
   NONE
 } task_id;
 
@@ -111,15 +112,21 @@ static rtems_event_set wait_for_events(void)
   return events;
 }
 
-static void sync_with_helper(test_context *ctx)
+static void sync_with_helper_by_id(test_context *ctx, task_id id)
 {
   rtems_event_set events;
 
-  send_event(ctx, H, REQ_WAKE_UP_HELPER);
+  send_event(ctx, id, REQ_WAKE_UP_HELPER);
   events = wait_for_events();
   rtems_test_assert(events == REQ_WAKE_UP_MASTER);
 }
 
+static void sync_with_helper(test_context *ctx)
+{
+  sync_with_helper_by_id(ctx, H_A);
+  sync_with_helper_by_id(ctx, H_B);
+}
+
 static void request(test_context *ctx, task_id id, request_id req)
 {
   send_event(ctx, id, req);
@@ -159,6 +166,24 @@ static void check_generations(test_context *ctx, task_id a, task_id b)
   }
 }
 
+static void assert_prio(
+  test_context *ctx,
+  task_id id,
+  rtems_task_priority expected
+)
+{
+  rtems_task_priority actual;
+  rtems_status_code sc;
+
+  sc = rtems_task_set_priority(
+    ctx->tasks[id],
+    RTEMS_CURRENT_PRIORITY,
+    &actual
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  rtems_test_assert(expected == actual);
+}
+
 static void helper(rtems_task_argument arg)
 {
   test_context *ctx = &test_instance;
@@ -202,7 +227,8 @@ static void test(void)
   start_task(ctx, B_4, worker, 4, SCHED_B);
   start_task(ctx, B_5_0, worker, 5, SCHED_B);
   start_task(ctx, B_5_1, worker, 5, SCHED_B);
-  start_task(ctx, H, helper, 6, SCHED_B);
+  start_task(ctx, H_A, helper, 3, SCHED_A);
+  start_task(ctx, H_B, helper, 6, SCHED_B);
 
   sc = rtems_semaphore_create(
     rtems_build_name(' ', 'M', 'T', 'X'),
@@ -216,8 +242,10 @@ static void test(void)
   obtain(ctx);
   request(ctx, A_1, REQ_MTX_OBTAIN);
   check_generations(ctx, NONE, NONE);
+  assert_prio(ctx, M, 1);
   release(ctx);
   check_generations(ctx, A_1, NONE);
+  assert_prio(ctx, M, 3);
   request(ctx, A_1, REQ_MTX_RELEASE);
   check_generations(ctx, A_1, NONE);
 
@@ -226,8 +254,11 @@ static void test(void)
   request(ctx, A_1, REQ_MTX_OBTAIN);
   request(ctx, A_2_1, REQ_MTX_OBTAIN);
   check_generations(ctx, NONE, NONE);
+  assert_prio(ctx, M, 1);
   release(ctx);
   check_generations(ctx, A_1, NONE);
+  assert_prio(ctx, M, 3);
+  assert_prio(ctx, A_1, 1);
   request(ctx, A_1, REQ_MTX_RELEASE);
   check_generations(ctx, A_1, A_2_0);
   request(ctx, A_2_0, REQ_MTX_RELEASE);
@@ -240,8 +271,10 @@ static void test(void)
   request(ctx, B_4, REQ_MTX_OBTAIN);
   request(ctx, B_5_1, REQ_MTX_OBTAIN);
   check_generations(ctx, NONE, NONE);
+  assert_prio(ctx, M, 0);
   release(ctx);
   sync_with_helper(ctx);
+  assert_prio(ctx, M, 3);
   check_generations(ctx, B_4, NONE);
   request(ctx, B_4, REQ_MTX_RELEASE);
   check_generations(ctx, B_4, B_5_0);
@@ -252,26 +285,44 @@ static void test(void)
 
   obtain(ctx);
   request(ctx, A_2_0, REQ_MTX_OBTAIN);
+  check_generations(ctx, NONE, NONE);
+  assert_prio(ctx, M, 2);
   request(ctx, B_5_0, REQ_MTX_OBTAIN);
+  check_generations(ctx, NONE, NONE);
+  assert_prio(ctx, M, 0);
   request(ctx, B_5_1, REQ_MTX_OBTAIN);
   request(ctx, B_4, REQ_MTX_OBTAIN);
   request(ctx, A_2_1, REQ_MTX_OBTAIN);
   request(ctx, A_1, REQ_MTX_OBTAIN);
   check_generations(ctx, NONE, NONE);
   release(ctx);
+  sync_with_helper(ctx);
   check_generations(ctx, A_1, NONE);
+  assert_prio(ctx, M, 3);
+  assert_prio(ctx, A_1, 0);
   request(ctx, A_1, REQ_MTX_RELEASE);
   check_generations(ctx, A_1, B_4);
+  assert_prio(ctx, A_1, 1);
+  assert_prio(ctx, B_4, 0);
   request(ctx, B_4, REQ_MTX_RELEASE);
   check_generations(ctx, B_4, A_2_0);
+  assert_prio(ctx, B_4, 4);
+  assert_prio(ctx, A_2_0, 0);
   request(ctx, A_2_0, REQ_MTX_RELEASE);
   check_generations(ctx, A_2_0, B_5_0);
+  assert_prio(ctx, A_2_0, 2);
+  assert_prio(ctx, B_5_0, 0);
   request(ctx, B_5_0, REQ_MTX_RELEASE);
   check_generations(ctx, B_5_0, A_2_1);
+  assert_prio(ctx, B_5_0, 5);
+  assert_prio(ctx, A_2_1, 0);
   request(ctx, A_2_1, REQ_MTX_RELEASE);
   check_generations(ctx, A_2_1, B_5_1);
+  assert_prio(ctx, A_2_1, 2);
+  assert_prio(ctx, B_5_1, 5);
   request(ctx, B_5_1, REQ_MTX_RELEASE);
   check_generations(ctx, B_5_1, NONE);
+  assert_prio(ctx, B_5_1, 5);
 }
 
 static void Init(rtems_task_argument arg)
-- 
1.8.4.5




More information about the devel mailing list