mm: hugetlb: fix hugepage memory leak in walk_page_range()
[linux-2.6.git] / mm / pagewalk.c
index d5878be..a286915 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/mm.h>
 #include <linux/highmem.h>
 #include <linux/sched.h>
+#include <linux/hugetlb.h>
 
 static int walk_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
                          struct mm_walk *walk)
@@ -107,6 +108,7 @@ int walk_page_range(unsigned long addr, unsigned long end,
        pgd_t *pgd;
        unsigned long next;
        int err = 0;
+       struct vm_area_struct *vma;
 
        if (addr >= end)
                return err;
@@ -117,11 +119,22 @@ int walk_page_range(unsigned long addr, unsigned long end,
        pgd = pgd_offset(walk->mm, addr);
        do {
                next = pgd_addr_end(addr, end);
+
+               /* skip hugetlb vma to avoid hugepage PMD being cleared
+                * in pmd_none_or_clear_bad(). */
+               vma = find_vma(walk->mm, addr);
+               if (vma && is_vm_hugetlb_page(vma)) {
+                       if (vma->vm_end < next)
+                               next = vma->vm_end;
+                       continue;
+               }
+
                if (pgd_none_or_clear_bad(pgd)) {
                        if (walk->pte_hole)
                                err = walk->pte_hole(addr, next, walk);
                        if (err)
                                break;
+                       pgd++;
                        continue;
                }
                if (walk->pgd_entry)
@@ -131,7 +144,8 @@ int walk_page_range(unsigned long addr, unsigned long end,
                        err = walk_pud_range(pgd, addr, next, walk);
                if (err)
                        break;
-       } while (pgd++, addr = next, addr != end);
+               pgd++;
+       } while (addr = next, addr != end);
 
        return err;
 }