Documentation/atomic_t: Document forward progress expectations
Add a few words on forward progress; there's been quite a bit of confusion on the subject. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Will Deacon <will@kernel.org> Acked-by: Boqun Feng <boqun.feng@gmail.com> Link: https://lkml.kernel.org/r/YQK9ziyogxTH0m9H@hirez.programming.kicks-ass.net
This commit is contained in:
parent
9248e52fec
commit
55bccf1f93
@ -312,3 +312,56 @@ Usage:
|
|||||||
|
|
||||||
NB. try_cmpxchg() also generates better code on some platforms (notably x86)
|
NB. try_cmpxchg() also generates better code on some platforms (notably x86)
|
||||||
where the function more closely matches the hardware instruction.
|
where the function more closely matches the hardware instruction.
|
||||||
|
|
||||||
|
|
||||||
|
FORWARD PROGRESS
|
||||||
|
----------------
|
||||||
|
|
||||||
|
In general strong forward progress is expected of all unconditional atomic
|
||||||
|
operations -- those in the Arithmetic and Bitwise classes and xchg(). However
|
||||||
|
a fair amount of code also requires forward progress from the conditional
|
||||||
|
atomic operations.
|
||||||
|
|
||||||
|
Specifically 'simple' cmpxchg() loops are expected to not starve one another
|
||||||
|
indefinitely. However, this is not evident on LL/SC architectures, because
|
||||||
|
while an LL/SC architecure 'can/should/must' provide forward progress
|
||||||
|
guarantees between competing LL/SC sections, such a guarantee does not
|
||||||
|
transfer to cmpxchg() implemented using LL/SC. Consider:
|
||||||
|
|
||||||
|
old = atomic_read(&v);
|
||||||
|
do {
|
||||||
|
new = func(old);
|
||||||
|
} while (!atomic_try_cmpxchg(&v, &old, new));
|
||||||
|
|
||||||
|
which on LL/SC becomes something like:
|
||||||
|
|
||||||
|
old = atomic_read(&v);
|
||||||
|
do {
|
||||||
|
new = func(old);
|
||||||
|
} while (!({
|
||||||
|
volatile asm ("1: LL %[oldval], %[v]\n"
|
||||||
|
" CMP %[oldval], %[old]\n"
|
||||||
|
" BNE 2f\n"
|
||||||
|
" SC %[new], %[v]\n"
|
||||||
|
" BNE 1b\n"
|
||||||
|
"2:\n"
|
||||||
|
: [oldval] "=&r" (oldval), [v] "m" (v)
|
||||||
|
: [old] "r" (old), [new] "r" (new)
|
||||||
|
: "memory");
|
||||||
|
success = (oldval == old);
|
||||||
|
if (!success)
|
||||||
|
old = oldval;
|
||||||
|
success; }));
|
||||||
|
|
||||||
|
However, even the forward branch from the failed compare can cause the LL/SC
|
||||||
|
to fail on some architectures, let alone whatever the compiler makes of the C
|
||||||
|
loop body. As a result there is no guarantee what so ever the cacheline
|
||||||
|
containing @v will stay on the local CPU and progress is made.
|
||||||
|
|
||||||
|
Even native CAS architectures can fail to provide forward progress for their
|
||||||
|
primitive (See Sparc64 for an example).
|
||||||
|
|
||||||
|
Such implementations are strongly encouraged to add exponential backoff loops
|
||||||
|
to a failed CAS in order to ensure some progress. Affected architectures are
|
||||||
|
also strongly encouraged to inspect/audit the atomic fallbacks, refcount_t and
|
||||||
|
their locking primitives.
|
||||||
|
Loading…
Reference in New Issue
Block a user