[rtems commit] rtems: Avoid potential recursion in ASR handling

Sebastian Huber sebh at rtems.org
Sat Feb 20 14:25:22 UTC 2021


Module:    rtems
Branch:    master
Commit:    a7e180b95dcc3c2f0bab82ec8eaa211cb0a4956a
Changeset: http://git.rtems.org/rtems/commit/?id=a7e180b95dcc3c2f0bab82ec8eaa211cb0a4956a

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Tue Feb 16 12:58:36 2021 +0100

rtems: Avoid potential recursion in ASR handling

Do the mode changes necessary for the ASR processing directly under
protection of the thread state lock to avoid the recursive calls to
thread dispatching done in rtems_task_mode().

Close #4244.

---

 cpukit/rtems/src/signalsend.c | 98 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 91 insertions(+), 7 deletions(-)

diff --git a/cpukit/rtems/src/signalsend.c b/cpukit/rtems/src/signalsend.c
index 606ddfc..8c4ce10 100644
--- a/cpukit/rtems/src/signalsend.c
+++ b/cpukit/rtems/src/signalsend.c
@@ -21,7 +21,9 @@
 #endif
 
 #include <rtems/rtems/signalimpl.h>
+#include <rtems/rtems/modesimpl.h>
 #include <rtems/rtems/tasksdata.h>
+#include <rtems/score/schedulerimpl.h>
 #include <rtems/score/threaddispatch.h>
 #include <rtems/score/threadimpl.h>
 
@@ -33,13 +35,29 @@ static void _Signal_Action_handler(
   ISR_lock_Context *lock_context
 )
 {
-  RTEMS_API_Control *api;
-  ASR_Information   *asr;
-  rtems_signal_set   signal_set;
-  rtems_mode      prev_mode;
+  RTEMS_API_Control           *api;
+  ASR_Information             *asr;
+  rtems_signal_set             signal_set;
+  bool                         normal_is_preemptible;
+  uint32_t                     normal_cpu_time_budget;
+  Thread_CPU_budget_algorithms normal_budget_algorithm;
+  bool                         normal_asr_is_enabled;
+  uint32_t                     normal_isr_level;
+  uint32_t                     before_call_isr_level;
+  bool                         after_call_is_preemptible;
+  bool                         after_call_asr_is_enabled;
 
   (void) action;
 
+  /*
+   * For the variable names the following notation is used.  The prefix
+   * "normal" specifies the mode associated with the normal task execution.
+   * The prefix "before_call" specifies the mode set up right before the ASR
+   * handler is called.  The prefix "after_call" specifies the mode after the
+   * ASR handler call returned.  This mode may differ from the "before_call"
+   * mode since an ASR handler is free to change the task mode.
+   */
+
   api = executing->API_Extensions[ THREAD_API_RTEMS ];
   asr = &api->Signal;
 
@@ -48,16 +66,82 @@ static void _Signal_Action_handler(
   _Assert( signal_set != 0 );
   asr->signals_pending = 0;
 
-  _Thread_State_release( executing, lock_context );
+  /* Save normal mode */
+
+  normal_is_preemptible = executing->is_preemptible;
+  normal_asr_is_enabled = asr->is_enabled;
+  normal_cpu_time_budget = executing->cpu_time_budget;
+  normal_budget_algorithm = executing->budget_algorithm;
+
+  /* Set mode for ASR processing */
+
+  executing->is_preemptible = _Modes_Is_preempt( asr->mode_set );
+  asr->is_enabled = !_Modes_Is_asr_disabled( asr->mode_set );
+  _Modes_Apply_timeslice_to_thread( asr->mode_set, executing );
+  before_call_isr_level = _Modes_Get_interrupt_level( asr->mode_set );
+
+  if ( executing->is_preemptible && !normal_is_preemptible ) {
+    Per_CPU_Control *cpu_self;
+
+    cpu_self = _Thread_Dispatch_disable_critical( lock_context );
+    _Scheduler_Schedule( executing );
+    _Thread_State_release( executing, lock_context );
+    _Thread_Dispatch_direct( cpu_self );
+  } else {
+    _Thread_State_release( executing, lock_context );
+  }
 
-  rtems_task_mode( asr->mode_set, RTEMS_ALL_MODE_MASKS, &prev_mode );
+  normal_isr_level = _ISR_Get_level();
+  _ISR_Set_level( before_call_isr_level );
 
   /* Call the ASR handler in the ASR processing mode */
   ( *asr->handler )( signal_set );
 
-  rtems_task_mode( prev_mode, RTEMS_ALL_MODE_MASKS, &prev_mode );
+  /* Restore normal mode */
+
+  _ISR_Set_level( normal_isr_level );
 
   _Thread_State_acquire( executing, lock_context );
+
+  executing->cpu_time_budget = normal_cpu_time_budget ;
+  executing->budget_algorithm = normal_budget_algorithm ;
+  after_call_is_preemptible = executing->is_preemptible;
+  executing->is_preemptible = normal_is_preemptible;
+
+  /*
+   * We do the best to avoid recursion in the ASR processing.  A well behaved
+   * application will disable ASR processing during ASR processing.  In this
+   * case, ASR processing is currently disabled.  We do now the thread dispatch
+   * necessary due to a re-enabled preemption mode.  This helps to avoid doing
+   * the next round of ASR processing recursively in _Thread_Dispatch_direct().
+   */
+  if ( normal_is_preemptible && !after_call_is_preemptible ) {
+    Per_CPU_Control *cpu_self;
+
+    cpu_self = _Thread_Dispatch_disable_critical( lock_context );
+    _Scheduler_Schedule( executing );
+    _Thread_State_release( executing, lock_context );
+    _Thread_Dispatch_direct( cpu_self );
+    _Thread_State_acquire( executing, lock_context );
+  }
+
+  /*
+   * Restore the normal ASR processing mode.  If we enable ASR processing and
+   * there are pending signals, then add us as a post-switch action.  The loop
+   * in _Thread_Run_post_switch_actions() will continue after our return and
+   * call us again.  This avoids a recursion.
+   */
+
+  after_call_asr_is_enabled = asr->is_enabled;
+  asr->is_enabled = normal_asr_is_enabled;
+
+  if (
+    normal_asr_is_enabled &&
+    !after_call_asr_is_enabled &&
+    asr->signals_pending != 0
+  ) {
+    _Thread_Append_post_switch_action( executing, action );
+  }
 }
 
 rtems_status_code rtems_signal_send(



More information about the vc mailing list