[PATCH 24/27] score: Rework ask for help requests

Sebastian Huber sebastian.huber at embedded-brains.de
Mon Nov 15 17:12:56 UTC 2021


Process ask for help requests on the current processor.  This avoids
using inter-processor interrupts to make the system behaviour a bit more
predictable.

Update #4531.
---
 cpukit/include/rtems/score/schedulerimpl.h    |  24 ----
 cpukit/include/rtems/score/schedulersmpimpl.h | 113 +++++++++++++-----
 cpukit/include/rtems/score/thread.h           |  20 +++-
 cpukit/include/rtems/score/threadimpl.h       |  26 ----
 cpukit/score/src/schedulersmp.c               |  29 ++---
 cpukit/score/src/threaddispatch.c             |   2 +-
 6 files changed, 112 insertions(+), 102 deletions(-)

diff --git a/cpukit/include/rtems/score/schedulerimpl.h b/cpukit/include/rtems/score/schedulerimpl.h
index 10dd039086..dda1b4ee6e 100644
--- a/cpukit/include/rtems/score/schedulerimpl.h
+++ b/cpukit/include/rtems/score/schedulerimpl.h
@@ -174,30 +174,6 @@ RTEMS_INLINE_ROUTINE bool _Scheduler_Is_non_preempt_mode_supported(
 }
 #endif
 
-#if defined(RTEMS_SMP)
-void _Scheduler_Request_ask_for_help( Thread_Control *the_thread );
-
-/**
- * @brief Registers an ask for help request if necessary.
- *
- * The actual ask for help operation is carried out during
- * _Thread_Do_dispatch() on a processor related to the thread.  This yields a
- * better separation of scheduler instances.  A thread of one scheduler
- * instance should not be forced to carry out too much work for threads on
- * other scheduler instances.
- *
- * @param the_thread The thread in need for help.
- */
-RTEMS_INLINE_ROUTINE void _Scheduler_Ask_for_help( Thread_Control *the_thread )
-{
-  _Assert( _Thread_State_is_owner( the_thread ) );
-
-  if ( the_thread->Scheduler.helping_nodes > 0 ) {
-    _Scheduler_Request_ask_for_help( the_thread );
-  }
-}
-#endif
-
 /**
  * The preferred method to add a new scheduler is to define the jump table
  * entries and add a case to the _Scheduler_Initialize routine.
diff --git a/cpukit/include/rtems/score/schedulersmpimpl.h b/cpukit/include/rtems/score/schedulersmpimpl.h
index 811376aa61..6e2e21e544 100644
--- a/cpukit/include/rtems/score/schedulersmpimpl.h
+++ b/cpukit/include/rtems/score/schedulersmpimpl.h
@@ -562,6 +562,73 @@ static inline bool _Scheduler_SMP_Is_processor_owned_by_us(
   return cpu->Scheduler.context == context;
 }
 
+/**
+ * @brief Removes the thread's ask for help request from the processor.
+ *
+ * The caller must be the owner of the thread's scheduler lock.
+ *
+ * @param[in, out] thread is the thread of the ask for help request.
+ *
+ * @param[in, out] cpu is the processor from which the ask for help request
+ *   should be removed.
+ */
+void _Scheduler_SMP_Remove_ask_for_help_from_processor(
+  Thread_Control  *thread,
+  Per_CPU_Control *cpu
+);
+
+/**
+ * @brief Cancels the thread's ask for help request.
+ *
+ * The caller must be the owner of the thread's scheduler lock.
+ *
+ * @param[in, out] thread is the thread of the ask help request.
+ */
+static inline void _Scheduler_SMP_Cancel_ask_for_help( Thread_Control *thread )
+{
+  Per_CPU_Control *cpu;
+
+  _Assert( _ISR_lock_Is_owner( &thread->Scheduler.Lock ) );
+  cpu = thread->Scheduler.ask_for_help_cpu;
+
+  if ( RTEMS_PREDICT_FALSE( cpu != NULL ) ) {
+    _Scheduler_SMP_Remove_ask_for_help_from_processor( thread, cpu );
+  }
+}
+
+/**
+ * @brief Requests to ask for help for the thread.
+ *
+ * The actual ask for help operations are carried out during
+ * _Thread_Do_dispatch() on the current processor.
+ *
+ * An alternative approach would be to carry out the requests on a processor
+ * related to the thread.  This could reduce the overhead for the preempting
+ * thread a bit, however, there are at least two problems with this approach.
+ * Firstly, we have to figure out what is a processor related to the thread.
+ * Secondly, we may need an inter-processor interrupt.
+ *
+ * @param[in, out] thread is the thread in need for help.
+ */
+static inline void _Scheduler_SMP_Request_ask_for_help( Thread_Control *thread )
+{
+  ISR_lock_Context lock_context;
+  Per_CPU_Control *cpu_self;
+
+  cpu_self = _Per_CPU_Get();
+
+  _Assert( thread->Scheduler.ask_for_help_cpu == NULL );
+  thread->Scheduler.ask_for_help_cpu = cpu_self;
+  cpu_self->dispatch_necessary = true;
+
+  _Per_CPU_Acquire( cpu_self, &lock_context );
+  _Chain_Append_unprotected(
+    &cpu_self->Threads_in_need_for_help,
+    &thread->Scheduler.Help_node
+  );
+  _Per_CPU_Release( cpu_self, &lock_context );
+}
+
 /**
  * @brief This enumeration defines what a scheduler should do with a node which
  * could be scheduled.
@@ -616,7 +683,7 @@ static inline Scheduler_SMP_Action _Scheduler_SMP_Try_to_schedule(
   owner_sticky_level = node->sticky_level;
 
   if ( RTEMS_PREDICT_TRUE( owner_state == THREAD_SCHEDULER_READY ) ) {
-    _Thread_Scheduler_cancel_need_for_help( owner, _Thread_Get_CPU( owner ) );
+    _Scheduler_SMP_Cancel_ask_for_help( owner );
     _Scheduler_Thread_change_state( owner, THREAD_SCHEDULER_SCHEDULED );
     _Thread_Scheduler_release_critical( owner, &lock_context );
     return SCHEDULER_SMP_DO_SCHEDULE;
@@ -769,22 +836,15 @@ static inline void _Scheduler_SMP_Preempt(
   _Thread_Scheduler_acquire_critical( victim_owner, &lock_context );
 
   if ( RTEMS_PREDICT_TRUE( victim_idle == NULL ) ) {
-    cpu = _Thread_Get_CPU( victim_owner );
-
     if ( victim_owner->Scheduler.state == THREAD_SCHEDULER_SCHEDULED ) {
       _Scheduler_Thread_change_state( victim_owner, THREAD_SCHEDULER_READY );
 
       if ( victim_owner->Scheduler.helping_nodes > 0 ) {
-        ISR_lock_Context lock_context_2;
-
-        _Per_CPU_Acquire( cpu, &lock_context_2 );
-        _Chain_Append_unprotected(
-          &cpu->Threads_in_need_for_help,
-          &victim_owner->Scheduler.Help_node
-        );
-        _Per_CPU_Release( cpu, &lock_context_2 );
+        _Scheduler_SMP_Request_ask_for_help( victim_owner );
       }
     }
+
+    cpu = _Thread_Get_CPU( victim_owner );
   } else {
     cpu = _Thread_Get_CPU( victim_idle );
   }
@@ -1030,10 +1090,7 @@ static inline void _Scheduler_SMP_Enqueue_scheduled(
         if ( owner->Scheduler.state == THREAD_SCHEDULER_READY ) {
           Per_CPU_Control *cpu;
 
-          _Thread_Scheduler_cancel_need_for_help(
-            owner,
-            _Thread_Get_CPU( owner )
-          );
+          _Scheduler_SMP_Cancel_ask_for_help( owner );
           _Scheduler_Thread_change_state( owner, THREAD_SCHEDULER_SCHEDULED );
           cpu = _Thread_Get_CPU( node_idle );
           _Thread_Set_CPU( owner, cpu );
@@ -1252,8 +1309,8 @@ static inline void _Scheduler_SMP_Block(
   _Assert( sticky_level >= 0 );
 
   _Thread_Scheduler_acquire_critical( thread, &lock_context );
+  _Scheduler_SMP_Cancel_ask_for_help( thread );
   cpu = _Thread_Get_CPU( thread );
-  _Thread_Scheduler_cancel_need_for_help( thread, cpu );
   _Scheduler_Thread_change_state( thread, THREAD_SCHEDULER_BLOCKED );
   _Thread_Scheduler_release_critical( thread, &lock_context );
 
@@ -1316,7 +1373,8 @@ static inline void _Scheduler_SMP_Unblock(
 {
   Scheduler_SMP_Node_state  node_state;
   Priority_Control          priority;
-  bool                      needs_help;
+
+  _Assert( _Chain_Is_node_off_chain( &thread->Scheduler.Help_node ) );
 
   ++node->sticky_level;
   _Assert( node->sticky_level > 0 );
@@ -1346,18 +1404,19 @@ static inline void _Scheduler_SMP_Unblock(
 
   if ( node_state == SCHEDULER_SMP_NODE_BLOCKED ) {
     Priority_Control insert_priority;
+    bool             needs_help;
 
     insert_priority = SCHEDULER_PRIORITY_APPEND( priority );
     needs_help = ( *enqueue )( context, node, insert_priority );
+
+    if ( needs_help && thread->Scheduler.helping_nodes > 0 ) {
+      _Scheduler_SMP_Request_ask_for_help( thread );
+    }
   } else {
     _Assert( node_state == SCHEDULER_SMP_NODE_READY );
     _Assert( node->sticky_level > 0 );
     _Assert( node->idle == NULL );
-    needs_help = true;
-  }
-
-  if ( needs_help ) {
-    _Scheduler_Ask_for_help( thread );
+    _Scheduler_SMP_Request_ask_for_help( thread );
   }
 }
 
@@ -1562,10 +1621,7 @@ static inline bool _Scheduler_SMP_Ask_for_help(
       ) {
         Thread_Control *lowest_scheduled_idle;
 
-        _Thread_Scheduler_cancel_need_for_help(
-          thread,
-          _Thread_Get_CPU( thread )
-        );
+        _Scheduler_SMP_Cancel_ask_for_help( thread );
         _Scheduler_Thread_change_state( thread, THREAD_SCHEDULER_SCHEDULED );
         _Thread_Scheduler_release_critical( thread, &lock_context );
 
@@ -1595,10 +1651,7 @@ static inline bool _Scheduler_SMP_Ask_for_help(
         success = false;
       }
     } else if ( node_state == SCHEDULER_SMP_NODE_SCHEDULED ) {
-      _Thread_Scheduler_cancel_need_for_help(
-        thread,
-        _Thread_Get_CPU( thread )
-      );
+      _Scheduler_SMP_Cancel_ask_for_help( thread );
       _Scheduler_Thread_change_state( thread, THREAD_SCHEDULER_SCHEDULED );
       _Thread_Scheduler_release_critical( thread, &lock_context );
       _Scheduler_Discard_idle_thread(
diff --git a/cpukit/include/rtems/score/thread.h b/cpukit/include/rtems/score/thread.h
index 4c8a97c0f7..23ed8e1406 100644
--- a/cpukit/include/rtems/score/thread.h
+++ b/cpukit/include/rtems/score/thread.h
@@ -306,10 +306,24 @@ typedef struct {
   Chain_Control Scheduler_nodes;
 
   /**
-   * @brief Node for the Per_CPU_Control::Threads_in_need_for_help chain.
+   * @brief If an ask for help request for the thread is pending, then this
+   *   member references the processor on which the ask for help request is
+   *   registered, otherwise it is NULL.
    *
-   * This chain is protected by the Per_CPU_Control::Lock lock of the assigned
-   * processor.
+   * Depending on the state of the thread and usage context, this member is
+   * protected by the Per_CPU_Control::Lock lock of the referenced processor,
+   * the scheduler lock of the thread (Thread_Scheduler_control::Lock), or the
+   * thread state lock.
+   */
+  struct Per_CPU_Control *ask_for_help_cpu;
+
+  /**
+   * @brief This member is the node for the
+   *   Per_CPU_Control::Threads_in_need_for_help chain.
+   *
+   * This chain is protected by the Per_CPU_Control::Lock lock of the processor
+   * on which the ask for help request is registered
+   * (Thread_Scheduler_control::ask_for_help_cpu).
    */
   Chain_Node Help_node;
 
diff --git a/cpukit/include/rtems/score/threadimpl.h b/cpukit/include/rtems/score/threadimpl.h
index e35a13abd6..86a2abeb38 100644
--- a/cpukit/include/rtems/score/threadimpl.h
+++ b/cpukit/include/rtems/score/threadimpl.h
@@ -1494,32 +1494,6 @@ RTEMS_INLINE_ROUTINE bool _Thread_Owns_resources(
 }
 #endif
 
-#if defined(RTEMS_SMP)
-/**
- * @brief Cancels the thread's need for help.
- *
- * @param the_thread The thread to cancel the help request of.
- * @param cpu The cpu to get the lock context of in order to
- *      cancel the help request.
- */
-RTEMS_INLINE_ROUTINE void _Thread_Scheduler_cancel_need_for_help(
-  Thread_Control  *the_thread,
-  Per_CPU_Control *cpu
-)
-{
-  ISR_lock_Context lock_context;
-
-  _Per_CPU_Acquire( cpu, &lock_context );
-
-  if ( !_Chain_Is_node_off_chain( &the_thread->Scheduler.Help_node ) ) {
-    _Chain_Extract_unprotected( &the_thread->Scheduler.Help_node );
-    _Chain_Set_off_chain( &the_thread->Scheduler.Help_node );
-  }
-
-  _Per_CPU_Release( cpu, &lock_context );
-}
-#endif
-
 /**
  * @brief Gets the home scheduler of the thread.
  *
diff --git a/cpukit/score/src/schedulersmp.c b/cpukit/score/src/schedulersmp.c
index cc566180b7..3bb2638139 100644
--- a/cpukit/score/src/schedulersmp.c
+++ b/cpukit/score/src/schedulersmp.c
@@ -21,28 +21,21 @@
 
 #include <rtems/score/schedulersmpimpl.h>
 
-void _Scheduler_Request_ask_for_help( Thread_Control *the_thread )
+void _Scheduler_SMP_Remove_ask_for_help_from_processor(
+  Thread_Control  *thread,
+  Per_CPU_Control *cpu
+)
 {
-  ISR_lock_Context scheduler_lock_context;
+  ISR_lock_Context lock_context;
 
-  _Thread_Scheduler_acquire_critical( the_thread, &scheduler_lock_context );
+  _Assert( _ISR_lock_Is_owner( &thread->Scheduler.Lock ) );
 
-  if ( _Chain_Is_node_off_chain( &the_thread->Scheduler.Help_node ) ) {
-    Per_CPU_Control  *cpu;
-    ISR_lock_Context  per_cpu_lock_context;
+  _Per_CPU_Acquire( cpu, &lock_context );
 
-    cpu = _Thread_Get_CPU( the_thread );
-    _Per_CPU_Acquire( cpu, &per_cpu_lock_context );
-
-    _Chain_Append_unprotected(
-      &cpu->Threads_in_need_for_help,
-      &the_thread->Scheduler.Help_node
-    );
-
-    _Per_CPU_Release( cpu, &per_cpu_lock_context );
-
-    _Thread_Dispatch_request( _Per_CPU_Get(), cpu );
+  if ( thread->Scheduler.ask_for_help_cpu == cpu ) {
+    _Chain_Extract_unprotected( &thread->Scheduler.Help_node );
+    thread->Scheduler.ask_for_help_cpu = NULL;
   }
 
-  _Thread_Scheduler_release_critical( the_thread, &scheduler_lock_context );
+  _Per_CPU_Release( cpu, &lock_context );
 }
diff --git a/cpukit/score/src/threaddispatch.c b/cpukit/score/src/threaddispatch.c
index a53c8de2b0..8dc5dfb9e8 100644
--- a/cpukit/score/src/threaddispatch.c
+++ b/cpukit/score/src/threaddispatch.c
@@ -172,8 +172,8 @@ static ISR_Level _Thread_Preemption_intervention(
     Thread_Control *the_thread;
 
     node = _Chain_Get_first_unprotected( &cpu_self->Threads_in_need_for_help );
-    _Chain_Set_off_chain( node );
     the_thread = THREAD_OF_SCHEDULER_HELP_NODE( node );
+    the_thread->Scheduler.ask_for_help_cpu = NULL;
 
     _Per_CPU_Release( cpu_self, &lock_context );
 
-- 
2.26.2



More information about the devel mailing list