aboutsummaryrefslogtreecommitdiffstats
path: root/mm/msync.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/msync.c')
-rw-r--r--mm/msync.c78
1 files changed, 31 insertions, 47 deletions
diff --git a/mm/msync.c b/mm/msync.c
index d0f5a1bce7..0e040e9c39 100644
--- a/mm/msync.c
+++ b/mm/msync.c
@@ -17,40 +17,48 @@
17#include <asm/pgtable.h> 17#include <asm/pgtable.h>
18#include <asm/tlbflush.h> 18#include <asm/tlbflush.h>
19 19
20/* 20static void msync_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
21 * Called with mm->page_table_lock held to protect against other
22 * threads/the swapper from ripping pte's out from under us.
23 */
24
25static void sync_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
26 unsigned long addr, unsigned long end) 21 unsigned long addr, unsigned long end)
27{ 22{
28 pte_t *pte; 23 pte_t *pte;
24 spinlock_t *ptl;
25 int progress = 0;
29 26
30 pte = pte_offset_map(pmd, addr); 27again:
28 pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
31 do { 29 do {
32 unsigned long pfn; 30 unsigned long pfn;
33 struct page *page; 31 struct page *page;
34 32
33 if (progress >= 64) {
34 progress = 0;
35 if (need_resched() || need_lockbreak(ptl))
36 break;
37 }
38 progress++;
35 if (!pte_present(*pte)) 39 if (!pte_present(*pte))
36 continue; 40 continue;
37 if (!pte_maybe_dirty(*pte)) 41 if (!pte_maybe_dirty(*pte))
38 continue; 42 continue;
39 pfn = pte_pfn(*pte); 43 pfn = pte_pfn(*pte);
40 if (!pfn_valid(pfn)) 44 if (unlikely(!pfn_valid(pfn))) {
45 print_bad_pte(vma, *pte, addr);
41 continue; 46 continue;
47 }
42 page = pfn_to_page(pfn); 48 page = pfn_to_page(pfn);
43 if (PageReserved(page))
44 continue;
45 49
46 if (ptep_clear_flush_dirty(vma, addr, pte) || 50 if (ptep_clear_flush_dirty(vma, addr, pte) ||
47 page_test_and_clear_dirty(page)) 51 page_test_and_clear_dirty(page))
48 set_page_dirty(page); 52 set_page_dirty(page);
53 progress += 3;
49 } while (pte++, addr += PAGE_SIZE, addr != end); 54 } while (pte++, addr += PAGE_SIZE, addr != end);
50 pte_unmap(pte - 1); 55 pte_unmap_unlock(pte - 1, ptl);
56 cond_resched();
57 if (addr != end)
58 goto again;
51} 59}
52 60
53static inline void sync_pmd_range(struct vm_area_struct *vma, pud_t *pud, 61static inline void msync_pmd_range(struct vm_area_struct *vma, pud_t *pud,
54 unsigned long addr, unsigned long end) 62 unsigned long addr, unsigned long end)
55{ 63{
56 pmd_t *pmd; 64 pmd_t *pmd;
@@ -61,11 +69,11 @@ static inline void sync_pmd_range(struct vm_area_struct *vma, pud_t *pud,
61 next = pmd_addr_end(addr, end); 69 next = pmd_addr_end(addr, end);
62 if (pmd_none_or_clear_bad(pmd)) 70 if (pmd_none_or_clear_bad(pmd))
63 continue; 71 continue;
64 sync_pte_range(vma, pmd, addr, next); 72 msync_pte_range(vma, pmd, addr, next);
65 } while (pmd++, addr = next, addr != end); 73 } while (pmd++, addr = next, addr != end);
66} 74}
67 75
68static inline void sync_pud_range(struct vm_area_struct *vma, pgd_t *pgd, 76static inline void msync_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
69 unsigned long addr, unsigned long end) 77 unsigned long addr, unsigned long end)
70{ 78{
71 pud_t *pud; 79 pud_t *pud;
@@ -76,58 +84,34 @@ static inline void sync_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
76 next = pud_addr_end(addr, end); 84 next = pud_addr_end(addr, end);
77 if (pud_none_or_clear_bad(pud)) 85 if (pud_none_or_clear_bad(pud))
78 continue; 86 continue;
79 sync_pmd_range(vma, pud, addr, next); 87 msync_pmd_range(vma, pud, addr, next);
80 } while (pud++, addr = next, addr != end); 88 } while (pud++, addr = next, addr != end);
81} 89}
82 90
83static void sync_page_range(struct vm_area_struct *vma, 91static void msync_page_range(struct vm_area_struct *vma,
84 unsigned long addr, unsigned long end) 92 unsigned long addr, unsigned long end)
85{ 93{
86 struct mm_struct *mm = vma->vm_mm;
87 pgd_t *pgd; 94 pgd_t *pgd;
88 unsigned long next; 95 unsigned long next;
89 96
90 /* For hugepages we can't go walking the page table normally, 97 /* For hugepages we can't go walking the page table normally,
91 * but that's ok, hugetlbfs is memory based, so we don't need 98 * but that's ok, hugetlbfs is memory based, so we don't need
92 * to do anything more on an msync() */ 99 * to do anything more on an msync().
93 if (is_vm_hugetlb_page(vma)) 100 * Can't do anything with VM_RESERVED regions either.
101 */
102 if (vma->vm_flags & (VM_HUGETLB|VM_RESERVED))
94 return; 103 return;
95 104
96 BUG_ON(addr >= end); 105 BUG_ON(addr >= end);
97 pgd = pgd_offset(mm, addr); 106 pgd = pgd_offset(vma->vm_mm, addr);
98 flush_cache_range(vma, addr, end); 107 flush_cache_range(vma, addr, end);
99 spin_lock(&mm->page_table_lock);
100 do { 108 do {
101 next = pgd_addr_end(addr, end); 109 next = pgd_addr_end(addr, end);
102 if (pgd_none_or_clear_bad(pgd)) 110 if (pgd_none_or_clear_bad(pgd))
103 continue; 111 continue;
104 sync_pud_range(vma, pgd, addr, next); 112 msync_pud_range(vma, pgd, addr, next);
105 } while (pgd++, addr = next, addr != end); 113 } while (pgd++, addr = next, addr != end);
106 spin_unlock(&mm->page_table_lock);
107}
108
109#ifdef CONFIG_PREEMPT
110static inline void filemap_sync(struct vm_area_struct *vma,
111 unsigned long addr, unsigned long end)
112{
113 const size_t chunk = 64 * 1024; /* bytes */
114 unsigned long next;
115
116 do {
117 next = addr + chunk;
118 if (next > end || next < addr)
119 next = end;
120 sync_page_range(vma, addr, next);
121 cond_resched();
122 } while (addr = next, addr != end);
123}
124#else
125static inline void filemap_sync(struct vm_area_struct *vma,
126 unsigned long addr, unsigned long end)
127{
128 sync_page_range(vma, addr, end);
129} 114}
130#endif
131 115
132/* 116/*
133 * MS_SYNC syncs the entire file - including mappings. 117 * MS_SYNC syncs the entire file - including mappings.
@@ -150,7 +134,7 @@ static int msync_interval(struct vm_area_struct *vma,
150 return -EBUSY; 134 return -EBUSY;
151 135
152 if (file && (vma->vm_flags & VM_SHARED)) { 136 if (file && (vma->vm_flags & VM_SHARED)) {
153 filemap_sync(vma, addr, end); 137 msync_page_range(vma, addr, end);
154 138
155 if (flags & MS_SYNC) { 139 if (flags & MS_SYNC) {
156 struct address_space *mapping = file->f_mapping; 140 struct address_space *mapping = file->f_mapping;