Merge branch 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-block.git] / mm / memory.c
index 14fc0b40f0bb6cf3ee50cfff8e7db865ad442cdd..235ba51b2fbf07ffeeeb6b70d6522b4b0addb3de 100644 (file)
 
 #include <linux/kernel_stat.h>
 #include <linux/mm.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/coredump.h>
+#include <linux/sched/numa_balancing.h>
+#include <linux/sched/task.h>
 #include <linux/hugetlb.h>
 #include <linux/mman.h>
 #include <linux/swap.h>
@@ -441,7 +445,7 @@ static inline void free_pmd_range(struct mmu_gather *tlb, pud_t *pud,
        mm_dec_nr_pmds(tlb->mm);
 }
 
-static inline void free_pud_range(struct mmu_gather *tlb, pgd_t *pgd,
+static inline void free_pud_range(struct mmu_gather *tlb, p4d_t *p4d,
                                unsigned long addr, unsigned long end,
                                unsigned long floor, unsigned long ceiling)
 {
@@ -450,7 +454,7 @@ static inline void free_pud_range(struct mmu_gather *tlb, pgd_t *pgd,
        unsigned long start;
 
        start = addr;
-       pud = pud_offset(pgd, addr);
+       pud = pud_offset(p4d, addr);
        do {
                next = pud_addr_end(addr, end);
                if (pud_none_or_clear_bad(pud))
@@ -458,6 +462,39 @@ static inline void free_pud_range(struct mmu_gather *tlb, pgd_t *pgd,
                free_pmd_range(tlb, pud, addr, next, floor, ceiling);
        } while (pud++, addr = next, addr != end);
 
+       start &= P4D_MASK;
+       if (start < floor)
+               return;
+       if (ceiling) {
+               ceiling &= P4D_MASK;
+               if (!ceiling)
+                       return;
+       }
+       if (end - 1 > ceiling - 1)
+               return;
+
+       pud = pud_offset(p4d, start);
+       p4d_clear(p4d);
+       pud_free_tlb(tlb, pud, start);
+}
+
+static inline void free_p4d_range(struct mmu_gather *tlb, pgd_t *pgd,
+                               unsigned long addr, unsigned long end,
+                               unsigned long floor, unsigned long ceiling)
+{
+       p4d_t *p4d;
+       unsigned long next;
+       unsigned long start;
+
+       start = addr;
+       p4d = p4d_offset(pgd, addr);
+       do {
+               next = p4d_addr_end(addr, end);
+               if (p4d_none_or_clear_bad(p4d))
+                       continue;
+               free_pud_range(tlb, p4d, addr, next, floor, ceiling);
+       } while (p4d++, addr = next, addr != end);
+
        start &= PGDIR_MASK;
        if (start < floor)
                return;
@@ -469,9 +506,9 @@ static inline void free_pud_range(struct mmu_gather *tlb, pgd_t *pgd,
        if (end - 1 > ceiling - 1)
                return;
 
-       pud = pud_offset(pgd, start);
+       p4d = p4d_offset(pgd, start);
        pgd_clear(pgd);
-       pud_free_tlb(tlb, pud, start);
+       p4d_free_tlb(tlb, p4d, start);
 }
 
 /*
@@ -535,7 +572,7 @@ void free_pgd_range(struct mmu_gather *tlb,
                next = pgd_addr_end(addr, end);
                if (pgd_none_or_clear_bad(pgd))
                        continue;
-               free_pud_range(tlb, pgd, addr, next, floor, ceiling);
+               free_p4d_range(tlb, pgd, addr, next, floor, ceiling);
        } while (pgd++, addr = next, addr != end);
 }
 
@@ -654,7 +691,8 @@ static void print_bad_pte(struct vm_area_struct *vma, unsigned long addr,
                          pte_t pte, struct page *page)
 {
        pgd_t *pgd = pgd_offset(vma->vm_mm, addr);
-       pud_t *pud = pud_offset(pgd, addr);
+       p4d_t *p4d = p4d_offset(pgd, addr);
+       pud_t *pud = pud_offset(p4d, addr);
        pmd_t *pmd = pmd_offset(pud, addr);
        struct address_space *mapping;
        pgoff_t index;
@@ -1019,16 +1057,16 @@ static inline int copy_pmd_range(struct mm_struct *dst_mm, struct mm_struct *src
 }
 
 static inline int copy_pud_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
-               pgd_t *dst_pgd, pgd_t *src_pgd, struct vm_area_struct *vma,
+               p4d_t *dst_p4d, p4d_t *src_p4d, struct vm_area_struct *vma,
                unsigned long addr, unsigned long end)
 {
        pud_t *src_pud, *dst_pud;
        unsigned long next;
 
-       dst_pud = pud_alloc(dst_mm, dst_pgd, addr);
+       dst_pud = pud_alloc(dst_mm, dst_p4d, addr);
        if (!dst_pud)
                return -ENOMEM;
-       src_pud = pud_offset(src_pgd, addr);
+       src_pud = pud_offset(src_p4d, addr);
        do {
                next = pud_addr_end(addr, end);
                if (pud_trans_huge(*src_pud) || pud_devmap(*src_pud)) {
@@ -1052,6 +1090,28 @@ static inline int copy_pud_range(struct mm_struct *dst_mm, struct mm_struct *src
        return 0;
 }
 
+static inline int copy_p4d_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+               pgd_t *dst_pgd, pgd_t *src_pgd, struct vm_area_struct *vma,
+               unsigned long addr, unsigned long end)
+{
+       p4d_t *src_p4d, *dst_p4d;
+       unsigned long next;
+
+       dst_p4d = p4d_alloc(dst_mm, dst_pgd, addr);
+       if (!dst_p4d)
+               return -ENOMEM;
+       src_p4d = p4d_offset(src_pgd, addr);
+       do {
+               next = p4d_addr_end(addr, end);
+               if (p4d_none_or_clear_bad(src_p4d))
+                       continue;
+               if (copy_pud_range(dst_mm, src_mm, dst_p4d, src_p4d,
+                                               vma, addr, next))
+                       return -ENOMEM;
+       } while (dst_p4d++, src_p4d++, addr = next, addr != end);
+       return 0;
+}
+
 int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                struct vm_area_struct *vma)
 {
@@ -1107,7 +1167,7 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                next = pgd_addr_end(addr, end);
                if (pgd_none_or_clear_bad(src_pgd))
                        continue;
-               if (unlikely(copy_pud_range(dst_mm, src_mm, dst_pgd, src_pgd,
+               if (unlikely(copy_p4d_range(dst_mm, src_mm, dst_pgd, src_pgd,
                                            vma, addr, next))) {
                        ret = -ENOMEM;
                        break;
@@ -1263,14 +1323,14 @@ next:
 }
 
 static inline unsigned long zap_pud_range(struct mmu_gather *tlb,
-                               struct vm_area_struct *vma, pgd_t *pgd,
+                               struct vm_area_struct *vma, p4d_t *p4d,
                                unsigned long addr, unsigned long end,
                                struct zap_details *details)
 {
        pud_t *pud;
        unsigned long next;
 
-       pud = pud_offset(pgd, addr);
+       pud = pud_offset(p4d, addr);
        do {
                next = pud_addr_end(addr, end);
                if (pud_trans_huge(*pud) || pud_devmap(*pud)) {
@@ -1291,6 +1351,25 @@ next:
        return addr;
 }
 
+static inline unsigned long zap_p4d_range(struct mmu_gather *tlb,
+                               struct vm_area_struct *vma, pgd_t *pgd,
+                               unsigned long addr, unsigned long end,
+                               struct zap_details *details)
+{
+       p4d_t *p4d;
+       unsigned long next;
+
+       p4d = p4d_offset(pgd, addr);
+       do {
+               next = p4d_addr_end(addr, end);
+               if (p4d_none_or_clear_bad(p4d))
+                       continue;
+               next = zap_pud_range(tlb, vma, p4d, addr, next, details);
+       } while (p4d++, addr = next, addr != end);
+
+       return addr;
+}
+
 void unmap_page_range(struct mmu_gather *tlb,
                             struct vm_area_struct *vma,
                             unsigned long addr, unsigned long end,
@@ -1306,7 +1385,7 @@ void unmap_page_range(struct mmu_gather *tlb,
                next = pgd_addr_end(addr, end);
                if (pgd_none_or_clear_bad(pgd))
                        continue;
-               next = zap_pud_range(tlb, vma, pgd, addr, next, details);
+               next = zap_p4d_range(tlb, vma, pgd, addr, next, details);
        } while (pgd++, addr = next, addr != end);
        tlb_end_vma(tlb, vma);
 }
@@ -1461,16 +1540,24 @@ EXPORT_SYMBOL_GPL(zap_vma_ptes);
 pte_t *__get_locked_pte(struct mm_struct *mm, unsigned long addr,
                        spinlock_t **ptl)
 {
-       pgd_t *pgd = pgd_offset(mm, addr);
-       pud_t *pud = pud_alloc(mm, pgd, addr);
-       if (pud) {
-               pmd_t *pmd = pmd_alloc(mm, pud, addr);
-               if (pmd) {
-                       VM_BUG_ON(pmd_trans_huge(*pmd));
-                       return pte_alloc_map_lock(mm, pmd, addr, ptl);
-               }
-       }
-       return NULL;
+       pgd_t *pgd;
+       p4d_t *p4d;
+       pud_t *pud;
+       pmd_t *pmd;
+
+       pgd = pgd_offset(mm, addr);
+       p4d = p4d_alloc(mm, pgd, addr);
+       if (!p4d)
+               return NULL;
+       pud = pud_alloc(mm, p4d, addr);
+       if (!pud)
+               return NULL;
+       pmd = pmd_alloc(mm, pud, addr);
+       if (!pmd)
+               return NULL;
+
+       VM_BUG_ON(pmd_trans_huge(*pmd));
+       return pte_alloc_map_lock(mm, pmd, addr, ptl);
 }
 
 /*
@@ -1736,7 +1823,7 @@ static inline int remap_pmd_range(struct mm_struct *mm, pud_t *pud,
        return 0;
 }
 
-static inline int remap_pud_range(struct mm_struct *mm, pgd_t *pgd,
+static inline int remap_pud_range(struct mm_struct *mm, p4d_t *p4d,
                        unsigned long addr, unsigned long end,
                        unsigned long pfn, pgprot_t prot)
 {
@@ -1744,7 +1831,7 @@ static inline int remap_pud_range(struct mm_struct *mm, pgd_t *pgd,
        unsigned long next;
 
        pfn -= addr >> PAGE_SHIFT;
-       pud = pud_alloc(mm, pgd, addr);
+       pud = pud_alloc(mm, p4d, addr);
        if (!pud)
                return -ENOMEM;
        do {
@@ -1756,6 +1843,26 @@ static inline int remap_pud_range(struct mm_struct *mm, pgd_t *pgd,
        return 0;
 }
 
+static inline int remap_p4d_range(struct mm_struct *mm, pgd_t *pgd,
+                       unsigned long addr, unsigned long end,
+                       unsigned long pfn, pgprot_t prot)
+{
+       p4d_t *p4d;
+       unsigned long next;
+
+       pfn -= addr >> PAGE_SHIFT;
+       p4d = p4d_alloc(mm, pgd, addr);
+       if (!p4d)
+               return -ENOMEM;
+       do {
+               next = p4d_addr_end(addr, end);
+               if (remap_pud_range(mm, p4d, addr, next,
+                               pfn + (addr >> PAGE_SHIFT), prot))
+                       return -ENOMEM;
+       } while (p4d++, addr = next, addr != end);
+       return 0;
+}
+
 /**
  * remap_pfn_range - remap kernel memory to userspace
  * @vma: user vma to map to
@@ -1812,7 +1919,7 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
        flush_cache_range(vma, addr, end);
        do {
                next = pgd_addr_end(addr, end);
-               err = remap_pud_range(mm, pgd, addr, next,
+               err = remap_p4d_range(mm, pgd, addr, next,
                                pfn + (addr >> PAGE_SHIFT), prot);
                if (err)
                        break;
@@ -1928,7 +2035,7 @@ static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud,
        return err;
 }
 
-static int apply_to_pud_range(struct mm_struct *mm, pgd_t *pgd,
+static int apply_to_pud_range(struct mm_struct *mm, p4d_t *p4d,
                                     unsigned long addr, unsigned long end,
                                     pte_fn_t fn, void *data)
 {
@@ -1936,7 +2043,7 @@ static int apply_to_pud_range(struct mm_struct *mm, pgd_t *pgd,
        unsigned long next;
        int err;
 
-       pud = pud_alloc(mm, pgd, addr);
+       pud = pud_alloc(mm, p4d, addr);
        if (!pud)
                return -ENOMEM;
        do {
@@ -1948,6 +2055,26 @@ static int apply_to_pud_range(struct mm_struct *mm, pgd_t *pgd,
        return err;
 }
 
+static int apply_to_p4d_range(struct mm_struct *mm, pgd_t *pgd,
+                                    unsigned long addr, unsigned long end,
+                                    pte_fn_t fn, void *data)
+{
+       p4d_t *p4d;
+       unsigned long next;
+       int err;
+
+       p4d = p4d_alloc(mm, pgd, addr);
+       if (!p4d)
+               return -ENOMEM;
+       do {
+               next = p4d_addr_end(addr, end);
+               err = apply_to_pud_range(mm, p4d, addr, next, fn, data);
+               if (err)
+                       break;
+       } while (p4d++, addr = next, addr != end);
+       return err;
+}
+
 /*
  * Scan a region of virtual memory, filling in page tables as necessary
  * and calling a provided function on each leaf page table.
@@ -1966,7 +2093,7 @@ int apply_to_page_range(struct mm_struct *mm, unsigned long addr,
        pgd = pgd_offset(mm, addr);
        do {
                next = pgd_addr_end(addr, end);
-               err = apply_to_pud_range(mm, pgd, addr, next, fn, data);
+               err = apply_to_p4d_range(mm, pgd, addr, next, fn, data);
                if (err)
                        break;
        } while (pgd++, addr = next, addr != end);
@@ -3649,11 +3776,15 @@ static int __handle_mm_fault(struct vm_area_struct *vma, unsigned long address,
        };
        struct mm_struct *mm = vma->vm_mm;
        pgd_t *pgd;
+       p4d_t *p4d;
        int ret;
 
        pgd = pgd_offset(mm, address);
+       p4d = p4d_alloc(mm, pgd, address);
+       if (!p4d)
+               return VM_FAULT_OOM;
 
-       vmf.pud = pud_alloc(mm, pgd, address);
+       vmf.pud = pud_alloc(mm, p4d, address);
        if (!vmf.pud)
                return VM_FAULT_OOM;
        if (pud_none(*vmf.pud) && transparent_hugepage_enabled(vma)) {
@@ -3775,12 +3906,35 @@ int handle_mm_fault(struct vm_area_struct *vma, unsigned long address,
 }
 EXPORT_SYMBOL_GPL(handle_mm_fault);
 
+#ifndef __PAGETABLE_P4D_FOLDED
+/*
+ * Allocate p4d page table.
+ * We've already handled the fast-path in-line.
+ */
+int __p4d_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
+{
+       p4d_t *new = p4d_alloc_one(mm, address);
+       if (!new)
+               return -ENOMEM;
+
+       smp_wmb(); /* See comment in __pte_alloc */
+
+       spin_lock(&mm->page_table_lock);
+       if (pgd_present(*pgd))          /* Another has populated it */
+               p4d_free(mm, new);
+       else
+               pgd_populate(mm, pgd, new);
+       spin_unlock(&mm->page_table_lock);
+       return 0;
+}
+#endif /* __PAGETABLE_P4D_FOLDED */
+
 #ifndef __PAGETABLE_PUD_FOLDED
 /*
  * Allocate page upper directory.
  * We've already handled the fast-path in-line.
  */
-int __pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
+int __pud_alloc(struct mm_struct *mm, p4d_t *p4d, unsigned long address)
 {
        pud_t *new = pud_alloc_one(mm, address);
        if (!new)
@@ -3789,10 +3943,17 @@ int __pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
        smp_wmb(); /* See comment in __pte_alloc */
 
        spin_lock(&mm->page_table_lock);
-       if (pgd_present(*pgd))          /* Another has populated it */
+#ifndef __ARCH_HAS_5LEVEL_HACK
+       if (p4d_present(*p4d))          /* Another has populated it */
                pud_free(mm, new);
        else
-               pgd_populate(mm, pgd, new);
+               p4d_populate(mm, p4d, new);
+#else
+       if (pgd_present(*p4d))          /* Another has populated it */
+               pud_free(mm, new);
+       else
+               pgd_populate(mm, p4d, new);
+#endif /* __ARCH_HAS_5LEVEL_HACK */
        spin_unlock(&mm->page_table_lock);
        return 0;
 }
@@ -3835,6 +3996,7 @@ static int __follow_pte_pmd(struct mm_struct *mm, unsigned long address,
                pte_t **ptepp, pmd_t **pmdpp, spinlock_t **ptlp)
 {
        pgd_t *pgd;
+       p4d_t *p4d;
        pud_t *pud;
        pmd_t *pmd;
        pte_t *ptep;
@@ -3843,7 +4005,11 @@ static int __follow_pte_pmd(struct mm_struct *mm, unsigned long address,
        if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
                goto out;
 
-       pud = pud_offset(pgd, address);
+       p4d = p4d_offset(pgd, address);
+       if (p4d_none(*p4d) || unlikely(p4d_bad(*p4d)))
+               goto out;
+
+       pud = pud_offset(p4d, address);
        if (pud_none(*pud) || unlikely(pud_bad(*pud)))
                goto out;