📄 smp.txt
字号:
All existing synchronization mechanisms work as before in an SMP
system. Additional synchronization mechanisms have been added to
provide explicit synchronization for SMP.
A set of functions have been added to the Kernel and device driver
APIs to provide spinlocks:
void cyg_spinlock_init( cyg_spinlock_t *lock, cyg_bool_t locked );
void cyg_drv_spinlock_init( cyg_spinlock_t *lock, cyg_bool_t locked );
void cyg_spinlock_destroy( cyg_spinlock_t *lock );
void cyg_drv_spinlock_destroy( cyg_spinlock_t *lock );
void cyg_spinlock_spin( cyg_spinlock_t *lock );
void cyg_drv_spinlock_spin( cyg_spinlock_t *lock );
void cyg_spinlock_clear( cyg_spinlock_t *lock );
void cyg_drv_spinlock_clear( cyg_spinlock_t *lock );
cyg_bool_t cyg_spinlock_try( cyg_spinlock_t *lock );
cyg_bool_t cyg_drv_spinlock_try( cyg_spinlock_t *lock );
cyg_bool_t cyg_spinlock_test( cyg_spinlock_t *lock );
cyg_bool_t cyg_drv_spinlock_test( cyg_spinlock_t *lock );
void cyg_spinlock_spin_intsave( cyg_spinlock_t *lock,
cyg_addrword_t *istate );
void cyg_drv_spinlock_spin_intsave( cyg_spinlock_t *lock,
cyg_addrword_t *istate );
void cyg_spinlock_clear_intsave( cyg_spinlock_t *lock,
cyg_addrword_t istate );
void cyg_drv_spinlock_clear_intsave( cyg_spinlock_t *lock,
cyg_addrword_t istate );
The *_init() functions initialize the lock, to either locked or clear,
and the *_destroy() functions destroy the lock. Init() should be called
before the lock is used and destroy() should be called when it is
finished with.
The *_spin() functions will cause the calling CPU to spin until it can
claim the lock and the *_clear() functions clear the lock so that the
next CPU can claim it. The *_try() functions attempts to claim the lock
but returns false if it cannot. The *_test() functions simply return
the state of the lock.
None of these functions will necessarily block interrupts while they
spin. If the spinlock is only to be used between threads on different
CPUs, or in circumstances where it is known that the relevant
interrupts are disabled, then these functions will suffice. However,
if the spinlock is also to be used from an ISR, which may be called at
any point, a straightforward spinlock may result in deadlock. Hence
the *_intsave() variants are supplied to disable interrupts while the
lock is held.
The *_spin_intsave() function disables interrupts, saving the current
state in *istate, and then claims the lock. The *_clear_intsave()
function clears the spinlock and restores the interrupt enable state
from *istate.
HAL Support
-----------
SMP support in any platform depends on the HAL supplying the
appropriate operations. All HAL SMP support is defined in the
hal_smp.h header (and if necessary var_smp.h and plf_smp.h).
SMP support falls into a number of functional groups.
CPU Control
~~~~~~~~~~~
This group consists of descriptive and control macros for managing the
CPUs in an SMP system.
HAL_SMP_CPU_TYPE A type that can contain a CPU id. A CPU id is
usually a small integer that is used to index
arrays of variables that are managed on an
per-CPU basis.
HAL_SMP_CPU_MAX The maximum number of CPUs that can be
supported. This is used to provide the size of
any arrays that have an element per CPU.
HAL_SMP_CPU_COUNT() Returns the number of CPUs currently
operational. This may differ from
HAL_SMP_CPU_MAX depending on the runtime
environment.
HAL_SMP_CPU_THIS() Returns the CPU id of the current CPU.
HAL_SMP_CPU_NONE A value that does not match any real CPU
id. This is uses where a CPU type variable
must be set to a nul value.
HAL_SMP_CPU_START( cpu )
Starts the given CPU executing at a defined
HAL entry point. After performing any HAL
level initialization, the CPU calls up into
the kernel at cyg_kernel_cpu_startup().
HAL_SMP_CPU_RESCHEDULE_INTERRUPT( cpu, wait )
Sends the CPU a reschedule interrupt, and if
_wait_ is non-zero, waits for an
acknowledgment. The interrupted CPU should
call cyg_scheduler_set_need_reschedule() in
its DSR to cause the reschedule to occur.
HAL_SMP_CPU_TIMESLICE_INTERRUPT( cpu, wait )
Sends the CPU a timeslice interrupt, and if
_wait_ is non-zero, waits for an
acknowledgment. The interrupted CPU should
call cyg_scheduler_timeslice_cpu() to cause
the timeslice event to be processed.
Test-and-set Support
~~~~~~~~~~~~~~~~~~~~
Test-and-set is the foundation of the SMP synchronization
mechanisms.
HAL_TAS_TYPE The type for all test-and-set variables. The
test-and-set macros only support operations on
a single bit (usually the least significant
bit) of this location. This allows for maximum
flexibility in the implementation.
HAL_TAS_SET( tas, oldb )
Performs a test and set operation on the
location _tas_. _oldb_ will contain *true* if
the location was already set, and *false* if
it was clear.
HAL_TAS_CLEAR( tas, oldb )
Performs a test and clear operation on the
location _tas_. _oldb_ will contain *true* if
the location was already set, and *false* if
it was clear.
Spinlocks
~~~~~~~~~
Spinlocks provide inter-CPU locking. Normally they will be implemented
on top of the test-and-set mechanism above, but may also be
implemented by other means if, for example, the hardware has more
direct support for spinlocks.
HAL_SPINLOCK_TYPE The type for all spinlock variables.
HAL_SPINLOCK_INIT_CLEAR A value that may be assigned to a spinlock
variable to initialize it to clear.
HAL_SPINLOCK_INIT_SET A value that may be assigned to a spinlock
variable to initialize it to set.
HAL_SPINLOCK_SPIN( lock )
The caller spins in a busy loop waiting for
the lock to become clear. It then sets it and
continues. This is all handled atomically, so
that there are no race conditions between CPUs.
HAL_SPINLOCK_CLEAR( lock )
The caller clears the lock. One of any waiting
spinners will then be able to proceed.
HAL_SPINLOCK_TRY( lock, val )
Attempts to set the lock. The value put in
_val_ will be *true* if the lock was
claimed successfully, and *false* if it was
not.
HAL_SPINLOCK_TEST( lock, val )
Tests the current value of the lock. The value
put in _val_ will be *true* if the lock is
claimed and *false* of it is clear.
Scheduler Lock
~~~~~~~~~~~~~~
The scheduler lock is the main protection for all kernel data
structures. By default the kernel implements the scheduler lock itself
using a spinlock. However, if spinlocks cannot be supported by the
hardware, or there is a more efficient implementation available, the
HAL may provide macros to implement the scheduler lock.
HAL_SMP_SCHEDLOCK_DATA_TYPE
A data type, possibly a structure, that
contains any data items needed by the
scheduler lock implementation. A variable of
this type will be instantiated as a static
member of the Cyg_Scheduler_SchedLock class
and passed to all the following macros.
HAL_SMP_SCHEDLOCK_INIT( lock, data )
Initialize the scheduler lock. The _lock_
argument is the scheduler lock counter and the
_data_ argument is a variable of
HAL_SMP_SCHEDLOCK_DATA_TYPE type.
HAL_SMP_SCHEDLOCK_INC( lock, data )
Increment the scheduler lock. The first
increment of the lock from zero to one for any
CPU may cause it to wait until the lock is
zeroed by another CPU. Subsequent increments
should be less expensive since this CPU
already holds the lock.
HAL_SMP_SCHEDLOCK_ZERO( lock, data )
Zero the scheduler lock. This operation will
also clear the lock so that other CPUs may
claim it.
HAL_SMP_SCHEDLOCK_SET( lock, data, new )
Set the lock to a different value, in
_new_. This is only called when the lock is
already known to be owned by the current
CPU. It is never called to zero the lock, or
to increment it from zero.
Interrupt Routing
~~~~~~~~~~~~~~~~~
The routing of interrupts to different CPUs is supported by two new
interfaces in hal_intr.h.
Once an interrupt has been routed to a new CPU, the existing vector
masking and configuration operations should take account of the CPU
routing. For example, if the operation is not invoked on the
destination CPU itself, then the HAL may need to arrange to transfer
the operation to the destination CPU for correct application.
HAL_INTERRUPT_SET_CPU( vector, cpu )
Route the interrupt for the given _vector_ to
the given _cpu_.
HAL_INTERRUPT_GET_CPU( vector, cpu )
Set _cpu_ to the id of the CPU to which this
vector is routed.
Annex 1 - Pentium SMP Support
=============================
ECos supports SMP working on Pentium class IA32 CPUs with integrated
SMP support. It uses the per-CPU APIC's and the IOAPIC to provide CPU
control and identification, and to distribute interrupts. Only PCI
interrupts that map into the ISA interrupt space are currently
supported. The code relies on the MP Configuration Table supplied by
the BIOS to discover the number of CPUs, IOAPIC location and interrupt
assignments - hardware based MP configuration discovery is
not currently supported.
Inter-CPU interrupts are mapped into interrupt vectors from 64
up. Each CPU has its own vector at 64+CPUID.
Interrupt delivery is initially configured to deliver all interrupts
to the initial CPU. HAL_INTERRUPT_SET_CPU() currently only supports
the ability to deliver interrupts to specific CPUs, dynamic CPU
selection is not currently supported.
eCos has only been tested in a dual processor configuration. While the
code has been written to handle an arbitrary number of CPUs, this has
not been tested.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -