From 7336be9d78266adfb540170e5105caf8eb003d2f Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 18 Feb 2014 13:40:39 +0100 Subject: score: SMP initialization and shutdown changes Rename _SMP_Request_other_cores_to_perform_first_context_switch() into _SMP_Request_start_multitasking() since this requests now a multitasking start on all configured and available processors. The name corresponds _Thread_Start_multitasking() and _SMP_Start_multitasking_on_secondary_processor() actions issued in response to this request. Move in source file to right place. Rename PER_CPU_STATE_READY_TO_BEGIN_MULTITASKING into PER_CPU_STATE_READY_TO_START_MULTITASKING. Rename PER_CPU_STATE_BEGIN_MULTITASKING into PER_CPU_STATE_REQUEST_START_MULTITASKING. Rename _SMP_Request_other_cores_to_shutdown() into _SMP_Request_shutdown(). Add a per-CPU state lock to protect all changes. This was necessary to offer a controlled shutdown of the system (atomic read/writes alone are not sufficient for this kind of synchronization). Add documentation for Per_CPU_State. Delete debug output. New tests smptests/smpfatal01 and smptests/smpfatal02. --- cpukit/sapi/src/exinit.c | 2 +- cpukit/score/cpu/no_cpu/rtems/score/cpu.h | 4 +- cpukit/score/include/rtems/score/percpu.h | 82 ++++++++------- cpukit/score/include/rtems/score/smpimpl.h | 22 ++-- cpukit/score/src/interr.c | 2 +- cpukit/score/src/percpu.c | 158 ++++++++++++++++++++++++++--- cpukit/score/src/smp.c | 77 +++++--------- cpukit/score/src/threadstartmultitasking.c | 2 +- 8 files changed, 230 insertions(+), 119 deletions(-) (limited to 'cpukit') diff --git a/cpukit/sapi/src/exinit.c b/cpukit/sapi/src/exinit.c index d265455afd..ee1c7fdecc 100644 --- a/cpukit/sapi/src/exinit.c +++ b/cpukit/sapi/src/exinit.c @@ -210,7 +210,7 @@ void rtems_initialize_start_multitasking(void) { _System_state_Set( SYSTEM_STATE_UP ); - _SMP_Request_other_cores_to_perform_first_context_switch(); + _SMP_Request_start_multitasking(); _Thread_Start_multitasking(); diff --git a/cpukit/score/cpu/no_cpu/rtems/score/cpu.h b/cpukit/score/cpu/no_cpu/rtems/score/cpu.h index 5c34e17d03..4155883f79 100644 --- a/cpukit/score/cpu/no_cpu/rtems/score/cpu.h +++ b/cpukit/score/cpu/no_cpu/rtems/score/cpu.h @@ -1462,10 +1462,10 @@ CPU_Counter_ticks _CPU_Counter_difference( #ifdef RTEMS_SMP /** - * @brief Performs CPU specific SMP initialization in the context of the main + * @brief Performs CPU specific SMP initialization in the context of the boot * processor. * - * This function is invoked on the main processor by RTEMS during + * This function is invoked on the boot processor by RTEMS during * initialization. All interrupt stacks are allocated at this point in case * the CPU port allocates the interrupt stacks. * diff --git a/cpukit/score/include/rtems/score/percpu.h b/cpukit/score/include/rtems/score/percpu.h index 4c46b50425..ca9185ea7a 100644 --- a/cpukit/score/include/rtems/score/percpu.h +++ b/cpukit/score/include/rtems/score/percpu.h @@ -70,64 +70,83 @@ typedef struct Thread_Control_struct Thread_Control; #error "deferred FP switch not implemented for SMP" #endif +/** + * @brief State of a processor. + * + * The processor state controls the life cycle of processors at the lowest + * level. No multi-threading or other high-level concepts matter here. + * + * State changes must be initiated via _Per_CPU_Change_state(). This function + * may not return in case someone requested a shutdown. The + * _SMP_Send_message() function will be used to notify other processors about + * state changes if the other processor is in the up state. + * + * Due to the sequential nature of the basic system initialization one + * processor has a special role. It is the processor executing the boot_card() + * function. This processor is called the boot processor. All other + * processors are called secondary. + * + * @dot + * digraph states { + * i [label="PER_CPU_STATE_INITIAL"]; + * rdy [label="PER_CPU_STATE_READY_TO_START_MULTITASKING"]; + * reqsm [label="PER_CPU_STATE_REQUEST_START_MULTITASKING"]; + * u [label="PER_CPU_STATE_UP"]; + * s [label="PER_CPU_STATE_SHUTDOWN"]; + * i -> rdy [label="processor\ncompleted initialization"]; + * rdy -> reqsm [label="boot processor\ncompleted initialization"]; + * reqsm -> u [label="processor\nstarts multitasking"]; + * i -> s; + * rdy -> s; + * reqsm -> s; + * u -> s; + * } + * @enddot + */ typedef enum { /** * @brief The per CPU controls are initialized to zero. * - * In this state the only valid field of the per CPU controls for secondary - * processors is the per CPU state. The secondary processors should perform - * their basic initialization now and change into the - * PER_CPU_STATE_READY_TO_BEGIN_MULTITASKING state once this is complete. - * - * The owner of the per CPU state field is the secondary processor in this - * state. + * The boot processor executes the sequential boot code in this state. The + * secondary processors should perform their basic initialization now and + * change into the PER_CPU_STATE_READY_TO_START_MULTITASKING state once this + * is complete. */ - PER_CPU_STATE_BEFORE_INITIALIZATION, + PER_CPU_STATE_INITIAL, /** - * @brief Secondary processor is ready to begin multitasking. + * @brief Processor is ready to start multitasking. * * The secondary processor performed its basic initialization and is ready to * receive inter-processor interrupts. Interrupt delivery must be disabled * in this state, but requested inter-processor interrupts must be recorded * and must be delivered once the secondary processor enables interrupts for - * the first time. The main processor will wait for all secondary processors + * the first time. The boot processor will wait for all secondary processors * to change into this state. In case a secondary processor does not reach * this state the system will not start. The secondary processors wait now - * for a change into the PER_CPU_STATE_BEGIN_MULTITASKING state set by the - * main processor once all secondary processors reached the - * PER_CPU_STATE_READY_TO_BEGIN_MULTITASKING state. - * - * The owner of the per CPU state field is the main processor in this state. + * for a change into the PER_CPU_STATE_REQUEST_START_MULTITASKING state set + * by the boot processor once all secondary processors reached the + * PER_CPU_STATE_READY_TO_START_MULTITASKING state. */ - PER_CPU_STATE_READY_TO_BEGIN_MULTITASKING, + PER_CPU_STATE_READY_TO_START_MULTITASKING, /** - * @brief Multitasking begin of secondary processor is requested. + * @brief Multitasking start of processor is requested. * - * The main processor completed system initialization and is about to perform + * The boot processor completed system initialization and is about to perform * a context switch to its heir thread. Secondary processors should now * issue a context switch to the heir thread. This normally enables * interrupts on the processor for the first time. - * - * The owner of the per CPU state field is the secondary processor in this - * state. */ - PER_CPU_STATE_BEGIN_MULTITASKING, + PER_CPU_STATE_REQUEST_START_MULTITASKING, /** * @brief Normal multitasking state. - * - * The owner of the per CPU state field is the secondary processor in this - * state. */ PER_CPU_STATE_UP, /** * @brief This is the terminal state. - * - * The owner of the per CPU state field is the secondary processor in this - * state. */ PER_CPU_STATE_SHUTDOWN } Per_CPU_State; @@ -313,16 +332,11 @@ static inline void _Per_CPU_Send_interrupt( const Per_CPU_Control *per_cpu ) */ void _Per_CPU_Initialize(void); -void _Per_CPU_Change_state( +void _Per_CPU_State_change( Per_CPU_Control *per_cpu, Per_CPU_State new_state ); -void _Per_CPU_Wait_for_state( - const Per_CPU_Control *per_cpu, - Per_CPU_State desired_state -); - #endif /* defined( RTEMS_SMP ) */ /* diff --git a/cpukit/score/include/rtems/score/smpimpl.h b/cpukit/score/include/rtems/score/smpimpl.h index d68af43916..da08cf54e8 100644 --- a/cpukit/score/include/rtems/score/smpimpl.h +++ b/cpukit/score/include/rtems/score/smpimpl.h @@ -108,8 +108,6 @@ static inline void _SMP_Inter_processor_interrupt_handler( void ) _Per_CPU_Release_and_ISR_enable( self_cpu, level ); if ( ( message & SMP_MESSAGE_SHUTDOWN ) != 0 ) { - _Per_CPU_Change_state( self_cpu, PER_CPU_STATE_SHUTDOWN ); - rtems_fatal( RTEMS_FATAL_SOURCE_SMP, SMP_FATAL_SHUTDOWN ); /* does not continue past here */ } @@ -143,27 +141,27 @@ void _SMP_Broadcast_message( #endif /* defined( RTEMS_SMP ) */ /** - * @brief Request other cores to perform first context switch. - * - * Send message to other cores requesting them to perform - * their first context switch operation. + * @brief Requests a multitasking start on all configured and available + * processors. */ #if defined( RTEMS_SMP ) - void _SMP_Request_other_cores_to_perform_first_context_switch( void ); + void _SMP_Request_start_multitasking( void ); #else - #define _SMP_Request_other_cores_to_perform_first_context_switch() \ + #define _SMP_Request_start_multitasking() \ do { } while ( 0 ) #endif /** - * @brief Request other cores to shutdown. + * @brief Requests a shutdown of all processors. + * + * This function is a part of the system termination procedure. * - * Send message to other cores requesting them to shutdown. + * @see _Terminate(). */ #if defined( RTEMS_SMP ) - void _SMP_Request_other_cores_to_shutdown( void ); + void _SMP_Request_shutdown( void ); #else - #define _SMP_Request_other_cores_to_shutdown() \ + #define _SMP_Request_shutdown() \ do { } while ( 0 ) #endif diff --git a/cpukit/score/src/interr.c b/cpukit/score/src/interr.c index c2a9fbe00f..11578a6101 100644 --- a/cpukit/score/src/interr.c +++ b/cpukit/score/src/interr.c @@ -39,7 +39,7 @@ void _Terminate( _ISR_Disable_without_giant( level ); (void) level; - _SMP_Request_other_cores_to_shutdown(); + _SMP_Request_shutdown(); _User_extensions_Fatal( the_source, is_internal, the_error ); diff --git a/cpukit/score/src/percpu.c b/cpukit/score/src/percpu.c index c68f378d1a..3a7a84518e 100644 --- a/cpukit/score/src/percpu.c +++ b/cpukit/score/src/percpu.c @@ -19,26 +19,156 @@ #endif #include +#include +#include +#include +#include #if defined(RTEMS_SMP) - void _Per_CPU_Change_state( - Per_CPU_Control *per_cpu, - Per_CPU_State new_state - ) - { - per_cpu->state = new_state; - _CPU_SMP_Processor_event_broadcast(); + +static SMP_lock_Control _Per_CPU_State_lock = SMP_LOCK_INITIALIZER; + +static ISR_Level _Per_CPU_State_acquire( void ) +{ + ISR_Level level; + + _SMP_lock_ISR_disable_and_acquire( &_Per_CPU_State_lock, level ); + + return level; +} + +static void _Per_CPU_State_release( ISR_Level level ) +{ + _SMP_lock_Release_and_ISR_enable( &_Per_CPU_State_lock, level ); +} + +static void _Per_CPU_State_busy_wait( + const Per_CPU_Control *per_cpu, + Per_CPU_State new_state +) +{ + Per_CPU_State state = per_cpu->state; + + switch ( new_state ) { + case PER_CPU_STATE_REQUEST_START_MULTITASKING: + while ( + state != PER_CPU_STATE_READY_TO_START_MULTITASKING + && state != PER_CPU_STATE_SHUTDOWN + ) { + _CPU_SMP_Processor_event_receive(); + state = per_cpu->state; + } + break; + case PER_CPU_STATE_UP: + while ( + state != PER_CPU_STATE_REQUEST_START_MULTITASKING + && state != PER_CPU_STATE_SHUTDOWN + ) { + _CPU_SMP_Processor_event_receive(); + state = per_cpu->state; + } + break; + default: + /* No need to wait */ + break; + } +} + +static Per_CPU_State _Per_CPU_State_get_next( + Per_CPU_State current_state, + Per_CPU_State new_state +) +{ + switch ( current_state ) { + case PER_CPU_STATE_INITIAL: + switch ( new_state ) { + case PER_CPU_STATE_READY_TO_START_MULTITASKING: + case PER_CPU_STATE_SHUTDOWN: + /* Change is acceptable */ + break; + default: + new_state = PER_CPU_STATE_SHUTDOWN; + break; + } + break; + case PER_CPU_STATE_READY_TO_START_MULTITASKING: + switch ( new_state ) { + case PER_CPU_STATE_REQUEST_START_MULTITASKING: + case PER_CPU_STATE_SHUTDOWN: + /* Change is acceptable */ + break; + default: + new_state = PER_CPU_STATE_SHUTDOWN; + break; + } + break; + case PER_CPU_STATE_REQUEST_START_MULTITASKING: + switch ( new_state ) { + case PER_CPU_STATE_UP: + case PER_CPU_STATE_SHUTDOWN: + /* Change is acceptable */ + break; + default: + new_state = PER_CPU_STATE_SHUTDOWN; + break; + } + break; + default: + new_state = PER_CPU_STATE_SHUTDOWN; + break; } - void _Per_CPU_Wait_for_state( - const Per_CPU_Control *per_cpu, - Per_CPU_State desired_state - ) - { - while ( per_cpu->state != desired_state ) { - _CPU_SMP_Processor_event_receive(); + return new_state; +} + +void _Per_CPU_State_change( + Per_CPU_Control *per_cpu, + Per_CPU_State new_state +) +{ + ISR_Level level; + Per_CPU_State next_state; + + _Per_CPU_State_busy_wait( per_cpu, new_state ); + + level = _Per_CPU_State_acquire(); + next_state = _Per_CPU_State_get_next( per_cpu->state, new_state ); + per_cpu->state = next_state; + + if ( next_state == PER_CPU_STATE_SHUTDOWN ) { + uint32_t ncpus = rtems_configuration_get_maximum_processors(); + uint32_t cpu; + + for ( cpu = 0 ; cpu < ncpus ; ++cpu ) { + Per_CPU_Control *other_cpu = _Per_CPU_Get_by_index( cpu ); + + if ( per_cpu != other_cpu ) { + switch ( other_cpu->state ) { + case PER_CPU_STATE_UP: + _SMP_Send_message( cpu, SMP_MESSAGE_SHUTDOWN ); + break; + default: + /* Nothing to do */ + break; + } + + other_cpu->state = PER_CPU_STATE_SHUTDOWN; + } } } + + _CPU_SMP_Processor_event_broadcast(); + + _Per_CPU_State_release( level ); + + if ( + next_state == PER_CPU_STATE_SHUTDOWN + && new_state != PER_CPU_STATE_SHUTDOWN + ) { + rtems_fatal( RTEMS_FATAL_SOURCE_SMP, SMP_FATAL_SHUTDOWN ); + } +} + #else /* * On single core systems, we can efficiently directly access a single diff --git a/cpukit/score/src/smp.c b/cpukit/score/src/smp.c index 59036eb466..0f632236b7 100644 --- a/cpukit/score/src/smp.c +++ b/cpukit/score/src/smp.c @@ -24,10 +24,6 @@ #include #include -#if defined(RTEMS_DEBUG) - #include -#endif - void _SMP_Handler_initialize( void ) { uint32_t max_cpus = rtems_configuration_get_maximum_processors(); @@ -47,21 +43,38 @@ void _SMP_Handler_initialize( void ) _SMP_Processor_count = max_cpus; } -void _SMP_Start_multitasking_on_secondary_processor( void ) +void _SMP_Request_start_multitasking( void ) { Per_CPU_Control *self_cpu = _Per_CPU_Get(); + uint32_t ncpus = _SMP_Get_processor_count(); + uint32_t cpu; + + _Per_CPU_State_change( self_cpu, PER_CPU_STATE_READY_TO_START_MULTITASKING ); - #if defined(RTEMS_DEBUG) - printk( "Made it to %d -- ", _Per_CPU_Get_index( self_cpu ) ); - #endif + for ( cpu = 0 ; cpu < ncpus ; ++cpu ) { + Per_CPU_Control *per_cpu = _Per_CPU_Get_by_index( cpu ); + + _Per_CPU_State_change( per_cpu, PER_CPU_STATE_REQUEST_START_MULTITASKING ); + } +} - _Per_CPU_Change_state( self_cpu, PER_CPU_STATE_READY_TO_BEGIN_MULTITASKING ); +void _SMP_Start_multitasking_on_secondary_processor( void ) +{ + Per_CPU_Control *self_cpu = _Per_CPU_Get(); - _Per_CPU_Wait_for_state( self_cpu, PER_CPU_STATE_BEGIN_MULTITASKING ); + _Per_CPU_State_change( self_cpu, PER_CPU_STATE_READY_TO_START_MULTITASKING ); _Thread_Start_multitasking(); } +void _SMP_Request_shutdown( void ) +{ + uint32_t self = _SMP_Get_current_processor(); + Per_CPU_Control *self_cpu = _Per_CPU_Get_by_index( self ); + + _Per_CPU_State_change( self_cpu, PER_CPU_STATE_SHUTDOWN ); +} + void _SMP_Send_message( uint32_t cpu, uint32_t message ) { Per_CPU_Control *per_cpu = _Per_CPU_Get_by_index( cpu ); @@ -88,47 +101,3 @@ void _SMP_Broadcast_message( uint32_t message ) } } } - -void _SMP_Request_other_cores_to_perform_first_context_switch( void ) -{ - uint32_t self = _SMP_Get_current_processor(); - uint32_t ncpus = _SMP_Get_processor_count(); - uint32_t cpu; - - for ( cpu = 0 ; cpu < ncpus ; ++cpu ) { - Per_CPU_Control *per_cpu = _Per_CPU_Get_by_index( cpu ); - - if ( cpu != self ) { - _Per_CPU_Wait_for_state( - per_cpu, - PER_CPU_STATE_READY_TO_BEGIN_MULTITASKING - ); - - _Per_CPU_Change_state( per_cpu, PER_CPU_STATE_BEGIN_MULTITASKING ); - } - } -} - -void _SMP_Request_other_cores_to_shutdown( void ) -{ - uint32_t self = _SMP_Get_current_processor(); - - /* - * Do not use _SMP_Get_processor_count() since this value might be not - * initialized yet. For example due to a fatal error in the middle of - * _CPU_SMP_Initialize(). - */ - uint32_t ncpus = rtems_configuration_get_maximum_processors(); - - uint32_t cpu; - - for ( cpu = 0 ; cpu < ncpus ; ++cpu ) { - if ( cpu != self ) { - const Per_CPU_Control *per_cpu = _Per_CPU_Get_by_index( cpu ); - - if ( per_cpu->state != PER_CPU_STATE_BEFORE_INITIALIZATION ) { - _SMP_Send_message( cpu, SMP_MESSAGE_SHUTDOWN ); - } - } - } -} diff --git a/cpukit/score/src/threadstartmultitasking.c b/cpukit/score/src/threadstartmultitasking.c index d73e5b4194..2c40170f36 100644 --- a/cpukit/score/src/threadstartmultitasking.c +++ b/cpukit/score/src/threadstartmultitasking.c @@ -26,7 +26,7 @@ void _Thread_Start_multitasking( void ) Thread_Control *heir; #if defined(RTEMS_SMP) - _Per_CPU_Change_state( self_cpu, PER_CPU_STATE_UP ); + _Per_CPU_State_change( self_cpu, PER_CPU_STATE_UP ); /* * Threads begin execution in the _Thread_Handler() function. This -- cgit v1.2.3