Skip to content

Commit 1c824bf

Browse files
committed
Merge tag 'lkmm.2022.01.09a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu
Pull memory model documentation updates from Paul McKenney: "This series contains documentation and litmus tests for locking, courtesy of Boqun Feng" * tag 'lkmm.2022.01.09a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu: tools/memory-model: litmus: Add two tests for unlock(A)+lock(B) ordering tools/memory-model: doc: Describe the requirement of the litmus-tests directory tools/memory-model: Provide extra ordering for unlock+lock pair on the same CPU
2 parents e7d38f1 + c438b7d commit 1c824bf

6 files changed

Lines changed: 116 additions & 22 deletions

File tree

tools/memory-model/Documentation/explanation.txt

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,15 +1813,16 @@ spin_trylock() -- we can call these things lock-releases and
18131813
lock-acquires -- have two properties beyond those of ordinary releases
18141814
and acquires.
18151815

1816-
First, when a lock-acquire reads from a lock-release, the LKMM
1817-
requires that every instruction po-before the lock-release must
1818-
execute before any instruction po-after the lock-acquire. This would
1819-
naturally hold if the release and acquire operations were on different
1820-
CPUs, but the LKMM says it holds even when they are on the same CPU.
1821-
For example:
1816+
First, when a lock-acquire reads from or is po-after a lock-release,
1817+
the LKMM requires that every instruction po-before the lock-release
1818+
must execute before any instruction po-after the lock-acquire. This
1819+
would naturally hold if the release and acquire operations were on
1820+
different CPUs and accessed the same lock variable, but the LKMM says
1821+
it also holds when they are on the same CPU, even if they access
1822+
different lock variables. For example:
18221823

18231824
int x, y;
1824-
spinlock_t s;
1825+
spinlock_t s, t;
18251826

18261827
P0()
18271828
{
@@ -1830,9 +1831,9 @@ For example:
18301831
spin_lock(&s);
18311832
r1 = READ_ONCE(x);
18321833
spin_unlock(&s);
1833-
spin_lock(&s);
1834+
spin_lock(&t);
18341835
r2 = READ_ONCE(y);
1835-
spin_unlock(&s);
1836+
spin_unlock(&t);
18361837
}
18371838

18381839
P1()
@@ -1842,10 +1843,10 @@ For example:
18421843
WRITE_ONCE(x, 1);
18431844
}
18441845

1845-
Here the second spin_lock() reads from the first spin_unlock(), and
1846-
therefore the load of x must execute before the load of y. Thus we
1847-
cannot have r1 = 1 and r2 = 0 at the end (this is an instance of the
1848-
MP pattern).
1846+
Here the second spin_lock() is po-after the first spin_unlock(), and
1847+
therefore the load of x must execute before the load of y, even though
1848+
the two locking operations use different locks. Thus we cannot have
1849+
r1 = 1 and r2 = 0 at the end (this is an instance of the MP pattern).
18491850

18501851
This requirement does not apply to ordinary release and acquire
18511852
fences, only to lock-related operations. For instance, suppose P0()
@@ -1872,13 +1873,13 @@ instructions in the following order:
18721873

18731874
and thus it could load y before x, obtaining r2 = 0 and r1 = 1.
18741875

1875-
Second, when a lock-acquire reads from a lock-release, and some other
1876-
stores W and W' occur po-before the lock-release and po-after the
1877-
lock-acquire respectively, the LKMM requires that W must propagate to
1878-
each CPU before W' does. For example, consider:
1876+
Second, when a lock-acquire reads from or is po-after a lock-release,
1877+
and some other stores W and W' occur po-before the lock-release and
1878+
po-after the lock-acquire respectively, the LKMM requires that W must
1879+
propagate to each CPU before W' does. For example, consider:
18791880

18801881
int x, y;
1881-
spinlock_t x;
1882+
spinlock_t s;
18821883

18831884
P0()
18841885
{
@@ -1908,7 +1909,12 @@ each CPU before W' does. For example, consider:
19081909

19091910
If r1 = 1 at the end then the spin_lock() in P1 must have read from
19101911
the spin_unlock() in P0. Hence the store to x must propagate to P2
1911-
before the store to y does, so we cannot have r2 = 1 and r3 = 0.
1912+
before the store to y does, so we cannot have r2 = 1 and r3 = 0. But
1913+
if P1 had used a lock variable different from s, the writes could have
1914+
propagated in either order. (On the other hand, if the code in P0 and
1915+
P1 had all executed on a single CPU, as in the example before this
1916+
one, then the writes would have propagated in order even if the two
1917+
critical sections used different lock variables.)
19121918

19131919
These two special requirements for lock-release and lock-acquire do
19141920
not arise from the operational model. Nevertheless, kernel developers

tools/memory-model/README

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,18 @@ litmus-tests
195195
are listed in litmus-tests/README. A great deal more litmus
196196
tests are available at https://github.com/paulmckrcu/litmus.
197197

198+
By "representative", it means the one in the litmus-tests
199+
directory is:
200+
201+
1) simple, the number of threads should be relatively
202+
small and each thread function should be relatively
203+
simple.
204+
2) orthogonal, there should be no two litmus tests
205+
describing the same aspect of the memory model.
206+
3) textbook, developers can easily copy-paste-modify
207+
the litmus tests to use the patterns on their own
208+
code.
209+
198210
lock.cat
199211
Provides a front-end analysis of lock acquisition and release,
200212
for example, associating a lock acquisition with the preceding

tools/memory-model/linux-kernel.cat

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ include "lock.cat"
2727
(* Release Acquire *)
2828
let acq-po = [Acquire] ; po ; [M]
2929
let po-rel = [M] ; po ; [Release]
30-
let po-unlock-rf-lock-po = po ; [UL] ; rf ; [LKR] ; po
30+
let po-unlock-lock-po = po ; [UL] ; (po|rf) ; [LKR] ; po
3131

3232
(* Fences *)
3333
let R4rmb = R \ Noreturn (* Reads for which rmb works *)
@@ -70,12 +70,12 @@ let rwdep = (dep | ctrl) ; [W]
7070
let overwrite = co | fr
7171
let to-w = rwdep | (overwrite & int) | (addr ; [Plain] ; wmb)
7272
let to-r = addr | (dep ; [Marked] ; rfi)
73-
let ppo = to-r | to-w | fence | (po-unlock-rf-lock-po & int)
73+
let ppo = to-r | to-w | fence | (po-unlock-lock-po & int)
7474

7575
(* Propagation: Ordering from release operations and strong fences. *)
7676
let A-cumul(r) = (rfe ; [Marked])? ; r
7777
let cumul-fence = [Marked] ; (A-cumul(strong-fence | po-rel) | wmb |
78-
po-unlock-rf-lock-po) ; [Marked]
78+
po-unlock-lock-po) ; [Marked]
7979
let prop = [Marked] ; (overwrite & ext)? ; cumul-fence* ;
8080
[Marked] ; rfe? ; [Marked]
8181

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
C LB+unlocklockonceonce+poacquireonce
2+
3+
(*
4+
* Result: Never
5+
*
6+
* If two locked critical sections execute on the same CPU, all accesses
7+
* in the first must execute before any accesses in the second, even if the
8+
* critical sections are protected by different locks. Note: Even when a
9+
* write executes before a read, their memory effects can be reordered from
10+
* the viewpoint of another CPU (the kind of reordering allowed by TSO).
11+
*)
12+
13+
{}
14+
15+
P0(spinlock_t *s, spinlock_t *t, int *x, int *y)
16+
{
17+
int r1;
18+
19+
spin_lock(s);
20+
r1 = READ_ONCE(*x);
21+
spin_unlock(s);
22+
spin_lock(t);
23+
WRITE_ONCE(*y, 1);
24+
spin_unlock(t);
25+
}
26+
27+
P1(int *x, int *y)
28+
{
29+
int r2;
30+
31+
r2 = smp_load_acquire(y);
32+
WRITE_ONCE(*x, 1);
33+
}
34+
35+
exists (0:r1=1 /\ 1:r2=1)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
C MP+unlocklockonceonce+fencermbonceonce
2+
3+
(*
4+
* Result: Never
5+
*
6+
* If two locked critical sections execute on the same CPU, stores in the
7+
* first must propagate to each CPU before stores in the second do, even if
8+
* the critical sections are protected by different locks.
9+
*)
10+
11+
{}
12+
13+
P0(spinlock_t *s, spinlock_t *t, int *x, int *y)
14+
{
15+
spin_lock(s);
16+
WRITE_ONCE(*x, 1);
17+
spin_unlock(s);
18+
spin_lock(t);
19+
WRITE_ONCE(*y, 1);
20+
spin_unlock(t);
21+
}
22+
23+
P1(int *x, int *y)
24+
{
25+
int r1;
26+
int r2;
27+
28+
r1 = READ_ONCE(*y);
29+
smp_rmb();
30+
r2 = READ_ONCE(*x);
31+
}
32+
33+
exists (1:r1=1 /\ 1:r2=0)

tools/memory-model/litmus-tests/README

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ LB+poonceonces.litmus
6363
As above, but with store-release replaced with WRITE_ONCE()
6464
and load-acquire replaced with READ_ONCE().
6565

66+
LB+unlocklockonceonce+poacquireonce.litmus
67+
Does a unlock+lock pair provides ordering guarantee between a
68+
load and a store?
69+
6670
MP+onceassign+derefonce.litmus
6771
As below, but with rcu_assign_pointer() and an rcu_dereference().
6872

@@ -90,6 +94,10 @@ MP+porevlocks.litmus
9094
As below, but with the first access of the writer process
9195
and the second access of reader process protected by a lock.
9296

97+
MP+unlocklockonceonce+fencermbonceonce.litmus
98+
Does a unlock+lock pair provides ordering guarantee between a
99+
store and another store?
100+
93101
MP+fencewmbonceonce+fencermbonceonce.litmus
94102
Does a smp_wmb() (between the stores) and an smp_rmb() (between
95103
the loads) suffice for the message-passing litmus test, where one

0 commit comments

Comments
 (0)