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

Unified · Split

kernel/sched/membarrier.c+128−0
@@ -6,6 +6,134 @@ · 5 unmodified lines
6 */6 */
7#include "sched.h"7#include "sched.h"
8 8
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
9/*137/*
10 * Bitmask made from a "or" of all commands within enum membarrier_cmd,138 * Bitmask made from a "or" of all commands within enum membarrier_cmd,
11 * except MEMBARRIER_CMD_QUERY.139 * 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