diff options
author | Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | 2014-06-04 19:08:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-04 19:54:04 -0400 |
commit | 69e68b4f03135da4a09d1215a3942d7dabd1075b (patch) | |
tree | b69462f8a648708c31244d89a7fda3bde225ac7c | |
parent | f2b495ca82e188fd2818479a551f126edf023756 (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>
-rw-r--r-- | mm/gup.c | 231 |
1 files changed, 119 insertions, 112 deletions
@@ -12,105 +12,35 @@ | |||
12 | 12 | ||
13 | #include "internal.h" | 13 | #include "internal.h" |
14 | 14 | ||
15 | /** | 15 | static 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 | */ | ||
28 | struct 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); | 31 | static 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); | 39 | retry: |
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 | } | ||
108 | split_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 | } |
181 | unlock: | ||
182 | pte_unmap_unlock(ptep, ptl); | 113 | pte_unmap_unlock(ptep, ptl); |
183 | out: | ||
184 | return page; | 114 | return page; |
185 | |||
186 | bad_page: | 115 | bad_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: | |||
190 | no_page: | 119 | no_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 | */ | ||
139 | struct 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 | ||
195 | no_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 | ||
210 | static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) | 217 | static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) |