aboutsummaryrefslogtreecommitdiffstats
path: root/mm/pagewalk.c
diff options
context:
space:
mode:
authorNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>2015-02-11 18:28:06 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-11 20:06:06 -0500
commit48684a65b4e3ff544d62532c1b78962c9677b632 (patch)
tree884da0dc303b41042c9928b40ef7ae651d34e22d /mm/pagewalk.c
parent6f4576e3687b1f93145b89fce49d6a8fec9e7dc2 (diff)
mm: pagewalk: fix misbehavior of walk_page_range for vma(VM_PFNMAP)
walk_page_range() silently skips vma having VM_PFNMAP set, which leads to undesirable behaviour at client end (who called walk_page_range). For example for pagemap_read(), when no callbacks are called against VM_PFNMAP vma, pagemap_read() may prepare pagemap data for next virtual address range at wrong index. That could confuse and/or break userspace applications. This patch avoid this misbehavior caused by vma(VM_PFNMAP) like follows: - for pagemap_read() which has its own ->pte_hole(), call the ->pte_hole() over vma(VM_PFNMAP), - for clear_refs and queue_pages which have their own ->tests_walk, just return 1 and skip vma(VM_PFNMAP). This is no problem because these are not interested in hole regions, - for other callers, just skip the vma(VM_PFNMAP) as a default behavior. Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Signed-off-by: Shiraz Hashim <shashim@codeaurora.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/pagewalk.c')
-rw-r--r--mm/pagewalk.c21
1 files changed, 13 insertions, 8 deletions
diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index 4c9a653ba563..75c1f2878519 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -35,7 +35,7 @@ static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
35 do { 35 do {
36again: 36again:
37 next = pmd_addr_end(addr, end); 37 next = pmd_addr_end(addr, end);
38 if (pmd_none(*pmd)) { 38 if (pmd_none(*pmd) || !walk->vma) {
39 if (walk->pte_hole) 39 if (walk->pte_hole)
40 err = walk->pte_hole(addr, next, walk); 40 err = walk->pte_hole(addr, next, walk);
41 if (err) 41 if (err)
@@ -165,9 +165,6 @@ static int walk_hugetlb_range(unsigned long addr, unsigned long end,
165 * or skip it via the returned value. Return 0 if we do walk over the 165 * or skip it via the returned value. Return 0 if we do walk over the
166 * current vma, and return 1 if we skip the vma. Negative values means 166 * current vma, and return 1 if we skip the vma. Negative values means
167 * error, where we abort the current walk. 167 * error, where we abort the current walk.
168 *
169 * Default check (only VM_PFNMAP check for now) is used when the caller
170 * doesn't define test_walk() callback.
171 */ 168 */
172static int walk_page_test(unsigned long start, unsigned long end, 169static int walk_page_test(unsigned long start, unsigned long end,
173 struct mm_walk *walk) 170 struct mm_walk *walk)
@@ -178,11 +175,19 @@ static int walk_page_test(unsigned long start, unsigned long end,
178 return walk->test_walk(start, end, walk); 175 return walk->test_walk(start, end, walk);
179 176
180 /* 177 /*
181 * Do not walk over vma(VM_PFNMAP), because we have no valid struct 178 * vma(VM_PFNMAP) doesn't have any valid struct pages behind VM_PFNMAP
182 * page backing a VM_PFNMAP range. See also commit a9ff785e4437. 179 * range, so we don't walk over it as we do for normal vmas. However,
180 * Some callers are interested in handling hole range and they don't
181 * want to just ignore any single address range. Such users certainly
182 * define their ->pte_hole() callbacks, so let's delegate them to handle
183 * vma(VM_PFNMAP).
183 */ 184 */
184 if (vma->vm_flags & VM_PFNMAP) 185 if (vma->vm_flags & VM_PFNMAP) {
185 return 1; 186 int err = 1;
187 if (walk->pte_hole)
188 err = walk->pte_hole(start, end, walk);
189 return err ? err : 1;
190 }
186 return 0; 191 return 0;
187} 192}
188 193