Commit 20273941f2129aa5a432796d98a276ed73d60782

Authored by Peter Zijlstra
Committed by Linus Torvalds
1 parent a8e23a2918

mm: fix race in kunmap_atomic()

Christoph reported a nice splat which illustrated a race in the new stack
based kmap_atomic implementation.

The problem is that we pop our stack slot before we're completely done
resetting its state -- in particular clearing the PTE (sometimes that's
CONFIG_DEBUG_HIGHMEM).  If an interrupt happens before we actually clear
the PTE used for the last slot, that interrupt can reuse the slot in a
dirty state, which triggers a BUG in kmap_atomic().

Fix this by introducing kmap_atomic_idx() which reports the current slot
index without actually releasing it and use that to find the PTE and delay
the _pop() until after we're completely done.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Reported-by: Christoph Hellwig <hch@infradead.org>
Acked-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 10 changed files with 26 additions and 9 deletions Side-by-side Diff

arch/arm/mm/highmem.c
... ... @@ -89,7 +89,7 @@
89 89 int idx, type;
90 90  
91 91 if (kvaddr >= (void *)FIXADDR_START) {
92   - type = kmap_atomic_idx_pop();
  92 + type = kmap_atomic_idx();
93 93 idx = type + KM_TYPE_NR * smp_processor_id();
94 94  
95 95 if (cache_is_vivt())
... ... @@ -101,6 +101,7 @@
101 101 #else
102 102 (void) idx; /* to kill a warning */
103 103 #endif
  104 + kmap_atomic_idx_pop();
104 105 } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
105 106 /* this address was obtained through kmap_high_get() */
106 107 kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
arch/frv/mm/highmem.c
... ... @@ -68,7 +68,7 @@
68 68  
69 69 void __kunmap_atomic(void *kvaddr)
70 70 {
71   - int type = kmap_atomic_idx_pop();
  71 + int type = kmap_atomic_idx();
72 72 switch (type) {
73 73 case 0: __kunmap_atomic_primary(4, 6); break;
74 74 case 1: __kunmap_atomic_primary(5, 7); break;
... ... @@ -83,6 +83,7 @@
83 83 default:
84 84 BUG();
85 85 }
  86 + kmap_atomic_idx_pop();
86 87 pagefault_enable();
87 88 }
88 89 EXPORT_SYMBOL(__kunmap_atomic);
arch/mips/mm/highmem.c
... ... @@ -74,7 +74,7 @@
74 74 return;
75 75 }
76 76  
77   - type = kmap_atomic_idx_pop();
  77 + type = kmap_atomic_idx();
78 78 #ifdef CONFIG_DEBUG_HIGHMEM
79 79 {
80 80 int idx = type + KM_TYPE_NR * smp_processor_id();
... ... @@ -89,6 +89,7 @@
89 89 local_flush_tlb_one(vaddr);
90 90 }
91 91 #endif
  92 + kmap_atomic_idx_pop();
92 93 pagefault_enable();
93 94 }
94 95 EXPORT_SYMBOL(__kunmap_atomic);
arch/mn10300/include/asm/highmem.h
... ... @@ -101,7 +101,7 @@
101 101 return;
102 102 }
103 103  
104   - type = kmap_atomic_idx_pop();
  104 + type = kmap_atomic_idx();
105 105  
106 106 #if HIGHMEM_DEBUG
107 107 {
... ... @@ -119,6 +119,8 @@
119 119 __flush_tlb_one(vaddr);
120 120 }
121 121 #endif
  122 +
  123 + kmap_atomic_idx_pop();
122 124 pagefault_enable();
123 125 }
124 126 #endif /* __KERNEL__ */
arch/powerpc/mm/highmem.c
... ... @@ -62,7 +62,7 @@
62 62 return;
63 63 }
64 64  
65   - type = kmap_atomic_idx_pop();
  65 + type = kmap_atomic_idx();
66 66  
67 67 #ifdef CONFIG_DEBUG_HIGHMEM
68 68 {
... ... @@ -79,6 +79,8 @@
79 79 local_flush_tlb_page(NULL, vaddr);
80 80 }
81 81 #endif
  82 +
  83 + kmap_atomic_idx_pop();
82 84 pagefault_enable();
83 85 }
84 86 EXPORT_SYMBOL(__kunmap_atomic);
arch/sparc/mm/highmem.c
... ... @@ -75,7 +75,7 @@
75 75 return;
76 76 }
77 77  
78   - type = kmap_atomic_idx_pop();
  78 + type = kmap_atomic_idx();
79 79  
80 80 #ifdef CONFIG_DEBUG_HIGHMEM
81 81 {
... ... @@ -104,6 +104,8 @@
104 104 #endif
105 105 }
106 106 #endif
  107 +
  108 + kmap_atomic_idx_pop();
107 109 pagefault_enable();
108 110 }
109 111 EXPORT_SYMBOL(__kunmap_atomic);
arch/tile/mm/highmem.c
... ... @@ -241,7 +241,7 @@
241 241 pte_t pteval = *pte;
242 242 int idx, type;
243 243  
244   - type = kmap_atomic_idx_pop();
  244 + type = kmap_atomic_idx();
245 245 idx = type + KM_TYPE_NR*smp_processor_id();
246 246  
247 247 /*
... ... @@ -252,6 +252,7 @@
252 252 BUG_ON(!pte_present(pteval) && !pte_migrating(pteval));
253 253 kmap_atomic_unregister(pte_page(pteval), vaddr);
254 254 kpte_clear_flush(pte, vaddr);
  255 + kmap_atomic_idx_pop();
255 256 } else {
256 257 /* Must be a lowmem page */
257 258 BUG_ON(vaddr < PAGE_OFFSET);
arch/x86/mm/highmem_32.c
... ... @@ -74,7 +74,7 @@
74 74 vaddr <= __fix_to_virt(FIX_KMAP_BEGIN)) {
75 75 int idx, type;
76 76  
77   - type = kmap_atomic_idx_pop();
  77 + type = kmap_atomic_idx();
78 78 idx = type + KM_TYPE_NR * smp_processor_id();
79 79  
80 80 #ifdef CONFIG_DEBUG_HIGHMEM
... ... @@ -87,6 +87,7 @@
87 87 * attributes or becomes a protected page in a hypervisor.
88 88 */
89 89 kpte_clear_flush(kmap_pte-idx, vaddr);
  90 + kmap_atomic_idx_pop();
90 91 }
91 92 #ifdef CONFIG_DEBUG_HIGHMEM
92 93 else {
arch/x86/mm/iomap_32.c
... ... @@ -98,7 +98,7 @@
98 98 vaddr <= __fix_to_virt(FIX_KMAP_BEGIN)) {
99 99 int idx, type;
100 100  
101   - type = kmap_atomic_idx_pop();
  101 + type = kmap_atomic_idx();
102 102 idx = type + KM_TYPE_NR * smp_processor_id();
103 103  
104 104 #ifdef CONFIG_DEBUG_HIGHMEM
... ... @@ -111,6 +111,7 @@
111 111 * attributes or becomes a protected page in a hypervisor.
112 112 */
113 113 kpte_clear_flush(kmap_pte-idx, vaddr);
  114 + kmap_atomic_idx_pop();
114 115 }
115 116  
116 117 pagefault_enable();
include/linux/highmem.h
... ... @@ -88,6 +88,11 @@
88 88 return idx;
89 89 }
90 90  
  91 +static inline int kmap_atomic_idx(void)
  92 +{
  93 + return __get_cpu_var(__kmap_atomic_idx) - 1;
  94 +}
  95 +
91 96 static inline int kmap_atomic_idx_pop(void)
92 97 {
93 98 int idx = --__get_cpu_var(__kmap_atomic_idx);