History

sched: membarrier: document memory ordering scenarios

Document membarrier ordering scenarios in membarrier.c. Thanks to Alan Stern for refreshing my memory. Now that I have those in mind, it seems appropriate to serialize them to comments for posterity.

Mathieu Desnoyers committed 6 years ago

Commit 25595eb1 parent 618758e1 file · +128 −0Browse files

Showing the changes to kernel (1 of 1 file). Show all

Unified · Split

kernel/sched/membarrier.c+128−0
@@ -6,6 +6,134 @@ · 5 unmodified lines
66 */
77 #include "sched.h"
88
9+/*
10+ * For documentation purposes, here are some membarrier ordering
11+ * scenarios to keep in mind:
12+ *
13+ * A) Userspace thread execution after IPI vs membarrier's memory
14+ * barrier before sending the IPI
15+ *
16+ * Userspace variables:
17+ *
18+ * int x = 0, y = 0;
19+ *
20+ * The memory barrier at the start of membarrier() on CPU0 is necessary in
21+ * order to enforce the guarantee that any writes occurring on CPU0 before
22+ * the membarrier() is executed will be visible to any code executing on
23+ * CPU1 after the IPI-induced memory barrier:
24+ *
25+ * CPU0 CPU1
26+ *
27+ * x = 1
28+ * membarrier():
29+ * a: smp_mb()
30+ * b: send IPI IPI-induced mb
31+ * c: smp_mb()
32+ * r2 = y
33+ * y = 1
34+ * barrier()
35+ * r1 = x
36+ *
37+ * BUG_ON(r1 == 0 && r2 == 0)
38+ *
39+ * The write to y and load from x by CPU1 are unordered by the hardware,
40+ * so it's possible to have "r1 = x" reordered before "y = 1" at any
41+ * point after (b). If the memory barrier at (a) is omitted, then "x = 1"
42+ * can be reordered after (a) (although not after (c)), so we get r1 == 0
43+ * and r2 == 0. This violates the guarantee that membarrier() is
44+ * supposed by provide.
45+ *
46+ * The timing of the memory barrier at (a) has to ensure that it executes
47+ * before the IPI-induced memory barrier on CPU1.
48+ *
49+ * B) Userspace thread execution before IPI vs membarrier's memory
50+ * barrier after completing the IPI
51+ *
52+ * Userspace variables:
53+ *
54+ * int x = 0, y = 0;
55+ *
56+ * The memory barrier at the end of membarrier() on CPU0 is necessary in
57+ * order to enforce the guarantee that any writes occurring on CPU1 before
58+ * the membarrier() is executed will be visible to any code executing on
59+ * CPU0 after the membarrier():
60+ *
61+ * CPU0 CPU1
62+ *
63+ * x = 1
64+ * barrier()
65+ * y = 1
66+ * r2 = y
67+ * membarrier():
68+ * a: smp_mb()
69+ * b: send IPI IPI-induced mb
70+ * c: smp_mb()
71+ * r1 = x
72+ * BUG_ON(r1 == 0 && r2 == 1)
73+ *
74+ * The writes to x and y are unordered by the hardware, so it's possible to
75+ * have "r2 = 1" even though the write to x doesn't execute until (b). If
76+ * the memory barrier at (c) is omitted then "r1 = x" can be reordered
77+ * before (b) (although not before (a)), so we get "r1 = 0". This violates
78+ * the guarantee that membarrier() is supposed to provide.
79+ *
80+ * The timing of the memory barrier at (c) has to ensure that it executes
81+ * after the IPI-induced memory barrier on CPU1.
82+ *
83+ * C) Scheduling userspace thread -> kthread -> userspace thread vs membarrier
84+ *
85+ * CPU0 CPU1
86+ *
87+ * membarrier():
88+ * a: smp_mb()
89+ * d: switch to kthread (includes mb)
90+ * b: read rq->curr->mm == NULL
91+ * e: switch to user (includes mb)
92+ * c: smp_mb()
93+ *
94+ * Using the scenario from (A), we can show that (a) needs to be paired
95+ * with (e). Using the scenario from (B), we can show that (c) needs to
96+ * be paired with (d).
97+ *
98+ * D) exit_mm vs membarrier
99+ *
100+ * Two thread groups are created, A and B. Thread group B is created by
101+ * issuing clone from group A with flag CLONE_VM set, but not CLONE_THREAD.
102+ * Let's assume we have a single thread within each thread group (Thread A
103+ * and Thread B). Thread A runs on CPU0, Thread B runs on CPU1.
104+ *
105+ * CPU0 CPU1
106+ *
107+ * membarrier():
108+ * a: smp_mb()
109+ * exit_mm():
110+ * d: smp_mb()
111+ * e: current->mm = NULL
112+ * b: read rq->curr->mm == NULL
113+ * c: smp_mb()
114+ *
115+ * Using scenario (B), we can show that (c) needs to be paired with (d).
116+ *
117+ * E) kthread_{use,unuse}_mm vs membarrier
118+ *
119+ * CPU0 CPU1
120+ *
121+ * membarrier():
122+ * a: smp_mb()
123+ * kthread_unuse_mm()
124+ * d: smp_mb()
125+ * e: current->mm = NULL
126+ * b: read rq->curr->mm == NULL
127+ * kthread_use_mm()
128+ * f: current->mm = mm
129+ * g: smp_mb()
130+ * c: smp_mb()
131+ *
132+ * Using the scenario from (A), we can show that (a) needs to be paired
133+ * with (g). Using the scenario from (B), we can show that (c) needs to
134+ * be paired with (d).
135+ */
136+
9137 /*
10138 * Bitmask made from a "or" of all commands within enum membarrier_cmd,
11139 * except MEMBARRIER_CMD_QUERY.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t