[PATCH v2 4/4] rtems: Avoid potential recursion in ASR handling

Sebastian Huber sebastian.huber at embedded-brains.de
Fri Feb 19 09:40:38 UTC 2021


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().

Update #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 606ddfcb53..f6489b41b3 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                     asr_isr_level;
+  bool                         previous_is_preemptible;
+  bool                         previous_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 "asr" specifies the mode set up before the ASR handler is
+   * called.  The prefix "previous" specifies the mode right before it is
+   * restored to "normal".  This mode may differ from the "asr" 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 );
+  asr_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( asr_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 ;
+  previous_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 && !previous_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.
+   */
+
+  previous_asr_is_enabled = asr->is_enabled;
+  asr->is_enabled = normal_asr_is_enabled;
+
+  if (
+    normal_asr_is_enabled &&
+      !previous_asr_is_enabled &&
+      asr->signals_pending != 0
+  ) {
+    _Thread_Append_post_switch_action( executing, action );
+  }
 }
 
 rtems_status_code rtems_signal_send(
-- 
2.26.2



More information about the devel mailing list