Commit 35476311 authored by Daniel Silsby's avatar Daniel Silsby Committed by Paul Burton

MIPS: Add partial 32-bit huge page support

 This adds initial support for huge pages to 32-bit MIPS systems.
Systems with extended addressing enabled (EVA,XPA,Alchemy/Netlogic)
are not yet supported.
 With huge pages enabled, this implementation will increase page table
memory overhead to match that of a 64-bit MIPS system. However, the
cache-friendliness of page table walks is not affected significantly.
Signed-off-by: default avatarDaniel Silsby <dansilsby@gmail.com>
Signed-off-by: default avatarPaul Cercueil <paul@crapouillou.net>
Signed-off-by: default avatarPaul Burton <paul.burton@mips.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: James Hogan <jhogan@kernel.org>
Cc: od@zcrc.me
Cc: linux-mips@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
parent 171543e7
...@@ -23,6 +23,24 @@ ...@@ -23,6 +23,24 @@
#include <asm/highmem.h> #include <asm/highmem.h>
#endif #endif
/*
* Regarding 32-bit MIPS huge page support (and the tradeoff it entails):
*
* We use the same huge page sizes as 64-bit MIPS. Assuming a 4KB page size,
* our 2-level table layout would normally have a PGD entry cover a contiguous
* 4MB virtual address region (pointing to a 4KB PTE page of 1,024 32-bit pte_t
* pointers, each pointing to a 4KB physical page). The problem is that 4MB,
* spanning both halves of a TLB EntryLo0,1 pair, requires 2MB hardware page
* support, not one of the standard supported sizes (1MB,4MB,16MB,...).
* To correct for this, when huge pages are enabled, we halve the number of
* pointers a PTE page holds, making its last half go to waste. Correspondingly,
* we double the number of PGD pages. Overall, page table memory overhead
* increases to match 64-bit MIPS, but PTE lookups remain CPU cache-friendly.
*
* NOTE: We don't yet support huge pages if extended-addressing is enabled
* (i.e. EVA, XPA, 36-bit Alchemy/Netlogic).
*/
extern int temp_tlb_entry; extern int temp_tlb_entry;
/* /*
...@@ -44,7 +62,12 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, ...@@ -44,7 +62,12 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
*/ */
/* PGDIR_SHIFT determines what a third-level page table entry can map */ /* PGDIR_SHIFT determines what a third-level page table entry can map */
#define PGDIR_SHIFT (2 * PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2) #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) && !defined(CONFIG_PHYS_ADDR_T_64BIT)
# define PGDIR_SHIFT (2 * PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2 - 1)
#else
# define PGDIR_SHIFT (2 * PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2)
#endif
#define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_SIZE (1UL << PGDIR_SHIFT)
#define PGDIR_MASK (~(PGDIR_SIZE-1)) #define PGDIR_MASK (~(PGDIR_SIZE-1))
...@@ -52,14 +75,23 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, ...@@ -52,14 +75,23 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
* Entries per page directory level: we use two-level, so * Entries per page directory level: we use two-level, so
* we don't really have any PUD/PMD directory physically. * we don't really have any PUD/PMD directory physically.
*/ */
#define __PGD_ORDER (32 - 3 * PAGE_SHIFT + PGD_T_LOG2 + PTE_T_LOG2) #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) && !defined(CONFIG_PHYS_ADDR_T_64BIT)
# define __PGD_ORDER (32 - 3 * PAGE_SHIFT + PGD_T_LOG2 + PTE_T_LOG2 + 1)
#else
# define __PGD_ORDER (32 - 3 * PAGE_SHIFT + PGD_T_LOG2 + PTE_T_LOG2)
#endif
#define PGD_ORDER (__PGD_ORDER >= 0 ? __PGD_ORDER : 0) #define PGD_ORDER (__PGD_ORDER >= 0 ? __PGD_ORDER : 0)
#define PUD_ORDER aieeee_attempt_to_allocate_pud #define PUD_ORDER aieeee_attempt_to_allocate_pud
#define PMD_ORDER 1 #define PMD_ORDER 1
#define PTE_ORDER 0 #define PTE_ORDER 0
#define PTRS_PER_PGD (USER_PTRS_PER_PGD * 2) #define PTRS_PER_PGD (USER_PTRS_PER_PGD * 2)
#define PTRS_PER_PTE ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t)) #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) && !defined(CONFIG_PHYS_ADDR_T_64BIT)
# define PTRS_PER_PTE ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t) / 2)
#else
# define PTRS_PER_PTE ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t))
#endif
#define USER_PTRS_PER_PGD (0x80000000UL/PGDIR_SIZE) #define USER_PTRS_PER_PGD (0x80000000UL/PGDIR_SIZE)
#define FIRST_USER_ADDRESS 0UL #define FIRST_USER_ADDRESS 0UL
...@@ -87,7 +119,7 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, ...@@ -87,7 +119,7 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
extern void load_pgd(unsigned long pg_dir); extern void load_pgd(unsigned long pg_dir);
extern pte_t invalid_pte_table[PAGE_SIZE/sizeof(pte_t)]; extern pte_t invalid_pte_table[PTRS_PER_PTE];
/* /*
* Empty pgd/pmd entries point to the invalid_pte_table. * Empty pgd/pmd entries point to the invalid_pte_table.
...@@ -97,7 +129,19 @@ static inline int pmd_none(pmd_t pmd) ...@@ -97,7 +129,19 @@ static inline int pmd_none(pmd_t pmd)
return pmd_val(pmd) == (unsigned long) invalid_pte_table; return pmd_val(pmd) == (unsigned long) invalid_pte_table;
} }
#define pmd_bad(pmd) (pmd_val(pmd) & ~PAGE_MASK) static inline int pmd_bad(pmd_t pmd)
{
#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
/* pmd_huge(pmd) but inline */
if (unlikely(pmd_val(pmd) & _PAGE_HUGE))
return 0;
#endif
if (unlikely(pmd_val(pmd) & ~PAGE_MASK))
return 1;
return 0;
}
static inline int pmd_present(pmd_t pmd) static inline int pmd_present(pmd_t pmd)
{ {
...@@ -146,6 +190,7 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot) ...@@ -146,6 +190,7 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot)
#else #else
#define pte_pfn(x) ((unsigned long)((x).pte >> _PFN_SHIFT)) #define pte_pfn(x) ((unsigned long)((x).pte >> _PFN_SHIFT))
#define pfn_pte(pfn, prot) __pte(((unsigned long long)(pfn) << _PFN_SHIFT) | pgprot_val(prot)) #define pfn_pte(pfn, prot) __pte(((unsigned long long)(pfn) << _PFN_SHIFT) | pgprot_val(prot))
#define pfn_pmd(pfn, prot) __pmd(((unsigned long long)(pfn) << _PFN_SHIFT) | pgprot_val(prot))
#endif #endif
#endif /* defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32) */ #endif /* defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32) */
...@@ -159,6 +204,7 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot) ...@@ -159,6 +204,7 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot)
#define pgd_offset_k(address) pgd_offset(&init_mm, address) #define pgd_offset_k(address) pgd_offset(&init_mm, address)
#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) #define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
#define pmd_index(address) (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
/* to find an entry in a page-table-directory */ /* to find an entry in a page-table-directory */
#define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr)) #define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr))
......
...@@ -110,7 +110,7 @@ enum pgtable_bits { ...@@ -110,7 +110,7 @@ enum pgtable_bits {
_PAGE_WRITE_SHIFT, _PAGE_WRITE_SHIFT,
_PAGE_ACCESSED_SHIFT, _PAGE_ACCESSED_SHIFT,
_PAGE_MODIFIED_SHIFT, _PAGE_MODIFIED_SHIFT,
#if defined(CONFIG_64BIT) && defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT)
_PAGE_HUGE_SHIFT, _PAGE_HUGE_SHIFT,
#endif #endif
...@@ -132,7 +132,7 @@ enum pgtable_bits { ...@@ -132,7 +132,7 @@ enum pgtable_bits {
#define _PAGE_WRITE (1 << _PAGE_WRITE_SHIFT) #define _PAGE_WRITE (1 << _PAGE_WRITE_SHIFT)
#define _PAGE_ACCESSED (1 << _PAGE_ACCESSED_SHIFT) #define _PAGE_ACCESSED (1 << _PAGE_ACCESSED_SHIFT)
#define _PAGE_MODIFIED (1 << _PAGE_MODIFIED_SHIFT) #define _PAGE_MODIFIED (1 << _PAGE_MODIFIED_SHIFT)
#if defined(CONFIG_64BIT) && defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT)
# define _PAGE_HUGE (1 << _PAGE_HUGE_SHIFT) # define _PAGE_HUGE (1 << _PAGE_HUGE_SHIFT)
#endif #endif
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <asm/fixmap.h> #include <asm/fixmap.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/tlbflush.h>
void pgd_init(unsigned long page) void pgd_init(unsigned long page)
{ {
...@@ -30,6 +31,25 @@ void pgd_init(unsigned long page) ...@@ -30,6 +31,25 @@ void pgd_init(unsigned long page)
} }
} }
#if defined(CONFIG_TRANSPARENT_HUGEPAGE)
pmd_t mk_pmd(struct page *page, pgprot_t prot)
{
pmd_t pmd;
pmd_val(pmd) = (page_to_pfn(page) << _PFN_SHIFT) | pgprot_val(prot);
return pmd;
}
void set_pmd_at(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp, pmd_t pmd)
{
*pmdp = pmd;
flush_tlb_all();
}
#endif /* defined(CONFIG_TRANSPARENT_HUGEPAGE) */
void __init pagetable_init(void) void __init pagetable_init(void)
{ {
unsigned long vaddr; unsigned long vaddr;
......
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