aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2016-07-26 18:24:09 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-26 19:19:19 -0400
commite9d55e157034a9efd99405c99c1565d64619d82b (patch)
treed97dfd0c54d5a9fd1c7b6292c64c83791f9f5278
parent31d49da5ad01728e48a1bb2b43795598b23de68a (diff)
mm: change the interface for __tlb_remove_page()
This updates the generic and arch specific implementation to return true if we need to do a tlb flush. That means if a __tlb_remove_page indicate a flush is needed, the page we try to remove need to be tracked and added again after the flush. We need to track it because we have already update the pte to none and we can't just loop back. This change is done to enable us to do a tlb_flush when we try to flush a range that consists of different page sizes. For architectures like ppc64, we can do a range based tlb flush and we need to track page size for that. When we try to remove a huge page, we will force a tlb flush and starts a new mmu gather. [aneesh.kumar@linux.vnet.ibm.com: mm-change-the-interface-for-__tlb_remove_page-v3] Link: http://lkml.kernel.org/r/1465049193-22197-2-git-send-email-aneesh.kumar@linux.vnet.ibm.com Link: http://lkml.kernel.org/r/1464860389-29019-2-git-send-email-aneesh.kumar@linux.vnet.ibm.com Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Hugh Dickins <hughd@google.com> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Mel Gorman <mgorman@suse.de> Cc: David Rientjes <rientjes@google.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Minchan Kim <minchan.kim@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/arm/include/asm/tlb.h17
-rw-r--r--arch/ia64/include/asm/tlb.h19
-rw-r--r--arch/s390/include/asm/tlb.h9
-rw-r--r--arch/sh/include/asm/tlb.h8
-rw-r--r--arch/um/include/asm/tlb.h8
-rw-r--r--include/asm-generic/tlb.h44
-rw-r--r--mm/memory.c19
7 files changed, 94 insertions, 30 deletions
diff --git a/arch/arm/include/asm/tlb.h b/arch/arm/include/asm/tlb.h
index 3cadb726ec88..a9d2aee3826f 100644
--- a/arch/arm/include/asm/tlb.h
+++ b/arch/arm/include/asm/tlb.h
@@ -209,17 +209,26 @@ tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
209 tlb_flush(tlb); 209 tlb_flush(tlb);
210} 210}
211 211
212static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page) 212static inline bool __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
213{ 213{
214 if (tlb->nr == tlb->max)
215 return true;
214 tlb->pages[tlb->nr++] = page; 216 tlb->pages[tlb->nr++] = page;
215 VM_BUG_ON(tlb->nr > tlb->max); 217 return false;
216 return tlb->max - tlb->nr;
217} 218}
218 219
219static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) 220static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
220{ 221{
221 if (!__tlb_remove_page(tlb, page)) 222 if (__tlb_remove_page(tlb, page)) {
222 tlb_flush_mmu(tlb); 223 tlb_flush_mmu(tlb);
224 __tlb_remove_page(tlb, page);
225 }
226}
227
228static inline bool __tlb_remove_pte_page(struct mmu_gather *tlb,
229 struct page *page)
230{
231 return __tlb_remove_page(tlb, page);
223} 232}
224 233
225static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, 234static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
diff --git a/arch/ia64/include/asm/tlb.h b/arch/ia64/include/asm/tlb.h
index 39d64e0df1de..e7da41aa9110 100644
--- a/arch/ia64/include/asm/tlb.h
+++ b/arch/ia64/include/asm/tlb.h
@@ -205,17 +205,18 @@ tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end)
205 * must be delayed until after the TLB has been flushed (see comments at the beginning of 205 * must be delayed until after the TLB has been flushed (see comments at the beginning of
206 * this file). 206 * this file).
207 */ 207 */
208static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page) 208static inline bool __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
209{ 209{
210 if (tlb->nr == tlb->max)
211 return true;
212
210 tlb->need_flush = 1; 213 tlb->need_flush = 1;
211 214
212 if (!tlb->nr && tlb->pages == tlb->local) 215 if (!tlb->nr && tlb->pages == tlb->local)
213 __tlb_alloc_page(tlb); 216 __tlb_alloc_page(tlb);
214 217
215 tlb->pages[tlb->nr++] = page; 218 tlb->pages[tlb->nr++] = page;
216 VM_BUG_ON(tlb->nr > tlb->max); 219 return false;
217
218 return tlb->max - tlb->nr;
219} 220}
220 221
221static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) 222static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb)
@@ -235,8 +236,16 @@ static inline void tlb_flush_mmu(struct mmu_gather *tlb)
235 236
236static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) 237static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
237{ 238{
238 if (!__tlb_remove_page(tlb, page)) 239 if (__tlb_remove_page(tlb, page)) {
239 tlb_flush_mmu(tlb); 240 tlb_flush_mmu(tlb);
241 __tlb_remove_page(tlb, page);
242 }
243}
244
245static inline bool __tlb_remove_pte_page(struct mmu_gather *tlb,
246 struct page *page)
247{
248 return __tlb_remove_page(tlb, page);
240} 249}
241 250
242/* 251/*
diff --git a/arch/s390/include/asm/tlb.h b/arch/s390/include/asm/tlb.h
index 7a92e69c50bc..30759b560849 100644
--- a/arch/s390/include/asm/tlb.h
+++ b/arch/s390/include/asm/tlb.h
@@ -87,10 +87,10 @@ static inline void tlb_finish_mmu(struct mmu_gather *tlb,
87 * tlb_ptep_clear_flush. In both flush modes the tlb for a page cache page 87 * tlb_ptep_clear_flush. In both flush modes the tlb for a page cache page
88 * has already been freed, so just do free_page_and_swap_cache. 88 * has already been freed, so just do free_page_and_swap_cache.
89 */ 89 */
90static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page) 90static inline bool __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
91{ 91{
92 free_page_and_swap_cache(page); 92 free_page_and_swap_cache(page);
93 return 1; /* avoid calling tlb_flush_mmu */ 93 return false; /* avoid calling tlb_flush_mmu */
94} 94}
95 95
96static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) 96static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
@@ -98,6 +98,11 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
98 free_page_and_swap_cache(page); 98 free_page_and_swap_cache(page);
99} 99}
100 100
101static inline bool __tlb_remove_pte_page(struct mmu_gather *tlb,
102 struct page *page)
103{
104 return __tlb_remove_page(tlb, page);
105}
101/* 106/*
102 * pte_free_tlb frees a pte table and clears the CRSTE for the 107 * pte_free_tlb frees a pte table and clears the CRSTE for the
103 * page table from the tlb. 108 * page table from the tlb.
diff --git a/arch/sh/include/asm/tlb.h b/arch/sh/include/asm/tlb.h
index 62f80d2a9df9..21ae8f5546b2 100644
--- a/arch/sh/include/asm/tlb.h
+++ b/arch/sh/include/asm/tlb.h
@@ -101,7 +101,7 @@ static inline void tlb_flush_mmu(struct mmu_gather *tlb)
101static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page) 101static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
102{ 102{
103 free_page_and_swap_cache(page); 103 free_page_and_swap_cache(page);
104 return 1; /* avoid calling tlb_flush_mmu */ 104 return false; /* avoid calling tlb_flush_mmu */
105} 105}
106 106
107static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) 107static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
@@ -109,6 +109,12 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
109 __tlb_remove_page(tlb, page); 109 __tlb_remove_page(tlb, page);
110} 110}
111 111
112static inline bool __tlb_remove_pte_page(struct mmu_gather *tlb,
113 struct page *page)
114{
115 return __tlb_remove_page(tlb, page);
116}
117
112#define pte_free_tlb(tlb, ptep, addr) pte_free((tlb)->mm, ptep) 118#define pte_free_tlb(tlb, ptep, addr) pte_free((tlb)->mm, ptep)
113#define pmd_free_tlb(tlb, pmdp, addr) pmd_free((tlb)->mm, pmdp) 119#define pmd_free_tlb(tlb, pmdp, addr) pmd_free((tlb)->mm, pmdp)
114#define pud_free_tlb(tlb, pudp, addr) pud_free((tlb)->mm, pudp) 120#define pud_free_tlb(tlb, pudp, addr) pud_free((tlb)->mm, pudp)
diff --git a/arch/um/include/asm/tlb.h b/arch/um/include/asm/tlb.h
index 16eb63fac57d..3dc4cbb3c2c0 100644
--- a/arch/um/include/asm/tlb.h
+++ b/arch/um/include/asm/tlb.h
@@ -102,7 +102,7 @@ static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
102{ 102{
103 tlb->need_flush = 1; 103 tlb->need_flush = 1;
104 free_page_and_swap_cache(page); 104 free_page_and_swap_cache(page);
105 return 1; /* avoid calling tlb_flush_mmu */ 105 return false; /* avoid calling tlb_flush_mmu */
106} 106}
107 107
108static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) 108static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
@@ -110,6 +110,12 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
110 __tlb_remove_page(tlb, page); 110 __tlb_remove_page(tlb, page);
111} 111}
112 112
113static inline bool __tlb_remove_pte_page(struct mmu_gather *tlb,
114 struct page *page)
115{
116 return __tlb_remove_page(tlb, page);
117}
118
113/** 119/**
114 * tlb_remove_tlb_entry - remember a pte unmapping for later tlb invalidation. 120 * tlb_remove_tlb_entry - remember a pte unmapping for later tlb invalidation.
115 * 121 *
diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h
index 9dbb739cafa0..7b899a46a4cb 100644
--- a/include/asm-generic/tlb.h
+++ b/include/asm-generic/tlb.h
@@ -107,6 +107,11 @@ struct mmu_gather {
107 struct mmu_gather_batch local; 107 struct mmu_gather_batch local;
108 struct page *__pages[MMU_GATHER_BUNDLE]; 108 struct page *__pages[MMU_GATHER_BUNDLE];
109 unsigned int batch_count; 109 unsigned int batch_count;
110 /*
111 * __tlb_adjust_range will track the new addr here,
112 * that that we can adjust the range after the flush
113 */
114 unsigned long addr;
110}; 115};
111 116
112#define HAVE_GENERIC_MMU_GATHER 117#define HAVE_GENERIC_MMU_GATHER
@@ -115,23 +120,19 @@ void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long
115void tlb_flush_mmu(struct mmu_gather *tlb); 120void tlb_flush_mmu(struct mmu_gather *tlb);
116void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, 121void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start,
117 unsigned long end); 122 unsigned long end);
118int __tlb_remove_page(struct mmu_gather *tlb, struct page *page); 123bool __tlb_remove_page(struct mmu_gather *tlb, struct page *page);
119
120/* tlb_remove_page
121 * Similar to __tlb_remove_page but will call tlb_flush_mmu() itself when
122 * required.
123 */
124static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
125{
126 if (!__tlb_remove_page(tlb, page))
127 tlb_flush_mmu(tlb);
128}
129 124
130static inline void __tlb_adjust_range(struct mmu_gather *tlb, 125static inline void __tlb_adjust_range(struct mmu_gather *tlb,
131 unsigned long address) 126 unsigned long address)
132{ 127{
133 tlb->start = min(tlb->start, address); 128 tlb->start = min(tlb->start, address);
134 tlb->end = max(tlb->end, address + PAGE_SIZE); 129 tlb->end = max(tlb->end, address + PAGE_SIZE);
130 /*
131 * Track the last address with which we adjusted the range. This
132 * will be used later to adjust again after a mmu_flush due to
133 * failed __tlb_remove_page
134 */
135 tlb->addr = address;
135} 136}
136 137
137static inline void __tlb_reset_range(struct mmu_gather *tlb) 138static inline void __tlb_reset_range(struct mmu_gather *tlb)
@@ -144,6 +145,27 @@ static inline void __tlb_reset_range(struct mmu_gather *tlb)
144 } 145 }
145} 146}
146 147
148/* tlb_remove_page
149 * Similar to __tlb_remove_page but will call tlb_flush_mmu() itself when
150 * required.
151 */
152static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
153{
154 if (__tlb_remove_page(tlb, page)) {
155 tlb_flush_mmu(tlb);
156 __tlb_adjust_range(tlb, tlb->addr);
157 __tlb_remove_page(tlb, page);
158 }
159}
160
161static inline bool __tlb_remove_pte_page(struct mmu_gather *tlb, struct page *page)
162{
163 /* active->nr should be zero when we call this */
164 VM_BUG_ON_PAGE(tlb->active->nr, page);
165 __tlb_adjust_range(tlb, tlb->addr);
166 return __tlb_remove_page(tlb, page);
167}
168
147/* 169/*
148 * In the case of tlb vma handling, we can optimise these away in the 170 * In the case of tlb vma handling, we can optimise these away in the
149 * case where we're doing a full MM flush. When we're doing a munmap, 171 * case where we're doing a full MM flush. When we're doing a munmap,
diff --git a/mm/memory.c b/mm/memory.c
index 9e046819e619..12f31501c323 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -292,23 +292,24 @@ void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long e
292 * handling the additional races in SMP caused by other CPUs caching valid 292 * handling the additional races in SMP caused by other CPUs caching valid
293 * mappings in their TLBs. Returns the number of free page slots left. 293 * mappings in their TLBs. Returns the number of free page slots left.
294 * When out of page slots we must call tlb_flush_mmu(). 294 * When out of page slots we must call tlb_flush_mmu().
295 *returns true if the caller should flush.
295 */ 296 */
296int __tlb_remove_page(struct mmu_gather *tlb, struct page *page) 297bool __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
297{ 298{
298 struct mmu_gather_batch *batch; 299 struct mmu_gather_batch *batch;
299 300
300 VM_BUG_ON(!tlb->end); 301 VM_BUG_ON(!tlb->end);
301 302
302 batch = tlb->active; 303 batch = tlb->active;
303 batch->pages[batch->nr++] = page;
304 if (batch->nr == batch->max) { 304 if (batch->nr == batch->max) {
305 if (!tlb_next_batch(tlb)) 305 if (!tlb_next_batch(tlb))
306 return 0; 306 return true;
307 batch = tlb->active; 307 batch = tlb->active;
308 } 308 }
309 VM_BUG_ON_PAGE(batch->nr > batch->max, page); 309 VM_BUG_ON_PAGE(batch->nr > batch->max, page);
310 310
311 return batch->max - batch->nr; 311 batch->pages[batch->nr++] = page;
312 return false;
312} 313}
313 314
314#endif /* HAVE_GENERIC_MMU_GATHER */ 315#endif /* HAVE_GENERIC_MMU_GATHER */
@@ -1109,6 +1110,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
1109 pte_t *start_pte; 1110 pte_t *start_pte;
1110 pte_t *pte; 1111 pte_t *pte;
1111 swp_entry_t entry; 1112 swp_entry_t entry;
1113 struct page *pending_page = NULL;
1112 1114
1113again: 1115again:
1114 init_rss_vec(rss); 1116 init_rss_vec(rss);
@@ -1160,8 +1162,9 @@ again:
1160 page_remove_rmap(page, false); 1162 page_remove_rmap(page, false);
1161 if (unlikely(page_mapcount(page) < 0)) 1163 if (unlikely(page_mapcount(page) < 0))
1162 print_bad_pte(vma, addr, ptent, page); 1164 print_bad_pte(vma, addr, ptent, page);
1163 if (unlikely(!__tlb_remove_page(tlb, page))) { 1165 if (unlikely(__tlb_remove_page(tlb, page))) {
1164 force_flush = 1; 1166 force_flush = 1;
1167 pending_page = page;
1165 addr += PAGE_SIZE; 1168 addr += PAGE_SIZE;
1166 break; 1169 break;
1167 } 1170 }
@@ -1202,7 +1205,11 @@ again:
1202 if (force_flush) { 1205 if (force_flush) {
1203 force_flush = 0; 1206 force_flush = 0;
1204 tlb_flush_mmu_free(tlb); 1207 tlb_flush_mmu_free(tlb);
1205 1208 if (pending_page) {
1209 /* remove the page with new size */
1210 __tlb_remove_pte_page(tlb, pending_page);
1211 pending_page = NULL;
1212 }
1206 if (addr != end) 1213 if (addr != end)
1207 goto again; 1214 goto again;
1208 } 1215 }