|
@@ -21,6 +21,9 @@
|
|
|
#include <asm/pgalloc.h>
|
|
|
#include <asm/tlb.h>
|
|
|
#include <asm/setup.h>
|
|
|
+#include <asm/hugetlb.h>
|
|
|
+
|
|
|
+#ifdef CONFIG_HUGETLB_PAGE
|
|
|
|
|
|
#define PAGE_SHIFT_64K 16
|
|
|
#define PAGE_SHIFT_16M 24
|
|
@@ -100,66 +103,6 @@ int pgd_huge(pgd_t pgd)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-/*
|
|
|
- * We have 4 cases for pgds and pmds:
|
|
|
- * (1) invalid (all zeroes)
|
|
|
- * (2) pointer to next table, as normal; bottom 6 bits == 0
|
|
|
- * (3) leaf pte for huge page, bottom two bits != 00
|
|
|
- * (4) hugepd pointer, bottom two bits == 00, next 4 bits indicate size of table
|
|
|
- */
|
|
|
-pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift)
|
|
|
-{
|
|
|
- pgd_t *pg;
|
|
|
- pud_t *pu;
|
|
|
- pmd_t *pm;
|
|
|
- pte_t *ret_pte;
|
|
|
- hugepd_t *hpdp = NULL;
|
|
|
- unsigned pdshift = PGDIR_SHIFT;
|
|
|
-
|
|
|
- if (shift)
|
|
|
- *shift = 0;
|
|
|
-
|
|
|
- pg = pgdir + pgd_index(ea);
|
|
|
-
|
|
|
- if (pgd_huge(*pg)) {
|
|
|
- ret_pte = (pte_t *) pg;
|
|
|
- goto out;
|
|
|
- } else if (is_hugepd(pg))
|
|
|
- hpdp = (hugepd_t *)pg;
|
|
|
- else if (!pgd_none(*pg)) {
|
|
|
- pdshift = PUD_SHIFT;
|
|
|
- pu = pud_offset(pg, ea);
|
|
|
-
|
|
|
- if (pud_huge(*pu)) {
|
|
|
- ret_pte = (pte_t *) pu;
|
|
|
- goto out;
|
|
|
- } else if (is_hugepd(pu))
|
|
|
- hpdp = (hugepd_t *)pu;
|
|
|
- else if (!pud_none(*pu)) {
|
|
|
- pdshift = PMD_SHIFT;
|
|
|
- pm = pmd_offset(pu, ea);
|
|
|
-
|
|
|
- if (pmd_huge(*pm)) {
|
|
|
- ret_pte = (pte_t *) pm;
|
|
|
- goto out;
|
|
|
- } else if (is_hugepd(pm))
|
|
|
- hpdp = (hugepd_t *)pm;
|
|
|
- else if (!pmd_none(*pm))
|
|
|
- return pte_offset_kernel(pm, ea);
|
|
|
- }
|
|
|
- }
|
|
|
- if (!hpdp)
|
|
|
- return NULL;
|
|
|
-
|
|
|
- ret_pte = hugepte_offset(hpdp, ea, pdshift);
|
|
|
- pdshift = hugepd_shift(*hpdp);
|
|
|
-out:
|
|
|
- if (shift)
|
|
|
- *shift = pdshift;
|
|
|
- return ret_pte;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(find_linux_pte_or_hugepte);
|
|
|
-
|
|
|
pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
|
|
|
{
|
|
|
return find_linux_pte_or_hugepte(mm->pgd, addr, NULL);
|
|
@@ -753,69 +696,6 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address,
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long addr,
|
|
|
- unsigned long end, int write, struct page **pages, int *nr)
|
|
|
-{
|
|
|
- unsigned long mask;
|
|
|
- unsigned long pte_end;
|
|
|
- struct page *head, *page, *tail;
|
|
|
- pte_t pte;
|
|
|
- int refs;
|
|
|
-
|
|
|
- pte_end = (addr + sz) & ~(sz-1);
|
|
|
- if (pte_end < end)
|
|
|
- end = pte_end;
|
|
|
-
|
|
|
- pte = *ptep;
|
|
|
- mask = _PAGE_PRESENT | _PAGE_USER;
|
|
|
- if (write)
|
|
|
- mask |= _PAGE_RW;
|
|
|
-
|
|
|
- if ((pte_val(pte) & mask) != mask)
|
|
|
- return 0;
|
|
|
-
|
|
|
- /* hugepages are never "special" */
|
|
|
- VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
|
|
|
-
|
|
|
- refs = 0;
|
|
|
- head = pte_page(pte);
|
|
|
-
|
|
|
- page = head + ((addr & (sz-1)) >> PAGE_SHIFT);
|
|
|
- tail = page;
|
|
|
- do {
|
|
|
- VM_BUG_ON(compound_head(page) != head);
|
|
|
- pages[*nr] = page;
|
|
|
- (*nr)++;
|
|
|
- page++;
|
|
|
- refs++;
|
|
|
- } while (addr += PAGE_SIZE, addr != end);
|
|
|
-
|
|
|
- if (!page_cache_add_speculative(head, refs)) {
|
|
|
- *nr -= refs;
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- if (unlikely(pte_val(pte) != pte_val(*ptep))) {
|
|
|
- /* Could be optimized better */
|
|
|
- *nr -= refs;
|
|
|
- while (refs--)
|
|
|
- put_page(head);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Any tail page need their mapcount reference taken before we
|
|
|
- * return.
|
|
|
- */
|
|
|
- while (refs--) {
|
|
|
- if (PageTail(tail))
|
|
|
- get_huge_page_tail(tail);
|
|
|
- tail++;
|
|
|
- }
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
static unsigned long hugepte_addr_end(unsigned long addr, unsigned long end,
|
|
|
unsigned long sz)
|
|
|
{
|
|
@@ -1032,3 +912,128 @@ void flush_dcache_icache_hugepage(struct page *page)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+#endif /* CONFIG_HUGETLB_PAGE */
|
|
|
+
|
|
|
+/*
|
|
|
+ * We have 4 cases for pgds and pmds:
|
|
|
+ * (1) invalid (all zeroes)
|
|
|
+ * (2) pointer to next table, as normal; bottom 6 bits == 0
|
|
|
+ * (3) leaf pte for huge page, bottom two bits != 00
|
|
|
+ * (4) hugepd pointer, bottom two bits == 00, next 4 bits indicate size of table
|
|
|
+ */
|
|
|
+pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift)
|
|
|
+{
|
|
|
+ pgd_t *pg;
|
|
|
+ pud_t *pu;
|
|
|
+ pmd_t *pm;
|
|
|
+ pte_t *ret_pte;
|
|
|
+ hugepd_t *hpdp = NULL;
|
|
|
+ unsigned pdshift = PGDIR_SHIFT;
|
|
|
+
|
|
|
+ if (shift)
|
|
|
+ *shift = 0;
|
|
|
+
|
|
|
+ pg = pgdir + pgd_index(ea);
|
|
|
+
|
|
|
+ if (pgd_huge(*pg)) {
|
|
|
+ ret_pte = (pte_t *) pg;
|
|
|
+ goto out;
|
|
|
+ } else if (is_hugepd(pg))
|
|
|
+ hpdp = (hugepd_t *)pg;
|
|
|
+ else if (!pgd_none(*pg)) {
|
|
|
+ pdshift = PUD_SHIFT;
|
|
|
+ pu = pud_offset(pg, ea);
|
|
|
+
|
|
|
+ if (pud_huge(*pu)) {
|
|
|
+ ret_pte = (pte_t *) pu;
|
|
|
+ goto out;
|
|
|
+ } else if (is_hugepd(pu))
|
|
|
+ hpdp = (hugepd_t *)pu;
|
|
|
+ else if (!pud_none(*pu)) {
|
|
|
+ pdshift = PMD_SHIFT;
|
|
|
+ pm = pmd_offset(pu, ea);
|
|
|
+
|
|
|
+ if (pmd_huge(*pm)) {
|
|
|
+ ret_pte = (pte_t *) pm;
|
|
|
+ goto out;
|
|
|
+ } else if (is_hugepd(pm))
|
|
|
+ hpdp = (hugepd_t *)pm;
|
|
|
+ else if (!pmd_none(*pm))
|
|
|
+ return pte_offset_kernel(pm, ea);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!hpdp)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ ret_pte = hugepte_offset(hpdp, ea, pdshift);
|
|
|
+ pdshift = hugepd_shift(*hpdp);
|
|
|
+out:
|
|
|
+ if (shift)
|
|
|
+ *shift = pdshift;
|
|
|
+ return ret_pte;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(find_linux_pte_or_hugepte);
|
|
|
+
|
|
|
+int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long addr,
|
|
|
+ unsigned long end, int write, struct page **pages, int *nr)
|
|
|
+{
|
|
|
+ unsigned long mask;
|
|
|
+ unsigned long pte_end;
|
|
|
+ struct page *head, *page, *tail;
|
|
|
+ pte_t pte;
|
|
|
+ int refs;
|
|
|
+
|
|
|
+ pte_end = (addr + sz) & ~(sz-1);
|
|
|
+ if (pte_end < end)
|
|
|
+ end = pte_end;
|
|
|
+
|
|
|
+ pte = *ptep;
|
|
|
+ mask = _PAGE_PRESENT | _PAGE_USER;
|
|
|
+ if (write)
|
|
|
+ mask |= _PAGE_RW;
|
|
|
+
|
|
|
+ if ((pte_val(pte) & mask) != mask)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* hugepages are never "special" */
|
|
|
+ VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
|
|
|
+
|
|
|
+ refs = 0;
|
|
|
+ head = pte_page(pte);
|
|
|
+
|
|
|
+ page = head + ((addr & (sz-1)) >> PAGE_SHIFT);
|
|
|
+ tail = page;
|
|
|
+ do {
|
|
|
+ VM_BUG_ON(compound_head(page) != head);
|
|
|
+ pages[*nr] = page;
|
|
|
+ (*nr)++;
|
|
|
+ page++;
|
|
|
+ refs++;
|
|
|
+ } while (addr += PAGE_SIZE, addr != end);
|
|
|
+
|
|
|
+ if (!page_cache_add_speculative(head, refs)) {
|
|
|
+ *nr -= refs;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (unlikely(pte_val(pte) != pte_val(*ptep))) {
|
|
|
+ /* Could be optimized better */
|
|
|
+ *nr -= refs;
|
|
|
+ while (refs--)
|
|
|
+ put_page(head);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Any tail page need their mapcount reference taken before we
|
|
|
+ * return.
|
|
|
+ */
|
|
|
+ while (refs--) {
|
|
|
+ if (PageTail(tail))
|
|
|
+ get_huge_page_tail(tail);
|
|
|
+ tail++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|