2008-02-05 09:29:01 +03:00
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/sched.h>
|
2009-12-15 04:59:59 +03:00
|
|
|
#include <linux/hugetlb.h>
|
2008-02-05 09:29:01 +03:00
|
|
|
|
|
|
|
static int walk_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
|
2008-06-13 02:21:47 +04:00
|
|
|
struct mm_walk *walk)
|
2008-02-05 09:29:01 +03:00
|
|
|
{
|
|
|
|
pte_t *pte;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
pte = pte_offset_map(pmd, addr);
|
2008-04-28 13:11:47 +04:00
|
|
|
for (;;) {
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk->pte_entry(pte, addr, addr + PAGE_SIZE, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (err)
|
|
|
|
break;
|
2008-04-28 13:11:47 +04:00
|
|
|
addr += PAGE_SIZE;
|
|
|
|
if (addr == end)
|
|
|
|
break;
|
|
|
|
pte++;
|
|
|
|
}
|
2008-02-05 09:29:01 +03:00
|
|
|
|
|
|
|
pte_unmap(pte);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
|
2008-06-13 02:21:47 +04:00
|
|
|
struct mm_walk *walk)
|
2008-02-05 09:29:01 +03:00
|
|
|
{
|
|
|
|
pmd_t *pmd;
|
|
|
|
unsigned long next;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
pmd = pmd_offset(pud, addr);
|
|
|
|
do {
|
2011-03-23 02:32:56 +03:00
|
|
|
again:
|
2008-02-05 09:29:01 +03:00
|
|
|
next = pmd_addr_end(addr, end);
|
2011-03-23 02:32:56 +03:00
|
|
|
if (pmd_none(*pmd)) {
|
2008-02-05 09:29:01 +03:00
|
|
|
if (walk->pte_hole)
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk->pte_hole(addr, next, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
2011-03-23 02:32:56 +03:00
|
|
|
/*
|
|
|
|
* This implies that each ->pmd_entry() handler
|
|
|
|
* needs to know about pmd_trans_huge() pmds
|
|
|
|
*/
|
2008-02-05 09:29:01 +03:00
|
|
|
if (walk->pmd_entry)
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk->pmd_entry(pmd, addr, next, walk);
|
2011-03-23 02:32:56 +03:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check this here so we only break down trans_huge
|
|
|
|
* pages when we _need_ to
|
|
|
|
*/
|
|
|
|
if (!walk->pte_entry)
|
|
|
|
continue;
|
|
|
|
|
2012-12-13 01:50:59 +04:00
|
|
|
split_huge_page_pmd_mm(walk->mm, addr, pmd);
|
mm: thp: fix pmd_bad() triggering in code paths holding mmap_sem read mode
In some cases it may happen that pmd_none_or_clear_bad() is called with
the mmap_sem hold in read mode. In those cases the huge page faults can
allocate hugepmds under pmd_none_or_clear_bad() and that can trigger a
false positive from pmd_bad() that will not like to see a pmd
materializing as trans huge.
It's not khugepaged causing the problem, khugepaged holds the mmap_sem
in write mode (and all those sites must hold the mmap_sem in read mode
to prevent pagetables to go away from under them, during code review it
seems vm86 mode on 32bit kernels requires that too unless it's
restricted to 1 thread per process or UP builds). The race is only with
the huge pagefaults that can convert a pmd_none() into a
pmd_trans_huge().
Effectively all these pmd_none_or_clear_bad() sites running with
mmap_sem in read mode are somewhat speculative with the page faults, and
the result is always undefined when they run simultaneously. This is
probably why it wasn't common to run into this. For example if the
madvise(MADV_DONTNEED) runs zap_page_range() shortly before the page
fault, the hugepage will not be zapped, if the page fault runs first it
will be zapped.
Altering pmd_bad() not to error out if it finds hugepmds won't be enough
to fix this, because zap_pmd_range would then proceed to call
zap_pte_range (which would be incorrect if the pmd become a
pmd_trans_huge()).
The simplest way to fix this is to read the pmd in the local stack
(regardless of what we read, no need of actual CPU barriers, only
compiler barrier needed), and be sure it is not changing under the code
that computes its value. Even if the real pmd is changing under the
value we hold on the stack, we don't care. If we actually end up in
zap_pte_range it means the pmd was not none already and it was not huge,
and it can't become huge from under us (khugepaged locking explained
above).
All we need is to enforce that there is no way anymore that in a code
path like below, pmd_trans_huge can be false, but pmd_none_or_clear_bad
can run into a hugepmd. The overhead of a barrier() is just a compiler
tweak and should not be measurable (I only added it for THP builds). I
don't exclude different compiler versions may have prevented the race
too by caching the value of *pmd on the stack (that hasn't been
verified, but it wouldn't be impossible considering
pmd_none_or_clear_bad, pmd_bad, pmd_trans_huge, pmd_none are all inlines
and there's no external function called in between pmd_trans_huge and
pmd_none_or_clear_bad).
if (pmd_trans_huge(*pmd)) {
if (next-addr != HPAGE_PMD_SIZE) {
VM_BUG_ON(!rwsem_is_locked(&tlb->mm->mmap_sem));
split_huge_page_pmd(vma->vm_mm, pmd);
} else if (zap_huge_pmd(tlb, vma, pmd, addr))
continue;
/* fall through */
}
if (pmd_none_or_clear_bad(pmd))
Because this race condition could be exercised without special
privileges this was reported in CVE-2012-1179.
The race was identified and fully explained by Ulrich who debugged it.
I'm quoting his accurate explanation below, for reference.
====== start quote =======
mapcount 0 page_mapcount 1
kernel BUG at mm/huge_memory.c:1384!
At some point prior to the panic, a "bad pmd ..." message similar to the
following is logged on the console:
mm/memory.c:145: bad pmd ffff8800376e1f98(80000000314000e7).
The "bad pmd ..." message is logged by pmd_clear_bad() before it clears
the page's PMD table entry.
143 void pmd_clear_bad(pmd_t *pmd)
144 {
-> 145 pmd_ERROR(*pmd);
146 pmd_clear(pmd);
147 }
After the PMD table entry has been cleared, there is an inconsistency
between the actual number of PMD table entries that are mapping the page
and the page's map count (_mapcount field in struct page). When the page
is subsequently reclaimed, __split_huge_page() detects this inconsistency.
1381 if (mapcount != page_mapcount(page))
1382 printk(KERN_ERR "mapcount %d page_mapcount %d\n",
1383 mapcount, page_mapcount(page));
-> 1384 BUG_ON(mapcount != page_mapcount(page));
The root cause of the problem is a race of two threads in a multithreaded
process. Thread B incurs a page fault on a virtual address that has never
been accessed (PMD entry is zero) while Thread A is executing an madvise()
system call on a virtual address within the same 2 MB (huge page) range.
virtual address space
.---------------------.
| |
| |
.-|---------------------|
| | |
| | |<-- B(fault)
| | |
2 MB | |/////////////////////|-.
huge < |/////////////////////| > A(range)
page | |/////////////////////|-'
| | |
| | |
'-|---------------------|
| |
| |
'---------------------'
- Thread A is executing an madvise(..., MADV_DONTNEED) system call
on the virtual address range "A(range)" shown in the picture.
sys_madvise
// Acquire the semaphore in shared mode.
down_read(¤t->mm->mmap_sem)
...
madvise_vma
switch (behavior)
case MADV_DONTNEED:
madvise_dontneed
zap_page_range
unmap_vmas
unmap_page_range
zap_pud_range
zap_pmd_range
//
// Assume that this huge page has never been accessed.
// I.e. content of the PMD entry is zero (not mapped).
//
if (pmd_trans_huge(*pmd)) {
// We don't get here due to the above assumption.
}
//
// Assume that Thread B incurred a page fault and
.---------> // sneaks in here as shown below.
| //
| if (pmd_none_or_clear_bad(pmd))
| {
| if (unlikely(pmd_bad(*pmd)))
| pmd_clear_bad
| {
| pmd_ERROR
| // Log "bad pmd ..." message here.
| pmd_clear
| // Clear the page's PMD entry.
| // Thread B incremented the map count
| // in page_add_new_anon_rmap(), but
| // now the page is no longer mapped
| // by a PMD entry (-> inconsistency).
| }
| }
|
v
- Thread B is handling a page fault on virtual address "B(fault)" shown
in the picture.
...
do_page_fault
__do_page_fault
// Acquire the semaphore in shared mode.
down_read_trylock(&mm->mmap_sem)
...
handle_mm_fault
if (pmd_none(*pmd) && transparent_hugepage_enabled(vma))
// We get here due to the above assumption (PMD entry is zero).
do_huge_pmd_anonymous_page
alloc_hugepage_vma
// Allocate a new transparent huge page here.
...
__do_huge_pmd_anonymous_page
...
spin_lock(&mm->page_table_lock)
...
page_add_new_anon_rmap
// Here we increment the page's map count (starts at -1).
atomic_set(&page->_mapcount, 0)
set_pmd_at
// Here we set the page's PMD entry which will be cleared
// when Thread A calls pmd_clear_bad().
...
spin_unlock(&mm->page_table_lock)
The mmap_sem does not prevent the race because both threads are acquiring
it in shared mode (down_read). Thread B holds the page_table_lock while
the page's map count and PMD table entry are updated. However, Thread A
does not synchronize on that lock.
====== end quote =======
[akpm@linux-foundation.org: checkpatch fixes]
Reported-by: Ulrich Obergfell <uobergfe@redhat.com>
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Hugh Dickins <hughd@google.com>
Cc: Dave Jones <davej@redhat.com>
Acked-by: Larry Woodman <lwoodman@redhat.com>
Acked-by: Rik van Riel <riel@redhat.com>
Cc: <stable@vger.kernel.org> [2.6.38+]
Cc: Mark Salter <msalter@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 03:33:42 +04:00
|
|
|
if (pmd_none_or_trans_huge_or_clear_bad(pmd))
|
2011-03-23 02:32:56 +03:00
|
|
|
goto again;
|
|
|
|
err = walk_pte_range(pmd, addr, next, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
} while (pmd++, addr = next, addr != end);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int walk_pud_range(pgd_t *pgd, unsigned long addr, unsigned long end,
|
2008-06-13 02:21:47 +04:00
|
|
|
struct mm_walk *walk)
|
2008-02-05 09:29:01 +03:00
|
|
|
{
|
|
|
|
pud_t *pud;
|
|
|
|
unsigned long next;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
pud = pud_offset(pgd, addr);
|
|
|
|
do {
|
|
|
|
next = pud_addr_end(addr, end);
|
|
|
|
if (pud_none_or_clear_bad(pud)) {
|
|
|
|
if (walk->pte_hole)
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk->pte_hole(addr, next, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (walk->pud_entry)
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk->pud_entry(pud, addr, next, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (!err && (walk->pmd_entry || walk->pte_entry))
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk_pmd_range(pud, addr, next, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
} while (pud++, addr = next, addr != end);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-04-07 01:35:04 +04:00
|
|
|
#ifdef CONFIG_HUGETLB_PAGE
|
|
|
|
static unsigned long hugetlb_entry_end(struct hstate *h, unsigned long addr,
|
|
|
|
unsigned long end)
|
|
|
|
{
|
|
|
|
unsigned long boundary = (addr & huge_page_mask(h)) + huge_page_size(h);
|
|
|
|
return boundary < end ? boundary : end;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int walk_hugetlb_range(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr, unsigned long end,
|
|
|
|
struct mm_walk *walk)
|
|
|
|
{
|
|
|
|
struct hstate *h = hstate_vma(vma);
|
|
|
|
unsigned long next;
|
|
|
|
unsigned long hmask = huge_page_mask(h);
|
|
|
|
pte_t *pte;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
next = hugetlb_entry_end(h, addr, end);
|
|
|
|
pte = huge_pte_offset(walk->mm, addr & hmask);
|
|
|
|
if (pte && walk->hugetlb_entry)
|
|
|
|
err = walk->hugetlb_entry(pte, hmask, addr, next, walk);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
} while (addr = next, addr != end);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-07-26 04:12:09 +04:00
|
|
|
|
|
|
|
#else /* CONFIG_HUGETLB_PAGE */
|
|
|
|
static int walk_hugetlb_range(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr, unsigned long end,
|
|
|
|
struct mm_walk *walk)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_HUGETLB_PAGE */
|
|
|
|
|
|
|
|
|
2010-04-07 01:35:04 +04:00
|
|
|
|
2008-02-05 09:29:01 +03:00
|
|
|
/**
|
|
|
|
* walk_page_range - walk a memory map's page tables with a callback
|
2008-03-20 03:00:40 +03:00
|
|
|
* @addr: starting address
|
|
|
|
* @end: ending address
|
|
|
|
* @walk: set of callbacks to invoke for each level of the tree
|
2008-02-05 09:29:01 +03:00
|
|
|
*
|
|
|
|
* Recursively walk the page table for the memory area in a VMA,
|
|
|
|
* calling supplied callbacks. Callbacks are called in-order (first
|
|
|
|
* PGD, first PUD, first PMD, first PTE, second PTE... second PMD,
|
|
|
|
* etc.). If lower-level callbacks are omitted, walking depth is reduced.
|
|
|
|
*
|
2008-06-13 02:21:47 +04:00
|
|
|
* Each callback receives an entry pointer and the start and end of the
|
|
|
|
* associated range, and a copy of the original mm_walk for access to
|
|
|
|
* the ->private or ->mm fields.
|
2008-02-05 09:29:01 +03:00
|
|
|
*
|
2011-07-26 04:12:11 +04:00
|
|
|
* Usually no locks are taken, but splitting transparent huge page may
|
|
|
|
* take page table lock. And the bottom level iterator will map PTE
|
2008-02-05 09:29:01 +03:00
|
|
|
* directories from highmem if necessary.
|
|
|
|
*
|
|
|
|
* If any callback returns a non-zero value, the walk is aborted and
|
|
|
|
* the return value is propagated back to the caller. Otherwise 0 is returned.
|
2011-07-26 04:12:10 +04:00
|
|
|
*
|
|
|
|
* walk->mm->mmap_sem must be held for at least read if walk->hugetlb_entry
|
|
|
|
* is !NULL.
|
2008-02-05 09:29:01 +03:00
|
|
|
*/
|
2008-06-13 02:21:47 +04:00
|
|
|
int walk_page_range(unsigned long addr, unsigned long end,
|
|
|
|
struct mm_walk *walk)
|
2008-02-05 09:29:01 +03:00
|
|
|
{
|
|
|
|
pgd_t *pgd;
|
|
|
|
unsigned long next;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (addr >= end)
|
|
|
|
return err;
|
|
|
|
|
2008-06-13 02:21:47 +04:00
|
|
|
if (!walk->mm)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-05-25 02:55:36 +04:00
|
|
|
VM_BUG_ON(!rwsem_is_locked(&walk->mm->mmap_sem));
|
|
|
|
|
2008-06-13 02:21:47 +04:00
|
|
|
pgd = pgd_offset(walk->mm, addr);
|
2008-02-05 09:29:01 +03:00
|
|
|
do {
|
2013-05-25 02:55:36 +04:00
|
|
|
struct vm_area_struct *vma = NULL;
|
2010-11-24 23:57:10 +03:00
|
|
|
|
2008-02-05 09:29:01 +03:00
|
|
|
next = pgd_addr_end(addr, end);
|
2009-12-15 04:59:59 +03:00
|
|
|
|
mm hugetlb: add hugepage support to pagemap
This patch enables extraction of the pfn of a hugepage from
/proc/pid/pagemap in an architecture independent manner.
Details
-------
My test program (leak_pagemap) works as follows:
- creat() and mmap() a file on hugetlbfs (file size is 200MB == 100 hugepages,)
- read()/write() something on it,
- call page-types with option -p,
- munmap() and unlink() the file on hugetlbfs
Without my patches
------------------
$ ./leak_pagemap
flags page-count MB symbolic-flags long-symbolic-flags
0x0000000000000000 1 0 __________________________________
0x0000000000000804 1 0 __R________M______________________ referenced,mmap
0x000000000000086c 81 0 __RU_lA____M______________________ referenced,uptodate,lru,active,mmap
0x0000000000005808 5 0 ___U_______Ma_b___________________ uptodate,mmap,anonymous,swapbacked
0x0000000000005868 12 0 ___U_lA____Ma_b___________________ uptodate,lru,active,mmap,anonymous,swapbacked
0x000000000000586c 1 0 __RU_lA____Ma_b___________________ referenced,uptodate,lru,active,mmap,anonymous,swapbacked
total 101 0
The output of page-types don't show any hugepage.
With my patches
---------------
$ ./leak_pagemap
flags page-count MB symbolic-flags long-symbolic-flags
0x0000000000000000 1 0 __________________________________
0x0000000000030000 51100 199 ________________TG________________ compound_tail,huge
0x0000000000028018 100 0 ___UD__________H_G________________ uptodate,dirty,compound_head,huge
0x0000000000000804 1 0 __R________M______________________ referenced,mmap
0x000000000000080c 1 0 __RU_______M______________________ referenced,uptodate,mmap
0x000000000000086c 80 0 __RU_lA____M______________________ referenced,uptodate,lru,active,mmap
0x0000000000005808 4 0 ___U_______Ma_b___________________ uptodate,mmap,anonymous,swapbacked
0x0000000000005868 12 0 ___U_lA____Ma_b___________________ uptodate,lru,active,mmap,anonymous,swapbacked
0x000000000000586c 1 0 __RU_lA____Ma_b___________________ referenced,uptodate,lru,active,mmap,anonymous,swapbacked
total 51300 200
The output of page-types shows 51200 pages contributing to hugepages,
containing 100 head pages and 51100 tail pages as expected.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: Andy Whitcroft <apw@canonical.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-12-15 05:00:01 +03:00
|
|
|
/*
|
2013-05-25 02:55:36 +04:00
|
|
|
* This function was not intended to be vma based.
|
|
|
|
* But there are vma special cases to be handled:
|
|
|
|
* - hugetlb vma's
|
|
|
|
* - VM_PFNMAP vma's
|
mm hugetlb: add hugepage support to pagemap
This patch enables extraction of the pfn of a hugepage from
/proc/pid/pagemap in an architecture independent manner.
Details
-------
My test program (leak_pagemap) works as follows:
- creat() and mmap() a file on hugetlbfs (file size is 200MB == 100 hugepages,)
- read()/write() something on it,
- call page-types with option -p,
- munmap() and unlink() the file on hugetlbfs
Without my patches
------------------
$ ./leak_pagemap
flags page-count MB symbolic-flags long-symbolic-flags
0x0000000000000000 1 0 __________________________________
0x0000000000000804 1 0 __R________M______________________ referenced,mmap
0x000000000000086c 81 0 __RU_lA____M______________________ referenced,uptodate,lru,active,mmap
0x0000000000005808 5 0 ___U_______Ma_b___________________ uptodate,mmap,anonymous,swapbacked
0x0000000000005868 12 0 ___U_lA____Ma_b___________________ uptodate,lru,active,mmap,anonymous,swapbacked
0x000000000000586c 1 0 __RU_lA____Ma_b___________________ referenced,uptodate,lru,active,mmap,anonymous,swapbacked
total 101 0
The output of page-types don't show any hugepage.
With my patches
---------------
$ ./leak_pagemap
flags page-count MB symbolic-flags long-symbolic-flags
0x0000000000000000 1 0 __________________________________
0x0000000000030000 51100 199 ________________TG________________ compound_tail,huge
0x0000000000028018 100 0 ___UD__________H_G________________ uptodate,dirty,compound_head,huge
0x0000000000000804 1 0 __R________M______________________ referenced,mmap
0x000000000000080c 1 0 __RU_______M______________________ referenced,uptodate,mmap
0x000000000000086c 80 0 __RU_lA____M______________________ referenced,uptodate,lru,active,mmap
0x0000000000005808 4 0 ___U_______Ma_b___________________ uptodate,mmap,anonymous,swapbacked
0x0000000000005868 12 0 ___U_lA____Ma_b___________________ uptodate,lru,active,mmap,anonymous,swapbacked
0x000000000000586c 1 0 __RU_lA____Ma_b___________________ referenced,uptodate,lru,active,mmap,anonymous,swapbacked
total 51300 200
The output of page-types shows 51200 pages contributing to hugepages,
containing 100 head pages and 51100 tail pages as expected.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: Andy Whitcroft <apw@canonical.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-12-15 05:00:01 +03:00
|
|
|
*/
|
2013-05-25 02:55:36 +04:00
|
|
|
vma = find_vma(walk->mm, addr);
|
2011-07-26 04:12:09 +04:00
|
|
|
if (vma) {
|
2013-05-25 02:55:36 +04:00
|
|
|
/*
|
|
|
|
* There are no page structures backing a VM_PFNMAP
|
|
|
|
* range, so do not allow split_huge_page_pmd().
|
|
|
|
*/
|
|
|
|
if ((vma->vm_start <= addr) &&
|
|
|
|
(vma->vm_flags & VM_PFNMAP)) {
|
2009-12-15 04:59:59 +03:00
|
|
|
next = vma->vm_end;
|
2013-05-25 02:55:36 +04:00
|
|
|
pgd = pgd_offset(walk->mm, next);
|
|
|
|
continue;
|
|
|
|
}
|
2010-04-07 01:35:04 +04:00
|
|
|
/*
|
2013-05-25 02:55:36 +04:00
|
|
|
* Handle hugetlb vma individually because pagetable
|
|
|
|
* walk for the hugetlb page is dependent on the
|
|
|
|
* architecture and we can't handled it in the same
|
|
|
|
* manner as non-huge pages.
|
2010-04-07 01:35:04 +04:00
|
|
|
*/
|
2013-05-25 02:55:36 +04:00
|
|
|
if (walk->hugetlb_entry && (vma->vm_start <= addr) &&
|
|
|
|
is_vm_hugetlb_page(vma)) {
|
|
|
|
if (vma->vm_end < next)
|
|
|
|
next = vma->vm_end;
|
|
|
|
/*
|
|
|
|
* Hugepage is very tightly coupled with vma,
|
|
|
|
* so walk through hugetlb entries within a
|
|
|
|
* given vma.
|
|
|
|
*/
|
|
|
|
err = walk_hugetlb_range(vma, addr, next, walk);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
pgd = pgd_offset(walk->mm, next);
|
|
|
|
continue;
|
|
|
|
}
|
2009-12-15 04:59:59 +03:00
|
|
|
}
|
2011-07-26 04:12:09 +04:00
|
|
|
|
2008-02-05 09:29:01 +03:00
|
|
|
if (pgd_none_or_clear_bad(pgd)) {
|
|
|
|
if (walk->pte_hole)
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk->pte_hole(addr, next, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (err)
|
|
|
|
break;
|
2009-12-15 04:59:59 +03:00
|
|
|
pgd++;
|
2008-02-05 09:29:01 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (walk->pgd_entry)
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk->pgd_entry(pgd, addr, next, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (!err &&
|
|
|
|
(walk->pud_entry || walk->pmd_entry || walk->pte_entry))
|
2008-06-13 02:21:47 +04:00
|
|
|
err = walk_pud_range(pgd, addr, next, walk);
|
2008-02-05 09:29:01 +03:00
|
|
|
if (err)
|
|
|
|
break;
|
2009-12-15 04:59:59 +03:00
|
|
|
pgd++;
|
|
|
|
} while (addr = next, addr != end);
|
2008-02-05 09:29:01 +03:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|