[PATCH 3/8] score: Add and use _Thread_Do_dispatch()
Sebastian Huber
sebastian.huber at embedded-brains.de
Wed Mar 4 15:07:03 UTC 2015
The _Thread_Dispatch() function is quite complex and the time to set up
and tear down the stack frame is significant. Split this function into
two parts. The complex part is now in _Thread_Do_dispatch(). Call
_Thread_Do_dispatch() in _Thread_Enable_dispatch() only if necessary.
This increases the average case performance.
Simplify _Thread_Handler() for SMP configurations.
Update #2273.
---
cpukit/score/include/rtems/score/threaddispatch.h | 71 ++++++++++++++++++-----
cpukit/score/src/threaddispatch.c | 64 ++++++++++----------
cpukit/score/src/threadhandler.c | 70 ++++++++++------------
testsuites/tmtests/tm26/task1.c | 4 ++
4 files changed, 124 insertions(+), 85 deletions(-)
diff --git a/cpukit/score/include/rtems/score/threaddispatch.h b/cpukit/score/include/rtems/score/threaddispatch.h
index aec436f..80de19d 100644
--- a/cpukit/score/include/rtems/score/threaddispatch.h
+++ b/cpukit/score/include/rtems/score/threaddispatch.h
@@ -193,26 +193,40 @@ RTEMS_INLINE_ROUTINE void _Thread_Dispatch_initialization( void )
#endif /* RTEMS_SMP */
/**
- * @brief Dispatch thread.
+ * @brief Performs a thread dispatch if necessary.
*
- * This routine is responsible for transferring control of the
- * processor from the executing thread to the heir thread. Once the
- * heir is running an attempt is made to dispatch any ASRs.
- * As part of this process, it is responsible for the following actions:
- * + saving the context of the executing thread
- * + restoring the context of the heir thread
- * + dispatching any signals for the resulting executing thread
-
- * ALTERNATE ENTRY POINTS:
- * void _Thread_Enable_dispatch();
+ * This routine is responsible for transferring control of the processor from
+ * the executing thread to the heir thread. Once the heir is running an
+ * attempt is made to run the pending post-switch thread actions.
*
- * - INTERRUPT LATENCY:
- * + dispatch thread
- * + no dispatch thread
+ * As part of this process, it is responsible for the following actions
+ * - update timing information of the executing thread,
+ * - save the context of the executing thread,
+ * - invokation of the thread switch user extensions,
+ * - restore the context of the heir thread, and
+ * - run of pending post-switch thread actions of the resulting executing
+ * thread.
+ *
+ * On entry the thread dispatch level must be equal to zero.
*/
void _Thread_Dispatch( void );
/**
+ * @brief Performs a thread dispatch on the current processor.
+ *
+ * On entry the thread dispatch disable level must be equal to one and
+ * interrupts must be disabled.
+ *
+ * This function assumes that a thread dispatch is necessary.
+ *
+ * @param[in] cpu_self The current processor.
+ * @param[in] level The previous interrupt level.
+ *
+ * @see _Thread_Dispatch().
+ */
+void _Thread_Do_dispatch( Per_CPU_Control *cpu_self, ISR_Level level );
+
+/**
* This routine prevents dispatching.
*/
@@ -228,8 +242,33 @@ RTEMS_INLINE_ROUTINE void _Thread_Disable_dispatch( void )
RTEMS_INLINE_ROUTINE void _Thread_Enable_dispatch_body( void )
{
- if ( _Thread_Dispatch_decrement_disable_level() == 0 )
- _Thread_Dispatch();
+ Per_CPU_Control *cpu_self;
+ uint32_t disable_level;
+
+ cpu_self = _Per_CPU_Get();
+
+#if defined( RTEMS_SMP )
+ _Giant_Release( cpu_self );
+#endif
+
+ disable_level = cpu_self->thread_dispatch_disable_level;
+
+ if ( disable_level == 1 ) {
+ ISR_Level level;
+
+ _ISR_Disable_without_giant( level );
+
+ if ( cpu_self->dispatch_necessary ) {
+ _Thread_Do_dispatch( cpu_self, level );
+ } else {
+ cpu_self->thread_dispatch_disable_level = 0;
+ _Profiling_Thread_dispatch_enable( cpu_self, 0 );
+ }
+
+ _ISR_Enable_without_giant( level );
+ } else {
+ cpu_self->thread_dispatch_disable_level = disable_level - 1;
+ }
}
/**
diff --git a/cpukit/score/src/threaddispatch.c b/cpukit/score/src/threaddispatch.c
index cc023fc..f20f427 100644
--- a/cpukit/score/src/threaddispatch.c
+++ b/cpukit/score/src/threaddispatch.c
@@ -60,40 +60,15 @@ static void _Thread_Run_post_switch_actions( Thread_Control *executing )
_Thread_Action_release_and_ISR_enable( cpu_self, level );
}
-void _Thread_Dispatch( void )
+void _Thread_Do_dispatch( Per_CPU_Control *cpu_self, ISR_Level level )
{
- Per_CPU_Control *cpu_self;
- Thread_Control *executing;
- ISR_Level level;
-
-#if defined( RTEMS_SMP )
- /*
- * On SMP the complete context switch must be atomic with respect to one
- * processor. See also _Thread_Handler() since _Context_switch() may branch
- * to this function.
- */
- _ISR_Disable_without_giant( level );
-#endif
+ Thread_Control *executing;
- cpu_self = _Per_CPU_Get();
- _Assert( cpu_self->thread_dispatch_disable_level == 0 );
- _Profiling_Thread_dispatch_disable( cpu_self, 0 );
- cpu_self->thread_dispatch_disable_level = 1;
+ _Assert( cpu_self->thread_dispatch_disable_level == 1 );
- /*
- * Now determine if we need to perform a dispatch on the current CPU.
- */
executing = cpu_self->executing;
-#if !defined( RTEMS_SMP )
- _ISR_Disable( level );
-#endif
-
-#if defined( RTEMS_SMP )
- if ( cpu_self->dispatch_necessary ) {
-#else
- while ( cpu_self->dispatch_necessary ) {
-#endif
+ do {
Thread_Control *heir = _Thread_Get_heir_and_make_it_executing( cpu_self );
/*
@@ -115,6 +90,11 @@ void _Thread_Dispatch( void )
if ( heir->budget_algorithm == THREAD_CPU_BUDGET_ALGORITHM_RESET_TIMESLICE )
heir->cpu_time_budget = rtems_configuration_get_ticks_per_timeslice();
+ /*
+ * On SMP the complete context switch must be atomic with respect to one
+ * processor. See also _Thread_Handler() since _Context_switch() may branch
+ * to this function.
+ */
#if !defined( RTEMS_SMP )
_ISR_Enable( level );
#endif
@@ -158,7 +138,13 @@ void _Thread_Dispatch( void )
#if !defined( RTEMS_SMP )
_ISR_Disable( level );
#endif
- }
+ } while (
+#if defined( RTEMS_SMP )
+ false
+#else
+ cpu_self->dispatch_necessary
+#endif
+ );
post_switch:
_Assert( cpu_self->thread_dispatch_disable_level == 1 );
@@ -169,3 +155,21 @@ post_switch:
_Thread_Run_post_switch_actions( executing );
}
+
+void _Thread_Dispatch( void )
+{
+ ISR_Level level;
+ Per_CPU_Control *cpu_self;
+
+ _ISR_Disable_without_giant( level );
+
+ cpu_self = _Per_CPU_Get();
+
+ if ( cpu_self->dispatch_necessary ) {
+ _Profiling_Thread_dispatch_disable( cpu_self, 0 );
+ cpu_self->thread_dispatch_disable_level = 1;
+ _Thread_Do_dispatch( cpu_self, level );
+ } else {
+ _ISR_Enable_without_giant( level );
+ }
+}
diff --git a/cpukit/score/src/threadhandler.c b/cpukit/score/src/threadhandler.c
index db73028..fd828a2 100644
--- a/cpukit/score/src/threadhandler.c
+++ b/cpukit/score/src/threadhandler.c
@@ -26,9 +26,9 @@
void _Thread_Handler( void )
{
- Thread_Control *executing = _Thread_Executing;
- ISR_Level level;
-
+ Thread_Control *executing = _Thread_Executing;
+ ISR_Level level;
+ Per_CPU_Control *cpu_self;
/*
* Some CPUs need to tinker with the call frame or registers when the
@@ -37,16 +37,21 @@ void _Thread_Handler( void )
*/
_Context_Initialization_at_thread_begin();
- #if !defined(RTEMS_SMP)
- /*
- * have to put level into a register for those cpu's that use
- * inline asm here
- */
- level = executing->Start.isr_level;
- _ISR_Set_level( level );
+ #if defined(RTEMS_SMP)
+ /* On SMP we enter _Thread_Handler() with interrupts disabled */
+ _Assert( _ISR_Get_level() != 0 );
+
+ _Thread_Debug_set_real_processor( executing, _Per_CPU_Get() );
#endif
/*
+ * have to put level into a register for those cpu's that use
+ * inline asm here
+ */
+ level = executing->Start.isr_level;
+ _ISR_Set_level( level );
+
+ /*
* Initialize the floating point context because we do not come
* through _Thread_Dispatch on our first invocation. So the normal
* code path for performing the FP context switch is not hit.
@@ -61,37 +66,24 @@ void _Thread_Handler( void )
_User_extensions_Thread_begin( executing );
/*
+ * Do not use the level of the thread control block, since it has a
+ * different format.
+ */
+ _ISR_Disable_without_giant( level );
+
+ /*
* At this point, the dispatch disable level BETTER be 1.
*/
- #if defined(RTEMS_SMP)
- {
- /*
- * On SMP we enter _Thread_Handler() with interrupts disabled and
- * _Thread_Dispatch() obtained the per-CPU lock for us. We have to
- * release it here and set the desired interrupt level of the thread.
- */
- Per_CPU_Control *cpu_self = _Per_CPU_Get();
-
- _Assert( cpu_self->thread_dispatch_disable_level == 1 );
- _Assert( _ISR_Get_level() != 0 );
-
- _Thread_Debug_set_real_processor( executing, cpu_self );
-
- cpu_self->thread_dispatch_disable_level = 0;
- _Profiling_Thread_dispatch_enable( cpu_self, 0 );
-
- level = executing->Start.isr_level;
- _ISR_Set_level( level);
-
- /*
- * The thread dispatch level changed from one to zero. Make sure we lose
- * no thread dispatch necessary update.
- */
- _Thread_Dispatch();
- }
- #else
- _Thread_Enable_dispatch();
- #endif
+ cpu_self = _Per_CPU_Get();
+ _Assert( cpu_self->thread_dispatch_disable_level == 1 );
+
+ /*
+ * Make sure we lose no thread dispatch necessary update and execute the
+ * post-switch actions. As a side-effect change the thread dispatch level
+ * from one to zero. Do not use _Thread_Enable_dispatch() since there is no
+ * valid thread dispatch necessary indicator in this context.
+ */
+ _Thread_Do_dispatch( cpu_self, level );
/*
* RTEMS supports multiple APIs and each API can define a different
diff --git a/testsuites/tmtests/tm26/task1.c b/testsuites/tmtests/tm26/task1.c
index 9685940..5d74872 100644
--- a/testsuites/tmtests/tm26/task1.c
+++ b/testsuites/tmtests/tm26/task1.c
@@ -99,6 +99,10 @@ static void set_thread_dispatch_necessary( bool dispatch_necessary )
_Thread_Dispatch_necessary = dispatch_necessary;
+ if ( !dispatch_necessary ) {
+ _Thread_Heir = _Thread_Executing;
+ }
+
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_ISR_Enable_without_giant( level );
#endif
--
1.8.4.5
More information about the devel
mailing list