Merge tag 'powerpc-6.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[sfrench/cifs-2.6.git] / arch / powerpc / mm / book3s64 / pgtable.c
index 85c84e89e3eafcda18e9082f52384df33b605960..8f8a62d3ff4de4c95cfbb85473f7687207441f89 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/memremap.h>
 #include <linux/pkeys.h>
 #include <linux/debugfs.h>
+#include <linux/proc_fs.h>
 #include <misc/cxl-base.h>
 
 #include <asm/pgalloc.h>
@@ -64,11 +65,39 @@ int pmdp_set_access_flags(struct vm_area_struct *vma, unsigned long address,
        return changed;
 }
 
+int pudp_set_access_flags(struct vm_area_struct *vma, unsigned long address,
+                         pud_t *pudp, pud_t entry, int dirty)
+{
+       int changed;
+#ifdef CONFIG_DEBUG_VM
+       WARN_ON(!pud_devmap(*pudp));
+       assert_spin_locked(pud_lockptr(vma->vm_mm, pudp));
+#endif
+       changed = !pud_same(*(pudp), entry);
+       if (changed) {
+               /*
+                * We can use MMU_PAGE_1G here, because only radix
+                * path look at the psize.
+                */
+               __ptep_set_access_flags(vma, pudp_ptep(pudp),
+                                       pud_pte(entry), address, MMU_PAGE_1G);
+       }
+       return changed;
+}
+
+
 int pmdp_test_and_clear_young(struct vm_area_struct *vma,
                              unsigned long address, pmd_t *pmdp)
 {
        return __pmdp_test_and_clear_young(vma->vm_mm, address, pmdp);
 }
+
+int pudp_test_and_clear_young(struct vm_area_struct *vma,
+                             unsigned long address, pud_t *pudp)
+{
+       return __pudp_test_and_clear_young(vma->vm_mm, address, pudp);
+}
+
 /*
  * set a new huge pmd. We should not be called for updating
  * an existing pmd entry. That should go via pmd_hugepage_update.
@@ -90,6 +119,23 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr,
        return set_pte_at(mm, addr, pmdp_ptep(pmdp), pmd_pte(pmd));
 }
 
+void set_pud_at(struct mm_struct *mm, unsigned long addr,
+               pud_t *pudp, pud_t pud)
+{
+#ifdef CONFIG_DEBUG_VM
+       /*
+        * Make sure hardware valid bit is not set. We don't do
+        * tlb flush for this update.
+        */
+
+       WARN_ON(pte_hw_valid(pud_pte(*pudp)));
+       assert_spin_locked(pud_lockptr(mm, pudp));
+       WARN_ON(!(pud_large(pud)));
+#endif
+       trace_hugepage_set_pud(addr, pud_val(pud));
+       return set_pte_at(mm, addr, pudp_ptep(pudp), pud_pte(pud));
+}
+
 static void do_serialize(void *arg)
 {
        /* We've taken the IPI, so try to trim the mask while here */
@@ -147,11 +193,35 @@ pmd_t pmdp_huge_get_and_clear_full(struct vm_area_struct *vma,
        return pmd;
 }
 
+pud_t pudp_huge_get_and_clear_full(struct vm_area_struct *vma,
+                                  unsigned long addr, pud_t *pudp, int full)
+{
+       pud_t pud;
+
+       VM_BUG_ON(addr & ~HPAGE_PMD_MASK);
+       VM_BUG_ON((pud_present(*pudp) && !pud_devmap(*pudp)) ||
+                 !pud_present(*pudp));
+       pud = pudp_huge_get_and_clear(vma->vm_mm, addr, pudp);
+       /*
+        * if it not a fullmm flush, then we can possibly end up converting
+        * this PMD pte entry to a regular level 0 PTE by a parallel page fault.
+        * Make sure we flush the tlb in this case.
+        */
+       if (!full)
+               flush_pud_tlb_range(vma, addr, addr + HPAGE_PUD_SIZE);
+       return pud;
+}
+
 static pmd_t pmd_set_protbits(pmd_t pmd, pgprot_t pgprot)
 {
        return __pmd(pmd_val(pmd) | pgprot_val(pgprot));
 }
 
+static pud_t pud_set_protbits(pud_t pud, pgprot_t pgprot)
+{
+       return __pud(pud_val(pud) | pgprot_val(pgprot));
+}
+
 /*
  * At some point we should be able to get rid of
  * pmd_mkhuge() and mk_huge_pmd() when we update all the
@@ -166,6 +236,15 @@ pmd_t pfn_pmd(unsigned long pfn, pgprot_t pgprot)
        return __pmd_mkhuge(pmd_set_protbits(__pmd(pmdv), pgprot));
 }
 
+pud_t pfn_pud(unsigned long pfn, pgprot_t pgprot)
+{
+       unsigned long pudv;
+
+       pudv = (pfn << PAGE_SHIFT) & PTE_RPN_MASK;
+
+       return __pud_mkhuge(pud_set_protbits(__pud(pudv), pgprot));
+}
+
 pmd_t mk_pmd(struct page *page, pgprot_t pgprot)
 {
        return pfn_pmd(page_to_pfn(page), pgprot);
@@ -306,22 +385,22 @@ static pmd_t *get_pmd_from_cache(struct mm_struct *mm)
 static pmd_t *__alloc_for_pmdcache(struct mm_struct *mm)
 {
        void *ret = NULL;
-       struct page *page;
+       struct ptdesc *ptdesc;
        gfp_t gfp = GFP_KERNEL_ACCOUNT | __GFP_ZERO;
 
        if (mm == &init_mm)
                gfp &= ~__GFP_ACCOUNT;
-       page = alloc_page(gfp);
-       if (!page)
+       ptdesc = pagetable_alloc(gfp, 0);
+       if (!ptdesc)
                return NULL;
-       if (!pgtable_pmd_page_ctor(page)) {
-               __free_pages(page, 0);
+       if (!pagetable_pmd_ctor(ptdesc)) {
+               pagetable_free(ptdesc);
                return NULL;
        }
 
-       atomic_set(&page->pt_frag_refcount, 1);
+       atomic_set(&ptdesc->pt_frag_refcount, 1);
 
-       ret = page_address(page);
+       ret = ptdesc_address(ptdesc);
        /*
         * if we support only one fragment just return the
         * allocated page.
@@ -331,12 +410,12 @@ static pmd_t *__alloc_for_pmdcache(struct mm_struct *mm)
 
        spin_lock(&mm->page_table_lock);
        /*
-        * If we find pgtable_page set, we return
+        * If we find ptdesc_page set, we return
         * the allocated page with single fragment
         * count.
         */
        if (likely(!mm->context.pmd_frag)) {
-               atomic_set(&page->pt_frag_refcount, PMD_FRAG_NR);
+               atomic_set(&ptdesc->pt_frag_refcount, PMD_FRAG_NR);
                mm->context.pmd_frag = ret + PMD_FRAG_SIZE;
        }
        spin_unlock(&mm->page_table_lock);
@@ -357,15 +436,15 @@ pmd_t *pmd_fragment_alloc(struct mm_struct *mm, unsigned long vmaddr)
 
 void pmd_fragment_free(unsigned long *pmd)
 {
-       struct page *page = virt_to_page(pmd);
+       struct ptdesc *ptdesc = virt_to_ptdesc(pmd);
 
-       if (PageReserved(page))
-               return free_reserved_page(page);
+       if (pagetable_is_reserved(ptdesc))
+               return free_reserved_ptdesc(ptdesc);
 
-       BUG_ON(atomic_read(&page->pt_frag_refcount) <= 0);
-       if (atomic_dec_and_test(&page->pt_frag_refcount)) {
-               pgtable_pmd_page_dtor(page);
-               __free_page(page);
+       BUG_ON(atomic_read(&ptdesc->pt_frag_refcount) <= 0);
+       if (atomic_dec_and_test(&ptdesc->pt_frag_refcount)) {
+               pagetable_pmd_dtor(ptdesc);
+               pagetable_free(ptdesc);
        }
 }