[PATCH 2/4] score: SMP initialization and shutdown changes

Sebastian Huber sebastian.huber at embedded-brains.de
Wed Feb 26 08:43:33 UTC 2014


First some words to this patch set.  It is a gradual improvement of the 
existing SMP low-level initialization and shutdown procedure.

The existing solution was able to start an SMP system.  Only atomic 
reads/writes were used.  It is impossible to implement a controlled shutdown 
only with atomic reads/writes.  See also "The Art of Multiprocessor 
Programming", chapter 5, "The Relative Power of Primitive Synchronization 
Operations".

This patch introduces a statically initialized global SMP lock to manage the 
per-CPU state changes (_Per_CPU_State_lock).  It changes also the states and 
documents the state machine.

It is now also possible to use fatal error handlers to control the shutdown on 
the lowest-level.

Without this patch on NGMP the situation is like this:

***  SMP03 TEST ***
   CPU 3 running task Init
   CPU 2 running task TA1
   CPU 1 running task TA2
   CPU 0 running task TA3
   CPU 0 running task TA4
*** END OF TEST SMP03 ***
CPU 0:  Unknown watchpoint hit
         0x0000b1c4: 30800000  ba,a  0x0000B1C4  <rtems_smp_process_interrupt+172>
CPU 1:  Unknown watchpoint hit
         0x0000b1c4: 30800000  ba,a  0x0000B1C4  <rtems_smp_process_interrupt+172>
CPU 2:  Unknown watchpoint hit
         0x0000b1c4: 30800000  ba,a  0x0000B1C4  <rtems_smp_process_interrupt+172>
CPU 3:  Program exited normally.

Now I have this:

***  SMP03 TEST ***
   CPU 3 running task Init
   CPU 2 running task TA1
   CPU 1 running task TA2
   CPU 0 running task TA3
   CPU 0 running task TA4
*** END OF TEST SMP03 ***

   CPU 0:  Program exited normally.
   CPU 1:  Power down mode
   CPU 2:  Power down mode
   CPU 3:  Power down mode

I think we should try to discuss problems with a specific patch in one thread 
and general problems in a separate thread in the future.

On 2014-02-25 00:02, Chris Johns wrote:
> On 21/02/2014 6:48 pm, Sebastian Huber wrote:
>> On 2014-02-21 01:31, Chris Johns wrote:
>>> On 21/02/2014 3:31 am, Sebastian Huber wrote:
>>>> Hello Chris,
>>>>
>>>> On 2014-02-20 03:50, Chris Johns wrote:
>>>>> On 20/02/2014 12:42 am, Sebastian Huber wrote:
>>>>>> +/**
>>>>>> + * @brief State of a processor.
>>>>>> + *
>>>>>> + * @dot
>>>>>> + * digraph states {
>>>>>> + *   bi [label="PER_CPU_STATE_BEFORE_INITIALIZATION"];
>>>>>> + *   rsm [label="PER_CPU_STATE_READY_TO_START_MULTITASKING"];
>>>>>> + *   sm [label="PER_CPU_STATE_START_MULTITASKING"];
>>>>>> + *   ds [label="PER_CPU_STATE_DO_SHUTDOWN"];
>>>>>> + *   u [label="PER_CPU_STATE_UP"];
>>>>>> + *   s [label="PER_CPU_STATE_SHUTDOWN"];
>>>>>> + *   bi -> rsm [label="secondary processor\ncompleted
>>>>>> initialization"];
>>>>>> + *   bi -> u [label="main processor\nstarts multitasking"];
>>>>>> + *   rsm -> sm [label="main processor\ncompleted initialization"];
>>>>>> + *   rsm -> ds [label="a fatal error occurred"];
>>>>>> + *   ds -> s [label="do shutdown\nstate observed"];
>>>>>> + *   sm -> u [label="secondary processor\nstarts multitasking"];
>>>>>> + *   u -> s [label="shutdown initiated"];
>>>>>> + * }
>>>>>> + * @enddot
>>>>>
>>>>> I do not see sm to ds if the main core goes to s after going from bi
>>>>> to u ?
>>>>>
>>>>> I also see u to s and not to ds; why as the state is called "do
>>>>> shutdown" ?
>>>>> Should this state be PER_CPU_STATE_SHUTTING_DOWN ?
>>>>
>>>> I had to change the procedure considerably since I noticed unsolvable
>>>> problems with the previous approach based only on atomic read/write
>>>> operations. Attached is the new state diagram.
>>>>
>>>
>>> I do not follow this picture. I am assume there is one state machine
>>> per CPU
>>> and so I do not see where the state "starting multitasking" is. I see
>>> a state
>>> called "request start multitasking" then "up" and I am wondering what
>>> the state
>>> is after the request has happened and before up.
>>
>> The application defines a maximum count of processors (NP).  The
>> configuration will then define the _Per_CPU_Information[NP] table.  Each
>> table entry has one per-CPU state entry.
>
> Does the BSP define the max number of possible cores the hardware can support ?

Yes, via the return value of _CPU_SMP_Initialize().  This patch changes nothing 
in this area.

> I see later in this email you talk about AP being available processors and
> defined by the BSP. I think I am getting confused with the term "maximum count
> of processors". Does it mean the max count the application may require or the
> max count supported by the hardware ?

It is a mix of both.  The application configures a desired maximum count and 
the BSP may reduce it to match the actual hardware.

>
> There is another issue here I am struggling with and while specifically not on
> topic for this patch it relates to the configuration of processor count so
> please indulge me. The application having to define the number of cores means
> there is no default position and that means we have this ...
>
> http://git.rtems.org/rtems/tree/testsuites/smptests/smp01/system.h#n28
>
> which is based on this ...
>
> http://git.rtems.org/rtems/tree/cpukit/sapi/include/confdefs.h#n205
>
> A user builds RTEMS with --enable-smp and then has another gate in
> CONFIGURE_SMP_APPLICATION. This does not make sense. Why build for SMP and then
> never use it yet incur lots of overhead ? If I build RTEMS with SMP enabled for
> a BSP that supports SMP I would expect it to work with SMP and not default to
> some half way point. For example if you build the Zync A9 qemu BSP with SMP
> enabled and build all the tests then run them on a patched qemu with SMP
> support you happily see about 10 maybe 15 failures for over 480 tests however
> these result are meaningless because CONFIGURE_SMP_APPLICATION is only defined
> for a few SMP specific tests and incorrectly for the Zync because it only has 2
> cores and not 4. I feel we should not go to a release with the tests in this
> state even with clear documentation. If the tests fail with SMP we need to have
> the test results show this and document what the failures are so users are left
> with a clear picture of the state of SMP support. For example with the Zynq A9
> qemu example none of the block (libbd) tests should pass as libbd uses disable
> pre-emption and this should generate an error if used.
>
> The CONFIGURE_SMP_APPLICATION needs to go however there is no default core
> count defined by the BSP that can be used to allow this. IMO if the application
> does not define the core count it needs, ie less than the max supported by the
> hardware, the max possible should be used.

I didn't invent this CONFIGURE_SMP_APPLICATION.  At the moment it is useful 
since we lack important features like thread deletion.  With the introduction 
of clustered/partitioned scheduling the configuration will completely change. 
So I would like to postpone this discussion a couple of weeks.

>
>>  Since this table is in the BSS
>> section, all states start with PER_CPU_STATE_INITIAL.
>
> Who is clearing the BSS and how is this managed ? There seems to be some
> implicit requirements here. The requirement being "RTEMS requires all
> processors to run in an SMP environment must be started externally to RTEMS".
> Is this requirement what we really want ?

This is not the case.  The low-level start-up is highly BSP specific.  The 
leon3 BSP for example starts secondary processors on its own.  The QorIQ relies 
on U-Boot.  Our Altera Cyclone-V (ARM Cortex-A9 MPCore based) will also start 
secondary processors on its own.

>
> On the ARM (well the zynq I have used) starting processors is easy and the code
> needed is tiny and could be made available in the BSP shared area, while I
> understand on the PPC it is complex. I suppose the question is adding this
> support to RTEMS what we want to have and worth it verses adding this code
> gives us complete control over initialisation and makes the boot monitor simpler.
>
> Given I currently only use the ARM which is easy and I will not ship uboot due
> to licensing reason I favour adding support to RTEMS. :)
>
>>
>> The boot processor calls boot_card() and all other processors do what
>> they want, but they must wait until the boot processor gives the go
>> (explained later).
>>
>> The boot processor calls eventually:
>>
>>    /**
>>     * @brief Performs CPU specific SMP initialization in the context of
>> the boot
>>     * processor.
>>     *
>>     * 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.
>>     *
>>     * The CPU port should start secondary processors now.
>
> What does this mean ? I am not sure if you mean the boot monitor does this or
> this is handled in RTEMS in the BSP or related CPU code.
>
>>     *
>>     * @param[in] configured_cpu_count The count of processors requested
>> by the
>>     * application configuration.
>>     *
>>     * @return The count of processors available for the application in
>> the system.
>>     * This value is less than or equal to the configured count of
>> processors.
>>     */
>>    uint32_t _CPU_SMP_Initialize( uint32_t configured_cpu_count );
>>
>> So here has the CPU port (or BSP in most cases) the chance to reduce the
>> configured count of processors to the actually available (AP).  We have
>> AP <= NP.  In case this function returns "you have three processors",
>> then these three processors MUST start properly or terminate the
>> system.  If some processors are not always available or otherwise
>> unreliable this must be dealt with in _CPU_SMP_Initialize().
>
> This is confusing me. The application controls the max number of cores (NP) a
> system can have and the BSP (or CPU port which knows the max that can exist)
> can configure that down to the actual number. Does this mean I could define 32
> cores at the application and Zynq BSP would say, sorry only 2 here so this is
> what you get ?

Yes.  Its done in some tests, e.g.

http://git.rtems.org/rtems/tree/testsuites/smptests/smplock01/init.c#n28

Does this waste RAM ?

Yes, but its not much, e.g. the _Per_CPU_Information table is a bit bigger than 
necessary.

>
> What about an application defining 2 on a 4 core system ?

It gets two.

>
>>
>> System termination can happen at anytime on any processor.  So you can
>> change from every state into PER_CPU_STATE_SHUTDOWN.  This ability is
>> the core of this change set.  In the previous implementation this was
>> not guaranteed.
>>
>
> Did the state diagram show this ? I am sorry if I missed this.

Yes, you have arrows from all other states to PER_CPU_STATE_SHUTDOWN.

>
>> Now lets look at the normal start-up (no shutdown).  The next state
>> after PER_CPU_STATE_INITIAL is PER_CPU_STATE_READY_TO_START_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 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_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_START_MULTITASKING,
>>
>> The key point for a per-CPU state and not a global state is that every
>> processor must perform some initialization steps to set up the "I can
>> receive inter-processor interrupts (IPI)" state.  Before IPIs are
>> possible the only why are spin variables (e.g. this per-CPU state
>> variables).
>>
>> This PER_CPU_STATE_READY_TO_START_MULTITASKING is a synchronization
>> barrier.
>>
>> The boot processor will then set the state to
>> PER_CPU_STATE_REQUEST_START_MULTITASKING on all processors configured
>> and available (AP).  Once the secondary processor observes this state
>> change (it spins on its state variable), it will go into the
>> PER_CPU_STATE_UP state and perform a context switch to the first thread.
>>
>
> If this is about getting to a suitable IPI state on each core then why not have
> this in the states and functions rather then the global state type names of
> "start multitasking" etc.

I don't think we should be too specific about the IPI here.  The basic 
statement is that a processor is ready to start multitasking.

>
> I see the issue of needing a clear path from no IPI to having IPI however I am
> still wondering why the need to have the synch point and all cores to be there
> before moving on. A new core in the system should be able to see the system
> state and IPIs are active and then enable its support then add itself to the
> system.

Then you have to check each time which method you need to send a message to 
another processor.  I don't see a benefit here.

>
> If there is a global state variable with suitable spinning locks and atomics
> that direct the per cpu paths taken I fail to see why we need this sync
> barrier. The first core to a given global state makes the transition and the
> other cores need to wait or move to a different state. This assumes the BSS
> init issue is resolved.

I fail to see why this barrier is a problem.  The processors in an SMP system 
MUST work reliable if now or later it doesn't matter.  Do you really want to 
work an a system that works under this condition: "Sometimes scheduling actions 
take place, but you cannot assume that a thread will execute as planned.".

>
>>>
>>>>>
>>>>> I do not see why we have main and secondary processors ?
>>>>
>>>> I changed "main processor" into "boot processor" to highlight that this
>>>> is only the case during system boot.
>>>>
>>>
>>> Is there any code checking the processor number ? I see it in the
>>>
>>>>> This is symmetric
>>>>> multiprocessing which means each core is the same therefore capable of
>>>>> completing any required task. I understand there are paths which
>>>>> need to
>>>>> complete so if we have states for these phases as gates then any
>>>>> processor that
>>>>> arrives should be able to enter the gate (spinning lock for those that
>>>>> need to
>>>>> wait) and complete the work. This means a degraded state can exist and
>>>>> things
>>>>> at least start. The application would need to detect and manage the
>>>>> degraded
>>>>> state and so RTEMS should not be concerned with this condition other
>>>>> than doing
>>>>> its best to run where possible.
>>>>
>>>> In case the boot procedure of the system is unstable then it makes no
>>>> sense to run an application.
>>>>
>>>
>>> This depends on the application and its system requirements and how the
>>> hardware is constructed. A boot monitor can see cores are not present and
>>> decide not to start RTEMS or it can decide to start a single core.
>>> This relates
>>> to the system's requirement and can vary and has little to do with
>>> RTEMS. If a
>>> boot monitor passes control to RTEMS then it should run and only fail
>>> if it's
>>> integrity it not correct. RTEMS does should not move into the area of
>>> system
>>> requirements or constraints. Adding constraints like this in the
>>> operating
>>> system does not seem a good idea to me.
>>
>> I see no problem here.  See _CPU_SMP_Initialize() above.
>>
>
> I do not understand. Are you saying the design will run with a core count that
> does not match NP or AP ?

It is min { NP, AP }.

>
>>>
>>>>>
>>>>> I assume if we have n cores where n is 1..cpus available we enter the
>>>>> static
>>>>> constructors once and 'main' [1] once and this independent of the
>>>>> number of
>>>>> defined and/or operating cores. If an application's static constructor
>>>>> starts
>>>>> further threads it needs to manage the concurrency issues and main is
>>>>> only
>>>>> entered once.
>>>>
>>>> This change is about the low-level boot procedure.  High level concepts
>>>> like threads are not an issue here.
>>>>
>>>
>>> They are if you remove the all cores needing to sync plus using cpu
>>> numbers to
>>> control which cores go through to the static constructors and main and
>>> which
>>> cores go direct to the score thread code.
>>
>> If you remove this synchronization barrier via
>> PER_CPU_STATE_READY_TO_START_MULTITASKING, then you mandate that there
>> is boot loader that does these steps.  With the current set-up this boot
>> loader is optional.
>>
>
> Or the boot loading is not involved.
>
>>> It is this sort of code that concerns
>>> me ...
>>>
>>> http://git.rtems.org/rtems/tree/c/src/lib/libbsp/arm/shared/include/arm-a9mpcore-start.h#n88
>>>
>>>
>>
>> Yes, this is a dirty hack.  It works at the moment due to our immature
>> SMP state, but this must definitely change.
>>
>>>
>>>
>>> http://git.rtems.org/rtems/tree/c/src/lib/libbsp/sparc/shared/start/start.S#n226
>>>
>>>
>>
>> Yes, exactly the same issue.  This processor index 0 == boot processor
>> is a bad assumption.
>>
>
> I feel the single major issue here is a lack of defined requirement for SMP.
> The RTEMS Project and its community need to define the top level requirements
> and along with that tests that check those requirements. For me reviewing
> patches it is not clear what is a hack or temporary and what is long term and
> here for good. Questioning each patch or change to find this out is time
> consuming and not very productive.
[...]

Yes, we have the wiki page for this:

http://www.rtems.org/wiki/index.php?title=SMP

-- 
Sebastian Huber, embedded brains GmbH

Address : Dornierstr. 4, D-82178 Puchheim, Germany
Phone   : +49 89 189 47 41-16
Fax     : +49 89 189 47 41-09
E-Mail  : sebastian.huber at embedded-brains.de
PGP     : Public key available on request.

Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG.



More information about the devel mailing list