Concerns regarding SMP-mode vs. non-SMP mode in SysBios (and in general)
------------------------------------------------------------------------
1.
Choosing the right synchronization mechanisms for
a) Task/Task mutual exclusion in SMP-mode,
b) Task/ISR mutual exclusion in SMP-mode and
c) Task/Task/ISR mutual exclusion in SMP-mode
d) Furthermore, it shall be possible to signal events from ISR
There are several implications:
- Task/Task mutual exclusion could be implemented using Semaphores, but then no
access from ISR possible because you cannot call Semaphore_pend() in ISR context.
- Task/ISR mutual exclusion could be implemented using GateHwi (i.e. disabling ISRs).
However, this does not protect against mutual exclusion from another task in SMP
mode. Please note that it _would_ protect in non-SMP mode! This is one of the
reasons why we prefer non-SMP mode.
- Task/Task/ISR is really tricky. It is possible in other operating systems,
e.g. Linux Kernel using Spinlocks. But I don't know any SysBios
synchronization mechanism that allows us to implement this.
Summary of (1): In non-SMP mode it is trivial to implement the needed primitives.
In SMP-mode it is unclear how and whether this is possible at all.
2.
Ensuring correctness regarding parallel execution in presence of ARMv7's relaxed
memory model, where the following reordering is allowed for two memory accesses
to two different addresses A and B:
- a load from A is reordered after a load from B (and the load from A precedes the load from B in program order)
- a load from A is reordered after a stores to B (and the load from A precedes the store to B in program order)
- a store to A is reordered after a load from B (and the store to A precedes the load from B in program order)
- a store to A is reordered after a store to B (and the store to A precedes the store to B in program order)
See
or
http://preshing.com/20120710/memory-barriers-are-like-source-control-operations/
The SysBios manual does not state which synchronization primitives provide memory
barriers or if they provide memory barriers at all. Usually, one can assume that
mutual exclusion primitives working on memory (like Semaphores and Mutexes)
provide them, but not primitives that just work on disabling and enabling
interrupts (like GateHwi).
Of course, we must ensure correctness, but the documentation does not say anything
about this topic. How are we then supposed to know what is guaranteed and what not?
Example code:
int global_var_result = 0;
void tasks1(...)
{
/* produces result */
[...]
/* store result */
global_var_result = ...;
/* Set event for task2, see below */
Event_post(...);
[...]
}
void task2(...)
{
Event_Wait(...)
/* read result */
.. = global_var_result;
}
If Event_post() does not include a write memory barrier, the store to global_var_result
could be reordered by the hardware after the write that is used within
Event_post() to signal the event to task2. Obviously, this could lead to task2
exiting from Event_Wait() and reading an old value from global_var_result.
Please note that this is only possible in SMP-mode, not in non-SMP mode!
Analogous, the same reordering is possible on the read side: Assuming that these
two tasks are executing on CPU core1 and core2 respectively, and assuming that
core1 did _not_ reorder the stores of task1. Then it is still possible that
CPU core2 could speculatively read global_var_result before executing the read
to check the event condition in Event_Wait(). This will lead to old data being
read by core2, not the produced result from task1.
The Linux kernel provides the smp_rmb() and smp_wmb() macros to enforce memory
barriers where needed. On ARMv7, they will translate into a DMB instruction:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/CJAGIEIE.html
Further reads:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14041.html
Thus, assuming that the SysBios event mechanism does not include memory
barriers, in order to enforce correctness, the example would have to be changed
to:
void tasks1(...)
{
/* produces result */
[...]
/* store result */
global_var_result = ...;
smp_wmb(); // <- new! And needed in order to enforce ordering
/* Set event for task2, see below */
Event_post(...);
[...]
}
void task2(...)
{
Event_Wait(...)
smp_rmb(); // <- new! And needed in order to enforce ordering
/* read result */
.. = global_var_result;
}
However, there is no such thing as smp_rmb() and smp_wmb() in SysBios, so it is
unclear what the status is there and if it is needed or not.
Note that all of this is NOT necessary on non-SMP systems! If using only one core,
that core always sees _ITS_ memory accesses as if executed in program order.