1 From 7a30df49f63ad92318ddf1f7498d1129a77dd4bd Mon Sep 17 00:00:00 2001
2 From: Yang Shi <yang.shi@linux.alibaba.com>
3 Date: Thu, 13 Jun 2019 15:56:05 -0700
4 Subject: mm: mmu_gather: remove __tlb_reset_range() for force flush
6 From: Yang Shi <yang.shi@linux.alibaba.com>
8 commit 7a30df49f63ad92318ddf1f7498d1129a77dd4bd upstream.
10 A few new fields were added to mmu_gather to make TLB flush smarter for
11 huge page by telling what level of page table is changed.
13 __tlb_reset_range() is used to reset all these page table state to
14 unchanged, which is called by TLB flush for parallel mapping changes for
15 the same range under non-exclusive lock (i.e. read mmap_sem).
17 Before commit dd2283f2605e ("mm: mmap: zap pages with read mmap_sem in
18 munmap"), the syscalls (e.g. MADV_DONTNEED, MADV_FREE) which may update
19 PTEs in parallel don't remove page tables. But, the forementioned
20 commit may do munmap() under read mmap_sem and free page tables. This
21 may result in program hang on aarch64 reported by Jan Stancek. The
22 problem could be reproduced by his test program with slightly modified
27 static int map_size = 4096;
28 static int num_iter = 500;
29 static long threads_total;
31 static void *distant_area;
33 void *map_write_unmap(void *ptr)
36 unsigned char *map_address;
39 for (i = 0; i < num_iter; i++) {
40 map_address = mmap(distant_area, (size_t) map_size, PROT_WRITE | PROT_READ,
41 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
42 if (map_address == MAP_FAILED) {
47 for (j = 0; j < map_size; j++)
50 if (munmap(map_address, map_size) == -1) {
59 void *dummy(void *ptr)
68 /* hint for mmap in map_write_unmap() */
69 distant_area = mmap(0, DISTANT_MMAP_SIZE, PROT_WRITE | PROT_READ,
70 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
71 munmap(distant_area, (size_t)DISTANT_MMAP_SIZE);
72 distant_area += DISTANT_MMAP_SIZE / 2;
75 pthread_create(&thid[0], NULL, map_write_unmap, NULL);
76 pthread_create(&thid[1], NULL, dummy, NULL);
78 pthread_join(thid[0], NULL);
79 pthread_join(thid[1], NULL);
84 The program may bring in parallel execution like below:
88 downgrade_write(&mm->mmap_sem);
91 inc_tlb_flush_pending(tlb->mm);
97 madvise(thread_stack, 8M, MADV_DONTNEED)
100 inc_tlb_flush_pending(tlb->mm);
103 if (mm_tlb_flush_nested(tlb->mm))
106 __tlb_reset_range() would reset freed_tables and cleared_* bits, but this
107 may cause inconsistency for munmap() which do free page tables. Then it
108 may result in some architectures, e.g. aarch64, may not flush TLB
109 completely as expected to have stale TLB entries remained.
111 Use fullmm flush since it yields much better performance on aarch64 and
112 non-fullmm doesn't yields significant difference on x86.
114 The original proposed fix came from Jan Stancek who mainly debugged this
115 issue, I just wrapped up everything together.
117 Jan's testing results:
119 v5.2-rc2-24-gbec7550cca10
120 --------------------------
126 v5.2-rc2-24-gbec7550cca10 + "mm: mmu_gather: remove __tlb_reset_range() for force flush"
127 ---------------------------------------------------------------------------------------_
133 [akpm@linux-foundation.org: coding-style fixes]
134 Link: http://lkml.kernel.org/r/1558322252-113575-1-git-send-email-yang.shi@linux.alibaba.com
135 Fixes: dd2283f2605e ("mm: mmap: zap pages with read mmap_sem in munmap")
136 Signed-off-by: Yang Shi <yang.shi@linux.alibaba.com>
137 Signed-off-by: Jan Stancek <jstancek@redhat.com>
138 Reported-by: Jan Stancek <jstancek@redhat.com>
139 Tested-by: Jan Stancek <jstancek@redhat.com>
140 Suggested-by: Will Deacon <will.deacon@arm.com>
141 Tested-by: Will Deacon <will.deacon@arm.com>
142 Acked-by: Will Deacon <will.deacon@arm.com>
143 Cc: Peter Zijlstra <peterz@infradead.org>
144 Cc: Nick Piggin <npiggin@gmail.com>
145 Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
146 Cc: Nadav Amit <namit@vmware.com>
147 Cc: Minchan Kim <minchan@kernel.org>
148 Cc: Mel Gorman <mgorman@suse.de>
149 Cc: <stable@vger.kernel.org> [4.20+]
150 Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
151 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
152 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
155 mm/mmu_gather.c | 24 +++++++++++++++++++-----
156 1 file changed, 19 insertions(+), 5 deletions(-)
158 --- a/mm/mmu_gather.c
159 +++ b/mm/mmu_gather.c
160 @@ -93,8 +93,17 @@ void arch_tlb_finish_mmu(struct mmu_gath
161 struct mmu_gather_batch *batch, *next;
165 + * The aarch64 yields better performance with fullmm by
166 + * avoiding multiple CPUs spamming TLBI messages at the
169 + * On x86 non-fullmm doesn't yield significant difference
173 __tlb_reset_range(tlb);
174 - __tlb_adjust_range(tlb, start, end - start);
175 + tlb->freed_tables = 1;
179 @@ -249,10 +258,15 @@ void tlb_finish_mmu(struct mmu_gather *t
182 * If there are parallel threads are doing PTE changes on same range
183 - * under non-exclusive lock(e.g., mmap_sem read-side) but defer TLB
184 - * flush by batching, a thread has stable TLB entry can fail to flush
185 - * the TLB by observing pte_none|!pte_dirty, for example so flush TLB
186 - * forcefully if we detect parallel PTE batching threads.
187 + * under non-exclusive lock (e.g., mmap_sem read-side) but defer TLB
188 + * flush by batching, one thread may end up seeing inconsistent PTEs
189 + * and result in having stale TLB entries. So flush TLB forcefully
190 + * if we detect parallel PTE batching threads.
192 + * However, some syscalls, e.g. munmap(), may free page tables, this
193 + * needs force flush everything in the given range. Otherwise this
194 + * may result in having stale TLB entries for some architectures,
195 + * e.g. aarch64, that could specify flush what level TLB.
197 bool force = mm_tlb_flush_nested(tlb->mm);