1
+ ==============================================
2
+ Ordering I/O writes to memory-mapped addresses
3
+ ==============================================
4
+
1
5
On some platforms, so-called memory-mapped I/O is weakly ordered. On such
2
6
platforms, driver writers are responsible for ensuring that I/O writes to
3
7
memory-mapped addresses on their device arrive in the order intended. This is
@@ -8,39 +12,39 @@ critical section of code protected by spinlocks. This would ensure that
8
12
subsequent writes to I/O space arrived only after all prior writes (much like a
9
13
memory barrier op, mb(), only with respect to I/O).
10
14
11
- A more concrete example from a hypothetical device driver:
15
+ A more concrete example from a hypothetical device driver::
12
16
13
- ...
14
- CPU A: spin_lock_irqsave(&dev_lock, flags)
15
- CPU A: val = readl(my_status);
16
- CPU A: ...
17
- CPU A: writel(newval, ring_ptr);
18
- CPU A: spin_unlock_irqrestore(&dev_lock, flags)
19
- ...
20
- CPU B: spin_lock_irqsave(&dev_lock, flags)
21
- CPU B: val = readl(my_status);
22
- CPU B: ...
23
- CPU B: writel(newval2, ring_ptr);
24
- CPU B: spin_unlock_irqrestore(&dev_lock, flags)
25
- ...
17
+ ...
18
+ CPU A: spin_lock_irqsave(&dev_lock, flags)
19
+ CPU A: val = readl(my_status);
20
+ CPU A: ...
21
+ CPU A: writel(newval, ring_ptr);
22
+ CPU A: spin_unlock_irqrestore(&dev_lock, flags)
23
+ ...
24
+ CPU B: spin_lock_irqsave(&dev_lock, flags)
25
+ CPU B: val = readl(my_status);
26
+ CPU B: ...
27
+ CPU B: writel(newval2, ring_ptr);
28
+ CPU B: spin_unlock_irqrestore(&dev_lock, flags)
29
+ ...
26
30
27
31
In the case above, the device may receive newval2 before it receives newval,
28
- which could cause problems. Fixing it is easy enough though:
32
+ which could cause problems. Fixing it is easy enough though::
29
33
30
- ...
31
- CPU A: spin_lock_irqsave(&dev_lock, flags)
32
- CPU A: val = readl(my_status);
33
- CPU A: ...
34
- CPU A: writel(newval, ring_ptr);
35
- CPU A: (void)readl(safe_register); /* maybe a config register? */
36
- CPU A: spin_unlock_irqrestore(&dev_lock, flags)
37
- ...
38
- CPU B: spin_lock_irqsave(&dev_lock, flags)
39
- CPU B: val = readl(my_status);
40
- CPU B: ...
41
- CPU B: writel(newval2, ring_ptr);
42
- CPU B: (void)readl(safe_register); /* maybe a config register? */
43
- CPU B: spin_unlock_irqrestore(&dev_lock, flags)
34
+ ...
35
+ CPU A: spin_lock_irqsave(&dev_lock, flags)
36
+ CPU A: val = readl(my_status);
37
+ CPU A: ...
38
+ CPU A: writel(newval, ring_ptr);
39
+ CPU A: (void)readl(safe_register); /* maybe a config register? */
40
+ CPU A: spin_unlock_irqrestore(&dev_lock, flags)
41
+ ...
42
+ CPU B: spin_lock_irqsave(&dev_lock, flags)
43
+ CPU B: val = readl(my_status);
44
+ CPU B: ...
45
+ CPU B: writel(newval2, ring_ptr);
46
+ CPU B: (void)readl(safe_register); /* maybe a config register? */
47
+ CPU B: spin_unlock_irqrestore(&dev_lock, flags)
44
48
45
49
Here, the reads from safe_register will cause the I/O chipset to flush any
46
50
pending writes before actually posting the read to the chipset, preventing
0 commit comments