summaryrefslogblamecommitdiffstats
path: root/cpukit/score/src/percpu.c
blob: 0e4c0678e722653458248e73f7e86554fe81be90 (plain) (tree)
1
2
3
4
5
6
7
8
9






                                                    
  
                            



                                                           
                                         





                   
                               
                               
                                

                                
 









                                                 
                      
 






































                                                                             
 
                                     
                       


                         
                                   







                                                          
                           






                                                         
                                                         
                                           
                           

















































                                                       

   



                           
                       


                         
                                

                           
                                             
 
                                          
 

                                                                

                                               

                                                                    
 

                                                                      
 

                                     
                                
                                                                 





                               
                                                  
       
     
   


                                       
                                          




                                            
                                     


   







































                                                                             





                                                                            
                                                   
      
/**
 * @file
 *
 * @brief Allocate and Initialize Per CPU Structures
 * @ingroup PerCPU
 */

/*
 *  COPYRIGHT (c) 1989-2011.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems/score/percpu.h>
#include <rtems/score/assert.h>
#include <rtems/score/isrlock.h>
#include <rtems/score/smpimpl.h>
#include <rtems/config.h>

RTEMS_STATIC_ASSERT(
  sizeof( CPU_Uint32ptr ) >= sizeof( uintptr_t ),
  CPU_Uint32ptr_greater_equal_uintptr_t
);

RTEMS_STATIC_ASSERT(
  sizeof( CPU_Uint32ptr ) >= sizeof( uint32_t ),
  CPU_Uint32ptr_greater_equal_uint32_t
);

#if defined(RTEMS_SMP)

typedef struct {
  SMP_Action_handler handler;
  void *arg;
} SMP_Before_multicast_action;

ISR_LOCK_DEFINE( static, _Per_CPU_State_lock, "Per-CPU State" )

static void _Per_CPU_State_acquire( ISR_lock_Context *lock_context )
{
  _ISR_lock_ISR_disable_and_acquire( &_Per_CPU_State_lock, lock_context );
}

static void _Per_CPU_State_release( ISR_lock_Context *lock_context )
{
  _ISR_lock_Release_and_ISR_enable( &_Per_CPU_State_lock, lock_context );
}

static void _Per_CPU_State_before_multitasking_action( Per_CPU_Control *cpu )
{
  uintptr_t action_value;

  action_value = _Atomic_Load_uintptr(
    &cpu->before_multitasking_action,
    ATOMIC_ORDER_ACQUIRE
  );

  if ( action_value != 0 ) {
    SMP_Before_multicast_action *action =
      (SMP_Before_multicast_action *) action_value;

    ( *action->handler )( action->arg );

    _Atomic_Store_uintptr(
      &cpu->before_multitasking_action,
      0,
      ATOMIC_ORDER_RELEASE
    );
  }
}

static void _Per_CPU_State_busy_wait(
  Per_CPU_Control *cpu,
  Per_CPU_State new_state
)
{
  Per_CPU_State state = 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 = cpu->state;
      }
      break;
    case PER_CPU_STATE_UP:
      while (
        state != PER_CPU_STATE_REQUEST_START_MULTITASKING
          && state != PER_CPU_STATE_SHUTDOWN
      ) {
        _Per_CPU_State_before_multitasking_action( cpu );
        _CPU_SMP_Processor_event_receive();
        state = 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;
  }

  return new_state;
}

void _Per_CPU_State_change(
  Per_CPU_Control *cpu,
  Per_CPU_State new_state
)
{
  ISR_lock_Context lock_context;
  Per_CPU_State next_state;

  _Per_CPU_State_busy_wait( cpu, new_state );

  _Per_CPU_State_acquire( &lock_context );

  next_state = _Per_CPU_State_get_next( cpu->state, new_state );
  cpu->state = next_state;

  if ( next_state == PER_CPU_STATE_SHUTDOWN ) {
    uint32_t cpu_max = rtems_configuration_get_maximum_processors();
    uint32_t cpu_index;

    for ( cpu_index = 0 ; cpu_index < cpu_max ; ++cpu_index ) {
      Per_CPU_Control *cpu_other = _Per_CPU_Get_by_index( cpu_index );

      if ( cpu_other != cpu ) {
        switch ( cpu_other->state ) {
          case PER_CPU_STATE_UP:
            _SMP_Send_message( cpu_index, SMP_MESSAGE_SHUTDOWN );
            break;
          default:
            /* Nothing to do */
            break;
        }

        cpu_other->state = PER_CPU_STATE_SHUTDOWN;
      }
    }
  }

  _CPU_SMP_Processor_event_broadcast();

  _Per_CPU_State_release( &lock_context );

  if (
    next_state == PER_CPU_STATE_SHUTDOWN
      && new_state != PER_CPU_STATE_SHUTDOWN
  ) {
    _SMP_Fatal( SMP_FATAL_SHUTDOWN );
  }
}

bool _SMP_Before_multitasking_action(
  Per_CPU_Control    *cpu,
  SMP_Action_handler  handler,
  void               *arg
)
{
  bool done;

  _Assert( _Per_CPU_Is_boot_processor( _Per_CPU_Get() ) );

  if ( _Per_CPU_Is_processor_online( cpu ) ) {
    SMP_Before_multicast_action action = {
      .handler = handler,
      .arg = arg
    };
    Per_CPU_State expected_state = PER_CPU_STATE_READY_TO_START_MULTITASKING;

    _Atomic_Store_uintptr(
      &cpu->before_multitasking_action,
      (uintptr_t) &action,
      ATOMIC_ORDER_RELEASE
    );

    _CPU_SMP_Processor_event_broadcast();

    _Per_CPU_State_busy_wait( cpu, expected_state );

    do {
      done = _Atomic_Load_uintptr(
        &cpu->before_multitasking_action,
        ATOMIC_ORDER_ACQUIRE
      ) == 0;
    } while ( !done && cpu->state == expected_state );
  } else {
    done = false;
  }

  return done;
}

#else
  /*
   * On single core systems, we can efficiently directly access a single
   * statically allocated per cpu structure.  And the fields are initialized
   * as individual elements just like it has always been done.
   */
  Per_CPU_Control_envelope _Per_CPU_Information[1];
#endif