However, stores are not speculated. This means that ordering -is- provided
for load-store control dependencies, as in the following example:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
if (q) {
WRITE_ONCE(b, p);
}
Control dependencies pair normally with other types of barriers. That
-said, please note that READ_ONCE_CTRL() is not optional! Without the
-READ_ONCE_CTRL(), the compiler might combine the load from 'a' with
-other loads from 'a', and the store to 'b' with other stores to 'b',
-with possible highly counterintuitive effects on ordering.
+said, please note that READ_ONCE() is not optional! Without the
+READ_ONCE(), the compiler might combine the load from 'a' with other
+loads from 'a', and the store to 'b' with other stores to 'b', with
+possible highly counterintuitive effects on ordering.
Worse yet, if the compiler is able to prove (say) that the value of
variable 'a' is always non-zero, it would be well within its rights
q = a;
b = p; /* BUG: Compiler and CPU can both reorder!!! */
-Finally, the READ_ONCE_CTRL() includes an smp_read_barrier_depends()
-that DEC Alpha needs in order to respect control depedencies. Alternatively
-use one of atomic{,64}_read_ctrl().
-
-So don't leave out the READ_ONCE_CTRL().
+So don't leave out the READ_ONCE().
It is tempting to try to enforce ordering on identical stores on both
branches of the "if" statement as follows:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
if (q) {
barrier();
WRITE_ONCE(b, p);
Unfortunately, current compilers will transform this as follows at high
optimization levels:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
barrier();
WRITE_ONCE(b, p); /* BUG: No ordering vs. load from a!!! */
if (q) {
In contrast, without explicit memory barriers, two-legged-if control
ordering is guaranteed only when the stores differ, for example:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
if (q) {
WRITE_ONCE(b, p);
do_something();
do_something_else();
}
-The initial READ_ONCE_CTRL() is still required to prevent the compiler
-from proving the value of 'a'.
+The initial READ_ONCE() is still required to prevent the compiler from
+proving the value of 'a'.
In addition, you need to be careful what you do with the local variable 'q',
otherwise the compiler might be able to guess the value and again remove
the needed conditional. For example:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
if (q % MAX) {
WRITE_ONCE(b, p);
do_something();
equal to zero, in which case the compiler is within its rights to
transform the above code into the following:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
WRITE_ONCE(b, p);
do_something_else();
relying on this ordering, you should make sure that MAX is greater than
one, perhaps as follows:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
BUILD_BUG_ON(MAX <= 1); /* Order load from a with store to b. */
if (q % MAX) {
WRITE_ONCE(b, p);
You must also be careful not to rely too much on boolean short-circuit
evaluation. Consider this example:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
if (q || 1 > 0)
WRITE_ONCE(b, 1);
always true, the compiler can transform this example as following,
defeating control dependency:
- q = READ_ONCE_CTRL(a);
+ q = READ_ONCE(a);
WRITE_ONCE(b, 1);
This example underscores the need to ensure that the compiler cannot
CPU 0 CPU 1
======================= =======================
- r1 = READ_ONCE_CTRL(x); r2 = READ_ONCE_CTRL(y);
+ r1 = READ_ONCE(x); r2 = READ_ONCE(y);
if (r1 > 0) if (r2 > 0)
WRITE_ONCE(y, 1); WRITE_ONCE(x, 1);
In summary:
- (*) Control dependencies must be headed by READ_ONCE_CTRL(),
- atomic{,64}_read_ctrl(). Or, as a much less preferable alternative,
- interpose smp_read_barrier_depends() between a READ_ONCE() and the
- control-dependent write.
-
(*) Control dependencies can order prior loads against later stores.
However, they do -not- guarantee any other sort of ordering:
Not prior loads against later loads, nor prior stores against
between the prior load and the subsequent store, and this
conditional must involve the prior load. If the compiler is able
to optimize the conditional away, it will have also optimized
- away the ordering. Careful use of READ_ONCE_CTRL() READ_ONCE(),
- and WRITE_ONCE() can help to preserve the needed conditional.
+ away the ordering. Careful use of READ_ONCE() and WRITE_ONCE()
+ can help to preserve the needed conditional.
(*) Control dependencies require that the compiler avoid reordering the
- dependency into nonexistence. Careful use of READ_ONCE_CTRL(),
- atomic{,64}_read_ctrl() or smp_read_barrier_depends() can help to
- preserve your control dependency. Please see the Compiler Barrier
- section for more information.
+ dependency into nonexistence. Careful use of READ_ONCE() or
+ atomic{,64}_read() can help to preserve your control dependency.
+ Please see the Compiler Barrier section for more information.
(*) Control dependencies pair normally with other types of barriers.