aboutsummaryrefslogtreecommitdiffstats
path: root/mm/gup.c
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2014-06-04 19:08:11 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-04 19:54:04 -0400
commit69e68b4f03135da4a09d1215a3942d7dabd1075b (patch)
treeb69462f8a648708c31244d89a7fda3bde225ac7c /mm/gup.c
parentf2b495ca82e188fd2818479a551f126edf023756 (diff)
mm: cleanup follow_page_mask()
Cleanups: - move pte-related code to separate function. It's about half of the function; - get rid of some goto-logic; - use 'return NULL' instead of 'return page' where page can only be NULL; Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/gup.c')
-rw-r--r--mm/gup.c231
1 files changed, 119 insertions, 112 deletions
diff --git a/mm/gup.c b/mm/gup.c
index 0bf127b332e7..406367845ded 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -12,105 +12,35 @@
12 12
13#include "internal.h" 13#include "internal.h"
14 14
15/** 15static struct page *no_page_table(struct vm_area_struct *vma,
16 * follow_page_mask - look up a page descriptor from a user-virtual address 16 unsigned int flags)
17 * @vma: vm_area_struct mapping @address
18 * @address: virtual address to look up
19 * @flags: flags modifying lookup behaviour
20 * @page_mask: on output, *page_mask is set according to the size of the page
21 *
22 * @flags can have FOLL_ flags set, defined in <linux/mm.h>
23 *
24 * Returns the mapped (struct page *), %NULL if no mapping exists, or
25 * an error pointer if there is a mapping to something not represented
26 * by a page descriptor (see also vm_normal_page()).
27 */
28struct page *follow_page_mask(struct vm_area_struct *vma,
29 unsigned long address, unsigned int flags,
30 unsigned int *page_mask)
31{ 17{
32 pgd_t *pgd; 18 /*
33 pud_t *pud; 19 * When core dumping an enormous anonymous area that nobody
34 pmd_t *pmd; 20 * has touched so far, we don't want to allocate unnecessary pages or
35 pte_t *ptep, pte; 21 * page tables. Return error instead of NULL to skip handle_mm_fault,
36 spinlock_t *ptl; 22 * then get_dump_page() will return NULL to leave a hole in the dump.
37 struct page *page; 23 * But we can only make this optimization where a hole would surely
38 struct mm_struct *mm = vma->vm_mm; 24 * be zero-filled if handle_mm_fault() actually did handle it.
39 25 */
40 *page_mask = 0; 26 if ((flags & FOLL_DUMP) && (!vma->vm_ops || !vma->vm_ops->fault))
41 27 return ERR_PTR(-EFAULT);
42 page = follow_huge_addr(mm, address, flags & FOLL_WRITE); 28 return NULL;
43 if (!IS_ERR(page)) { 29}
44 BUG_ON(flags & FOLL_GET);
45 goto out;
46 }
47
48 page = NULL;
49 pgd = pgd_offset(mm, address);
50 if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
51 goto no_page_table;
52 30
53 pud = pud_offset(pgd, address); 31static struct page *follow_page_pte(struct vm_area_struct *vma,
54 if (pud_none(*pud)) 32 unsigned long address, pmd_t *pmd, unsigned int flags)
55 goto no_page_table; 33{
56 if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) { 34 struct mm_struct *mm = vma->vm_mm;
57 if (flags & FOLL_GET) 35 struct page *page;
58 goto out; 36 spinlock_t *ptl;
59 page = follow_huge_pud(mm, address, pud, flags & FOLL_WRITE); 37 pte_t *ptep, pte;
60 goto out;
61 }
62 if (unlikely(pud_bad(*pud)))
63 goto no_page_table;
64 38
65 pmd = pmd_offset(pud, address); 39retry:
66 if (pmd_none(*pmd))
67 goto no_page_table;
68 if (pmd_huge(*pmd) && vma->vm_flags & VM_HUGETLB) {
69 page = follow_huge_pmd(mm, address, pmd, flags & FOLL_WRITE);
70 if (flags & FOLL_GET) {
71 /*
72 * Refcount on tail pages are not well-defined and
73 * shouldn't be taken. The caller should handle a NULL
74 * return when trying to follow tail pages.
75 */
76 if (PageHead(page))
77 get_page(page);
78 else {
79 page = NULL;
80 goto out;
81 }
82 }
83 goto out;
84 }
85 if ((flags & FOLL_NUMA) && pmd_numa(*pmd))
86 goto no_page_table;
87 if (pmd_trans_huge(*pmd)) {
88 if (flags & FOLL_SPLIT) {
89 split_huge_page_pmd(vma, address, pmd);
90 goto split_fallthrough;
91 }
92 ptl = pmd_lock(mm, pmd);
93 if (likely(pmd_trans_huge(*pmd))) {
94 if (unlikely(pmd_trans_splitting(*pmd))) {
95 spin_unlock(ptl);
96 wait_split_huge_page(vma->anon_vma, pmd);
97 } else {
98 page = follow_trans_huge_pmd(vma, address,
99 pmd, flags);
100 spin_unlock(ptl);
101 *page_mask = HPAGE_PMD_NR - 1;
102 goto out;
103 }
104 } else
105 spin_unlock(ptl);
106 /* fall through */
107 }
108split_fallthrough:
109 if (unlikely(pmd_bad(*pmd))) 40 if (unlikely(pmd_bad(*pmd)))
110 goto no_page_table; 41 return no_page_table(vma, flags);
111 42
112 ptep = pte_offset_map_lock(mm, pmd, address, &ptl); 43 ptep = pte_offset_map_lock(mm, pmd, address, &ptl);
113
114 pte = *ptep; 44 pte = *ptep;
115 if (!pte_present(pte)) { 45 if (!pte_present(pte)) {
116 swp_entry_t entry; 46 swp_entry_t entry;
@@ -128,12 +58,14 @@ split_fallthrough:
128 goto no_page; 58 goto no_page;
129 pte_unmap_unlock(ptep, ptl); 59 pte_unmap_unlock(ptep, ptl);
130 migration_entry_wait(mm, pmd, address); 60 migration_entry_wait(mm, pmd, address);
131 goto split_fallthrough; 61 goto retry;
132 } 62 }
133 if ((flags & FOLL_NUMA) && pte_numa(pte)) 63 if ((flags & FOLL_NUMA) && pte_numa(pte))
134 goto no_page; 64 goto no_page;
135 if ((flags & FOLL_WRITE) && !pte_write(pte)) 65 if ((flags & FOLL_WRITE) && !pte_write(pte)) {
136 goto unlock; 66 pte_unmap_unlock(ptep, ptl);
67 return NULL;
68 }
137 69
138 page = vm_normal_page(vma, address, pte); 70 page = vm_normal_page(vma, address, pte);
139 if (unlikely(!page)) { 71 if (unlikely(!page)) {
@@ -178,11 +110,8 @@ split_fallthrough:
178 unlock_page(page); 110 unlock_page(page);
179 } 111 }
180 } 112 }
181unlock:
182 pte_unmap_unlock(ptep, ptl); 113 pte_unmap_unlock(ptep, ptl);
183out:
184 return page; 114 return page;
185
186bad_page: 115bad_page:
187 pte_unmap_unlock(ptep, ptl); 116 pte_unmap_unlock(ptep, ptl);
188 return ERR_PTR(-EFAULT); 117 return ERR_PTR(-EFAULT);
@@ -190,21 +119,99 @@ bad_page:
190no_page: 119no_page:
191 pte_unmap_unlock(ptep, ptl); 120 pte_unmap_unlock(ptep, ptl);
192 if (!pte_none(pte)) 121 if (!pte_none(pte))
122 return NULL;
123 return no_page_table(vma, flags);
124}
125
126/**
127 * follow_page_mask - look up a page descriptor from a user-virtual address
128 * @vma: vm_area_struct mapping @address
129 * @address: virtual address to look up
130 * @flags: flags modifying lookup behaviour
131 * @page_mask: on output, *page_mask is set according to the size of the page
132 *
133 * @flags can have FOLL_ flags set, defined in <linux/mm.h>
134 *
135 * Returns the mapped (struct page *), %NULL if no mapping exists, or
136 * an error pointer if there is a mapping to something not represented
137 * by a page descriptor (see also vm_normal_page()).
138 */
139struct page *follow_page_mask(struct vm_area_struct *vma,
140 unsigned long address, unsigned int flags,
141 unsigned int *page_mask)
142{
143 pgd_t *pgd;
144 pud_t *pud;
145 pmd_t *pmd;
146 spinlock_t *ptl;
147 struct page *page;
148 struct mm_struct *mm = vma->vm_mm;
149
150 *page_mask = 0;
151
152 page = follow_huge_addr(mm, address, flags & FOLL_WRITE);
153 if (!IS_ERR(page)) {
154 BUG_ON(flags & FOLL_GET);
193 return page; 155 return page;
156 }
194 157
195no_page_table: 158 pgd = pgd_offset(mm, address);
196 /* 159 if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
197 * When core dumping an enormous anonymous area that nobody 160 return no_page_table(vma, flags);
198 * has touched so far, we don't want to allocate unnecessary pages or 161
199 * page tables. Return error instead of NULL to skip handle_mm_fault, 162 pud = pud_offset(pgd, address);
200 * then get_dump_page() will return NULL to leave a hole in the dump. 163 if (pud_none(*pud))
201 * But we can only make this optimization where a hole would surely 164 return no_page_table(vma, flags);
202 * be zero-filled if handle_mm_fault() actually did handle it. 165 if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) {
203 */ 166 if (flags & FOLL_GET)
204 if ((flags & FOLL_DUMP) && 167 return NULL;
205 (!vma->vm_ops || !vma->vm_ops->fault)) 168 page = follow_huge_pud(mm, address, pud, flags & FOLL_WRITE);
206 return ERR_PTR(-EFAULT); 169 return page;
207 return page; 170 }
171 if (unlikely(pud_bad(*pud)))
172 return no_page_table(vma, flags);
173
174 pmd = pmd_offset(pud, address);
175 if (pmd_none(*pmd))
176 return no_page_table(vma, flags);
177 if (pmd_huge(*pmd) && vma->vm_flags & VM_HUGETLB) {
178 page = follow_huge_pmd(mm, address, pmd, flags & FOLL_WRITE);
179 if (flags & FOLL_GET) {
180 /*
181 * Refcount on tail pages are not well-defined and
182 * shouldn't be taken. The caller should handle a NULL
183 * return when trying to follow tail pages.
184 */
185 if (PageHead(page))
186 get_page(page);
187 else
188 page = NULL;
189 }
190 return page;
191 }
192 if ((flags & FOLL_NUMA) && pmd_numa(*pmd))
193 return no_page_table(vma, flags);
194 if (pmd_trans_huge(*pmd)) {
195 if (flags & FOLL_SPLIT) {
196 split_huge_page_pmd(vma, address, pmd);
197 return follow_page_pte(vma, address, pmd, flags);
198 }
199 ptl = pmd_lock(mm, pmd);
200 if (likely(pmd_trans_huge(*pmd))) {
201 if (unlikely(pmd_trans_splitting(*pmd))) {
202 spin_unlock(ptl);
203 wait_split_huge_page(vma->anon_vma, pmd);
204 } else {
205 page = follow_trans_huge_pmd(vma, address,
206 pmd, flags);
207 spin_unlock(ptl);
208 *page_mask = HPAGE_PMD_NR - 1;
209 return page;
210 }
211 } else
212 spin_unlock(ptl);
213 }
214 return follow_page_pte(vma, address, pmd, flags);
208} 215}
209 216
210static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) 217static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr)