Commit 2dde02ab authored by Vineet Gupta's avatar Vineet Gupta

ARC: mm: support 3 levels of page tables

ARCv2 MMU is software walked and Linux implements 2 levels of paging: pgd/pte.
Forthcoming hw will have multiple levels, so this change preps mm code
for same. It is also fun to try multi levels even on soft-walked code to
ensure generic mm code is robust to handle.

overview
________

2 levels {pgd, pte} : pmd is folded but pmd_* macros are valid and operate on pgd
3 levels {pgd, pmd, pte}:
  - pud is folded and pud_* macros point to pgd
  - pmd_* macros operate on actual pmd

code changes
____________

1. #include <asm-generic/pgtable-nopud.h>

2. Define CONFIG_PGTABLE_LEVELS 3

3a. Define PMD_SHIFT, PMD_SIZE, PMD_MASK, pmd_t
3b. Define pmd_val() which actually deals with pmd
    (pmd_offset(), pmd_index() are provided by generic code)
3c. pmd_alloc_one()/pmd_free() also provided by generic code
    (pmd_populate/pmd_free already exist)

4. Define pud_none(), pud_bad() macros based on generic pud_val() which
   internally pertains to pgd now.
4b. define pud_populate() to just setup pgd
Acked-by: default avatarMike Rapoport <rppt@linux.ibm.com>
Signed-off-by: default avatarVineet Gupta <vgupta@kernel.org>
parent 9f3c76ae
...@@ -314,6 +314,10 @@ config ARC_HUGEPAGE_16M ...@@ -314,6 +314,10 @@ config ARC_HUGEPAGE_16M
endchoice endchoice
config PGTABLE_LEVELS
int "Number of Page table levels"
default 2
config ARC_COMPACT_IRQ_LEVELS config ARC_COMPACT_IRQ_LEVELS
depends on ISA_ARCOMPACT depends on ISA_ARCOMPACT
bool "Setup Timer IRQ as high Priority" bool "Setup Timer IRQ as high Priority"
......
...@@ -41,6 +41,17 @@ typedef struct { ...@@ -41,6 +41,17 @@ typedef struct {
#define pgd_val(x) ((x).pgd) #define pgd_val(x) ((x).pgd)
#define __pgd(x) ((pgd_t) { (x) }) #define __pgd(x) ((pgd_t) { (x) })
#if CONFIG_PGTABLE_LEVELS > 2
typedef struct {
unsigned long pmd;
} pmd_t;
#define pmd_val(x) ((x).pmd)
#define __pmd(x) ((pmd_t) { (x) })
#endif
typedef struct { typedef struct {
#ifdef CONFIG_ARC_HAS_PAE40 #ifdef CONFIG_ARC_HAS_PAE40
unsigned long long pte; unsigned long long pte;
......
...@@ -70,6 +70,17 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) ...@@ -70,6 +70,17 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
return ret; return ret;
} }
#if CONFIG_PGTABLE_LEVELS > 2
static inline void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmdp)
{
set_pud(pudp, __pud((unsigned long)pmdp));
}
#define __pmd_free_tlb(tlb, pmd, addr) pmd_free((tlb)->mm, pmd)
#endif
#define __pte_free_tlb(tlb, pte, addr) pte_free((tlb)->mm, pte) #define __pte_free_tlb(tlb, pte, addr) pte_free((tlb)->mm, pte)
#endif /* _ASM_ARC_PGALLOC_H */ #endif /* _ASM_ARC_PGALLOC_H */
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#ifndef _ASM_ARC_PGTABLE_LEVELS_H #ifndef _ASM_ARC_PGTABLE_LEVELS_H
#define _ASM_ARC_PGTABLE_LEVELS_H #define _ASM_ARC_PGTABLE_LEVELS_H
#if CONFIG_PGTABLE_LEVELS == 2
/* /*
* 2 level paging setup for software walked MMUv3 (ARC700) and MMUv4 (HS) * 2 level paging setup for software walked MMUv3 (ARC700) and MMUv4 (HS)
* *
...@@ -47,16 +49,38 @@ ...@@ -47,16 +49,38 @@
#endif #endif
#define PGDIR_SIZE BIT(PGDIR_SHIFT) /* vaddr span, not PDG sz */ #else /* CONFIG_PGTABLE_LEVELS != 2 */
#define PGDIR_MASK (~(PGDIR_SIZE - 1))
/*
* A default 3 level paging testing setup in software walked MMU
* MMUv4 (8K page): <4> : <7> : <8> : <13>
*/
#define PGDIR_SHIFT 28
#if CONFIG_PGTABLE_LEVELS > 2
#define PMD_SHIFT 21
#endif
#endif /* CONFIG_PGTABLE_LEVELS */
#define PGDIR_SIZE BIT(PGDIR_SHIFT)
#define PGDIR_MASK (~(PGDIR_SIZE - 1))
#define PTRS_PER_PGD BIT(32 - PGDIR_SHIFT) #define PTRS_PER_PGD BIT(32 - PGDIR_SHIFT)
#define PTRS_PER_PTE BIT(PGDIR_SHIFT - PAGE_SHIFT) #if CONFIG_PGTABLE_LEVELS > 2
#define PMD_SIZE BIT(PMD_SHIFT)
#define PMD_MASK (~(PMD_SIZE - 1))
#define PTRS_PER_PMD BIT(PGDIR_SHIFT - PMD_SHIFT)
#endif
#define PTRS_PER_PTE BIT(PMD_SHIFT - PAGE_SHIFT)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#if CONFIG_PGTABLE_LEVELS > 2
#include <asm-generic/pgtable-nopud.h>
#else
#include <asm-generic/pgtable-nopmd.h> #include <asm-generic/pgtable-nopmd.h>
#endif
/* /*
* 1st level paging: pgd * 1st level paging: pgd
...@@ -67,9 +91,35 @@ ...@@ -67,9 +91,35 @@
#define pgd_ERROR(e) \ #define pgd_ERROR(e) \
pr_crit("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) pr_crit("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
#if CONFIG_PGTABLE_LEVELS > 2
/* In 3 level paging, pud_* macros work on pgd */
#define pud_none(x) (!pud_val(x))
#define pud_bad(x) ((pud_val(x) & ~PAGE_MASK))
#define pud_present(x) (pud_val(x))
#define pud_clear(xp) do { pud_val(*(xp)) = 0; } while (0)
#define pud_pgtable(pud) ((pmd_t *)(pud_val(pud) & PAGE_MASK))
#define pud_page(pud) virt_to_page(pud_pgtable(pud))
#define set_pud(pudp, pud) (*(pudp) = pud)
/* /*
* Due to the strange way generic pgtable level folding works, in a 2 level * 2nd level paging: pmd
* setup, pmd_val() returns pgd, so these pmd_* macros actually work on pgd */
#define pmd_ERROR(e) \
pr_crit("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
#define pmd_pfn(pmd) ((pmd_val(pmd) & PMD_MASK) >> PAGE_SHIFT)
#define pfn_pmd(pfn,prot) __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
#define mk_pmd(page,prot) pfn_pmd(page_to_pfn(page),prot)
#endif
/*
* Due to the strange way generic pgtable level folding works, the pmd_* macros
* - are valid even for 2 levels (which supposedly only has pgd - pte)
* - behave differently for 2 vs. 3
* In 2 level paging (pgd -> pte), pmd_* macros work on pgd
* In 3+ level paging (pgd -> pmd -> pte), pmd_* macros work on pmd
*/ */
#define pmd_none(x) (!pmd_val(x)) #define pmd_none(x) (!pmd_val(x))
#define pmd_bad(x) ((pmd_val(x) & ~PAGE_MASK)) #define pmd_bad(x) ((pmd_val(x) & ~PAGE_MASK))
...@@ -80,6 +130,9 @@ ...@@ -80,6 +130,9 @@
#define set_pmd(pmdp, pmd) (*(pmdp) = pmd) #define set_pmd(pmdp, pmd) (*(pmdp) = pmd)
#define pmd_pgtable(pmd) ((pgtable_t) pmd_page_vaddr(pmd)) #define pmd_pgtable(pmd) ((pgtable_t) pmd_page_vaddr(pmd))
/*
* 3rd level paging: pte
*/
#define pte_ERROR(e) \ #define pte_ERROR(e) \
pr_crit("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) pr_crit("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
......
...@@ -93,7 +93,7 @@ extern unsigned int get_wchan(struct task_struct *p); ...@@ -93,7 +93,7 @@ extern unsigned int get_wchan(struct task_struct *p);
#define VMALLOC_START (PAGE_OFFSET - (CONFIG_ARC_KVADDR_SIZE << 20)) #define VMALLOC_START (PAGE_OFFSET - (CONFIG_ARC_KVADDR_SIZE << 20))
/* 1 PGDIR_SIZE each for fixmap/pkmap, 2 PGDIR_SIZE gutter (see asm/highmem.h) */ /* 1 PGDIR_SIZE each for fixmap/pkmap, 2 PGDIR_SIZE gutter (see asm/highmem.h) */
#define VMALLOC_SIZE ((CONFIG_ARC_KVADDR_SIZE << 20) - PGDIR_SIZE * 4) #define VMALLOC_SIZE ((CONFIG_ARC_KVADDR_SIZE << 20) - PMD_SIZE * 4)
#define VMALLOC_END (VMALLOC_START + VMALLOC_SIZE) #define VMALLOC_END (VMALLOC_START + VMALLOC_SIZE)
......
...@@ -39,6 +39,8 @@ noinline static int handle_kernel_vaddr_fault(unsigned long address) ...@@ -39,6 +39,8 @@ noinline static int handle_kernel_vaddr_fault(unsigned long address)
if (!pgd_present(*pgd_k)) if (!pgd_present(*pgd_k))
goto bad_area; goto bad_area;
set_pgd(pgd, *pgd_k);
p4d = p4d_offset(pgd, address); p4d = p4d_offset(pgd, address);
p4d_k = p4d_offset(pgd_k, address); p4d_k = p4d_offset(pgd_k, address);
if (!p4d_present(*p4d_k)) if (!p4d_present(*p4d_k))
...@@ -49,6 +51,8 @@ noinline static int handle_kernel_vaddr_fault(unsigned long address) ...@@ -49,6 +51,8 @@ noinline static int handle_kernel_vaddr_fault(unsigned long address)
if (!pud_present(*pud_k)) if (!pud_present(*pud_k))
goto bad_area; goto bad_area;
set_pud(pud, *pud_k);
pmd = pmd_offset(pud, address); pmd = pmd_offset(pud, address);
pmd_k = pmd_offset(pud_k, address); pmd_k = pmd_offset(pud_k, address);
if (!pmd_present(*pmd_k)) if (!pmd_present(*pmd_k))
......
...@@ -191,6 +191,7 @@ void __init mem_init(void) ...@@ -191,6 +191,7 @@ void __init mem_init(void)
highmem_init(); highmem_init();
BUILD_BUG_ON((PTRS_PER_PGD * sizeof(pgd_t)) > PAGE_SIZE); BUILD_BUG_ON((PTRS_PER_PGD * sizeof(pgd_t)) > PAGE_SIZE);
BUILD_BUG_ON((PTRS_PER_PMD * sizeof(pmd_t)) > PAGE_SIZE);
BUILD_BUG_ON((PTRS_PER_PTE * sizeof(pte_t)) > PAGE_SIZE); BUILD_BUG_ON((PTRS_PER_PTE * sizeof(pte_t)) > PAGE_SIZE);
} }
......
...@@ -621,8 +621,8 @@ char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len) ...@@ -621,8 +621,8 @@ char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len)
IS_USED_CFG(CONFIG_TRANSPARENT_HUGEPAGE)); IS_USED_CFG(CONFIG_TRANSPARENT_HUGEPAGE));
n += scnprintf(buf + n, len - n, n += scnprintf(buf + n, len - n,
"MMU [v%x]\t: %dk PAGE, %sJTLB %d (%dx%d), uDTLB %d, uITLB %d%s%s\n", "MMU [v%x]\t: %dk PAGE, %s, swalk %d lvl, JTLB %d (%dx%d), uDTLB %d, uITLB %d%s%s\n",
p_mmu->ver, p_mmu->pg_sz_k, super_pg, p_mmu->ver, p_mmu->pg_sz_k, super_pg, CONFIG_PGTABLE_LEVELS,
p_mmu->sets * p_mmu->ways, p_mmu->sets, p_mmu->ways, p_mmu->sets * p_mmu->ways, p_mmu->sets, p_mmu->ways,
p_mmu->u_dtlb, p_mmu->u_itlb, p_mmu->u_dtlb, p_mmu->u_itlb,
IS_AVAIL2(p_mmu->pae, ", PAE40 ", CONFIG_ARC_HAS_PAE40)); IS_AVAIL2(p_mmu->pae, ", PAE40 ", CONFIG_ARC_HAS_PAE40));
......
...@@ -173,6 +173,15 @@ ex_saved_reg1: ...@@ -173,6 +173,15 @@ ex_saved_reg1:
tst r3, r3 tst r3, r3
bz do_slow_path_pf ; if no Page Table, do page fault bz do_slow_path_pf ; if no Page Table, do page fault
#if CONFIG_PGTABLE_LEVELS > 2
lsr r0, r2, PMD_SHIFT ; Bits for indexing into PMD
and r0, r0, (PTRS_PER_PMD - 1)
ld.as r1, [r3, r0] ; PMD entry
tst r1, r1
bz do_slow_path_pf
mov r3, r1
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE #ifdef CONFIG_TRANSPARENT_HUGEPAGE
and.f 0, r3, _PAGE_HW_SZ ; Is this Huge PMD (thp) and.f 0, r3, _PAGE_HW_SZ ; Is this Huge PMD (thp)
add2.nz r1, r1, r0 add2.nz r1, r1, r0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment