diff options
Diffstat (limited to 'mm/memory.c')
-rw-r--r-- | mm/memory.c | 49 |
1 files changed, 31 insertions, 18 deletions
diff --git a/mm/memory.c b/mm/memory.c index 1ce2e2a734fc..af84bc0ec17c 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -209,14 +209,15 @@ static int tlb_next_batch(struct mmu_gather *tlb) | |||
209 | * tear-down from @mm. The @fullmm argument is used when @mm is without | 209 | * tear-down from @mm. The @fullmm argument is used when @mm is without |
210 | * users and we're going to destroy the full address space (exit/execve). | 210 | * users and we're going to destroy the full address space (exit/execve). |
211 | */ | 211 | */ |
212 | void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, bool fullmm) | 212 | void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) |
213 | { | 213 | { |
214 | tlb->mm = mm; | 214 | tlb->mm = mm; |
215 | 215 | ||
216 | tlb->fullmm = fullmm; | 216 | /* Is it from 0 to ~0? */ |
217 | tlb->fullmm = !(start | (end+1)); | ||
217 | tlb->need_flush_all = 0; | 218 | tlb->need_flush_all = 0; |
218 | tlb->start = -1UL; | 219 | tlb->start = start; |
219 | tlb->end = 0; | 220 | tlb->end = end; |
220 | tlb->need_flush = 0; | 221 | tlb->need_flush = 0; |
221 | tlb->local.next = NULL; | 222 | tlb->local.next = NULL; |
222 | tlb->local.nr = 0; | 223 | tlb->local.nr = 0; |
@@ -256,8 +257,6 @@ void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long e | |||
256 | { | 257 | { |
257 | struct mmu_gather_batch *batch, *next; | 258 | struct mmu_gather_batch *batch, *next; |
258 | 259 | ||
259 | tlb->start = start; | ||
260 | tlb->end = end; | ||
261 | tlb_flush_mmu(tlb); | 260 | tlb_flush_mmu(tlb); |
262 | 261 | ||
263 | /* keep the page table cache within bounds */ | 262 | /* keep the page table cache within bounds */ |
@@ -1099,7 +1098,6 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, | |||
1099 | spinlock_t *ptl; | 1098 | spinlock_t *ptl; |
1100 | pte_t *start_pte; | 1099 | pte_t *start_pte; |
1101 | pte_t *pte; | 1100 | pte_t *pte; |
1102 | unsigned long range_start = addr; | ||
1103 | 1101 | ||
1104 | again: | 1102 | again: |
1105 | init_rss_vec(rss); | 1103 | init_rss_vec(rss); |
@@ -1141,9 +1139,12 @@ again: | |||
1141 | continue; | 1139 | continue; |
1142 | if (unlikely(details) && details->nonlinear_vma | 1140 | if (unlikely(details) && details->nonlinear_vma |
1143 | && linear_page_index(details->nonlinear_vma, | 1141 | && linear_page_index(details->nonlinear_vma, |
1144 | addr) != page->index) | 1142 | addr) != page->index) { |
1145 | set_pte_at(mm, addr, pte, | 1143 | pte_t ptfile = pgoff_to_pte(page->index); |
1146 | pgoff_to_pte(page->index)); | 1144 | if (pte_soft_dirty(ptent)) |
1145 | pte_file_mksoft_dirty(ptfile); | ||
1146 | set_pte_at(mm, addr, pte, ptfile); | ||
1147 | } | ||
1147 | if (PageAnon(page)) | 1148 | if (PageAnon(page)) |
1148 | rss[MM_ANONPAGES]--; | 1149 | rss[MM_ANONPAGES]--; |
1149 | else { | 1150 | else { |
@@ -1202,17 +1203,25 @@ again: | |||
1202 | * and page-free while holding it. | 1203 | * and page-free while holding it. |
1203 | */ | 1204 | */ |
1204 | if (force_flush) { | 1205 | if (force_flush) { |
1206 | unsigned long old_end; | ||
1207 | |||
1205 | force_flush = 0; | 1208 | force_flush = 0; |
1206 | 1209 | ||
1207 | #ifdef HAVE_GENERIC_MMU_GATHER | 1210 | /* |
1208 | tlb->start = range_start; | 1211 | * Flush the TLB just for the previous segment, |
1212 | * then update the range to be the remaining | ||
1213 | * TLB range. | ||
1214 | */ | ||
1215 | old_end = tlb->end; | ||
1209 | tlb->end = addr; | 1216 | tlb->end = addr; |
1210 | #endif | 1217 | |
1211 | tlb_flush_mmu(tlb); | 1218 | tlb_flush_mmu(tlb); |
1212 | if (addr != end) { | 1219 | |
1213 | range_start = addr; | 1220 | tlb->start = addr; |
1221 | tlb->end = old_end; | ||
1222 | |||
1223 | if (addr != end) | ||
1214 | goto again; | 1224 | goto again; |
1215 | } | ||
1216 | } | 1225 | } |
1217 | 1226 | ||
1218 | return addr; | 1227 | return addr; |
@@ -1397,7 +1406,7 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long start, | |||
1397 | unsigned long end = start + size; | 1406 | unsigned long end = start + size; |
1398 | 1407 | ||
1399 | lru_add_drain(); | 1408 | lru_add_drain(); |
1400 | tlb_gather_mmu(&tlb, mm, 0); | 1409 | tlb_gather_mmu(&tlb, mm, start, end); |
1401 | update_hiwater_rss(mm); | 1410 | update_hiwater_rss(mm); |
1402 | mmu_notifier_invalidate_range_start(mm, start, end); | 1411 | mmu_notifier_invalidate_range_start(mm, start, end); |
1403 | for ( ; vma && vma->vm_start < end; vma = vma->vm_next) | 1412 | for ( ; vma && vma->vm_start < end; vma = vma->vm_next) |
@@ -1423,7 +1432,7 @@ static void zap_page_range_single(struct vm_area_struct *vma, unsigned long addr | |||
1423 | unsigned long end = address + size; | 1432 | unsigned long end = address + size; |
1424 | 1433 | ||
1425 | lru_add_drain(); | 1434 | lru_add_drain(); |
1426 | tlb_gather_mmu(&tlb, mm, 0); | 1435 | tlb_gather_mmu(&tlb, mm, address, end); |
1427 | update_hiwater_rss(mm); | 1436 | update_hiwater_rss(mm); |
1428 | mmu_notifier_invalidate_range_start(mm, address, end); | 1437 | mmu_notifier_invalidate_range_start(mm, address, end); |
1429 | unmap_single_vma(&tlb, vma, address, end, details); | 1438 | unmap_single_vma(&tlb, vma, address, end, details); |
@@ -3115,6 +3124,8 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
3115 | exclusive = 1; | 3124 | exclusive = 1; |
3116 | } | 3125 | } |
3117 | flush_icache_page(vma, page); | 3126 | flush_icache_page(vma, page); |
3127 | if (pte_swp_soft_dirty(orig_pte)) | ||
3128 | pte = pte_mksoft_dirty(pte); | ||
3118 | set_pte_at(mm, address, page_table, pte); | 3129 | set_pte_at(mm, address, page_table, pte); |
3119 | if (page == swapcache) | 3130 | if (page == swapcache) |
3120 | do_page_add_anon_rmap(page, vma, address, exclusive); | 3131 | do_page_add_anon_rmap(page, vma, address, exclusive); |
@@ -3408,6 +3419,8 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, | |||
3408 | entry = mk_pte(page, vma->vm_page_prot); | 3419 | entry = mk_pte(page, vma->vm_page_prot); |
3409 | if (flags & FAULT_FLAG_WRITE) | 3420 | if (flags & FAULT_FLAG_WRITE) |
3410 | entry = maybe_mkwrite(pte_mkdirty(entry), vma); | 3421 | entry = maybe_mkwrite(pte_mkdirty(entry), vma); |
3422 | else if (pte_file(orig_pte) && pte_file_soft_dirty(orig_pte)) | ||
3423 | pte_mksoft_dirty(entry); | ||
3411 | if (anon) { | 3424 | if (anon) { |
3412 | inc_mm_counter_fast(mm, MM_ANONPAGES); | 3425 | inc_mm_counter_fast(mm, MM_ANONPAGES); |
3413 | page_add_new_anon_rmap(page, vma, address); | 3426 | page_add_new_anon_rmap(page, vma, address); |