powerpc/mm: Fix little-endian 4K hugetlb

When we switched to big endian page table, we never updated the hugepd
format such that it can work for both big endian and little endian
config. This patch series update hugepd format such that it is looked at
as __be64 value in big endian page table config.

This patch also switch hugepd_t.pd from signed long to unsigned long.
I did update the FSL hugepd_ok check to check for the top bit instead
of checking > 0.

Fixes: 5dc1ef858c ("powerpc/mm: Use big endian Linux page tables for book3s 64")
Cc: stable@vger.kernel.org # v4.7+
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
Aneesh Kumar K.V 2016-12-14 10:07:53 +05:30 committed by Michael Ellerman
parent ff8b85796d
commit 20717e1ff5
8 changed files with 44 additions and 25 deletions

View file

@ -36,12 +36,13 @@
#ifdef CONFIG_HUGETLB_PAGE #ifdef CONFIG_HUGETLB_PAGE
static inline int hash__hugepd_ok(hugepd_t hpd) static inline int hash__hugepd_ok(hugepd_t hpd)
{ {
unsigned long hpdval = hpd_val(hpd);
/* /*
* if it is not a pte and have hugepd shift mask * if it is not a pte and have hugepd shift mask
* set, then it is a hugepd directory pointer * set, then it is a hugepd directory pointer
*/ */
if (!(hpd.pd & _PAGE_PTE) && if (!(hpdval & _PAGE_PTE) &&
((hpd.pd & HUGEPD_SHIFT_MASK) != 0)) ((hpdval & HUGEPD_SHIFT_MASK) != 0))
return true; return true;
return false; return false;
} }

View file

@ -21,12 +21,12 @@ static inline pte_t *hugepd_page(hugepd_t hpd)
* We have only four bits to encode, MMU page size * We have only four bits to encode, MMU page size
*/ */
BUILD_BUG_ON((MMU_PAGE_COUNT - 1) > 0xf); BUILD_BUG_ON((MMU_PAGE_COUNT - 1) > 0xf);
return __va(hpd.pd & HUGEPD_ADDR_MASK); return __va(hpd_val(hpd) & HUGEPD_ADDR_MASK);
} }
static inline unsigned int hugepd_mmu_psize(hugepd_t hpd) static inline unsigned int hugepd_mmu_psize(hugepd_t hpd)
{ {
return (hpd.pd & HUGEPD_SHIFT_MASK) >> 2; return (hpd_val(hpd) & HUGEPD_SHIFT_MASK) >> 2;
} }
static inline unsigned int hugepd_shift(hugepd_t hpd) static inline unsigned int hugepd_shift(hugepd_t hpd)
@ -52,18 +52,20 @@ static inline pte_t *hugepd_page(hugepd_t hpd)
{ {
BUG_ON(!hugepd_ok(hpd)); BUG_ON(!hugepd_ok(hpd));
#ifdef CONFIG_PPC_8xx #ifdef CONFIG_PPC_8xx
return (pte_t *)__va(hpd.pd & ~(_PMD_PAGE_MASK | _PMD_PRESENT_MASK)); return (pte_t *)__va(hpd_val(hpd) &
~(_PMD_PAGE_MASK | _PMD_PRESENT_MASK));
#else #else
return (pte_t *)((hpd.pd & ~HUGEPD_SHIFT_MASK) | PD_HUGE); return (pte_t *)((hpd_val(hpd) &
~HUGEPD_SHIFT_MASK) | PD_HUGE);
#endif #endif
} }
static inline unsigned int hugepd_shift(hugepd_t hpd) static inline unsigned int hugepd_shift(hugepd_t hpd)
{ {
#ifdef CONFIG_PPC_8xx #ifdef CONFIG_PPC_8xx
return ((hpd.pd & _PMD_PAGE_MASK) >> 1) + 17; return ((hpd_val(hpd) & _PMD_PAGE_MASK) >> 1) + 17;
#else #else
return hpd.pd & HUGEPD_SHIFT_MASK; return hpd_val(hpd) & HUGEPD_SHIFT_MASK;
#endif #endif
} }

View file

@ -227,9 +227,10 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
static inline int hugepd_ok(hugepd_t hpd) static inline int hugepd_ok(hugepd_t hpd)
{ {
#ifdef CONFIG_PPC_8xx #ifdef CONFIG_PPC_8xx
return ((hpd.pd & 0x4) != 0); return ((hpd_val(hpd) & 0x4) != 0);
#else #else
return (hpd.pd > 0); /* We clear the top bit to indicate hugepd */
return ((hpd_val(hpd) & PD_HUGE) == 0);
#endif #endif
} }

View file

@ -294,15 +294,12 @@ extern long long virt_phys_offset;
#include <asm/pgtable-types.h> #include <asm/pgtable-types.h>
#endif #endif
typedef struct { signed long pd; } hugepd_t;
#ifndef CONFIG_HUGETLB_PAGE #ifndef CONFIG_HUGETLB_PAGE
#define is_hugepd(pdep) (0) #define is_hugepd(pdep) (0)
#define pgd_huge(pgd) (0) #define pgd_huge(pgd) (0)
#endif /* CONFIG_HUGETLB_PAGE */ #endif /* CONFIG_HUGETLB_PAGE */
#define __hugepd(x) ((hugepd_t) { (x) })
struct page; struct page;
extern void clear_user_page(void *page, unsigned long vaddr, struct page *pg); extern void clear_user_page(void *page, unsigned long vaddr, struct page *pg);
extern void copy_user_page(void *to, void *from, unsigned long vaddr, extern void copy_user_page(void *to, void *from, unsigned long vaddr,

View file

@ -104,4 +104,12 @@ static inline bool pmd_xchg(pmd_t *pmdp, pmd_t old, pmd_t new)
return pmd_raw(old) == prev; return pmd_raw(old) == prev;
} }
typedef struct { __be64 pdbe; } hugepd_t;
#define __hugepd(x) ((hugepd_t) { cpu_to_be64(x) })
static inline unsigned long hpd_val(hugepd_t x)
{
return be64_to_cpu(x.pdbe);
}
#endif /* _ASM_POWERPC_PGTABLE_BE_TYPES_H */ #endif /* _ASM_POWERPC_PGTABLE_BE_TYPES_H */

View file

@ -66,4 +66,11 @@ static inline bool pte_xchg(pte_t *ptep, pte_t old, pte_t new)
} }
#endif #endif
typedef struct { unsigned long pd; } hugepd_t;
#define __hugepd(x) ((hugepd_t) { (x) })
static inline unsigned long hpd_val(hugepd_t x)
{
return x.pd;
}
#endif /* _ASM_POWERPC_PGTABLE_TYPES_H */ #endif /* _ASM_POWERPC_PGTABLE_TYPES_H */

View file

@ -125,11 +125,14 @@ int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
int hugepd_ok(hugepd_t hpd) int hugepd_ok(hugepd_t hpd)
{ {
bool is_hugepd; bool is_hugepd;
unsigned long hpdval;
hpdval = hpd_val(hpd);
/* /*
* We should not find this format in page directory, warn otherwise. * We should not find this format in page directory, warn otherwise.
*/ */
is_hugepd = (((hpd.pd & 0x3) == 0x0) && ((hpd.pd & HUGEPD_SHIFT_MASK) != 0)); is_hugepd = (((hpdval & 0x3) == 0x0) && ((hpdval & HUGEPD_SHIFT_MASK) != 0));
WARN(is_hugepd, "Found wrong page directory format\n"); WARN(is_hugepd, "Found wrong page directory format\n");
return 0; return 0;
} }

View file

@ -53,7 +53,7 @@ static u64 gpage_freearray[MAX_NUMBER_GPAGES];
static unsigned nr_gpages; static unsigned nr_gpages;
#endif #endif
#define hugepd_none(hpd) ((hpd).pd == 0) #define hugepd_none(hpd) (hpd_val(hpd) == 0)
pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
{ {
@ -103,24 +103,24 @@ static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp,
for (i = 0; i < num_hugepd; i++, hpdp++) { for (i = 0; i < num_hugepd; i++, hpdp++) {
if (unlikely(!hugepd_none(*hpdp))) if (unlikely(!hugepd_none(*hpdp)))
break; break;
else else {
#ifdef CONFIG_PPC_BOOK3S_64 #ifdef CONFIG_PPC_BOOK3S_64
hpdp->pd = __pa(new) | *hpdp = __hugepd(__pa(new) |
(shift_to_mmu_psize(pshift) << 2); (shift_to_mmu_psize(pshift) << 2));
#elif defined(CONFIG_PPC_8xx) #elif defined(CONFIG_PPC_8xx)
hpdp->pd = __pa(new) | *hpdp = __hugepd(__pa(new) |
(pshift == PAGE_SHIFT_8M ? _PMD_PAGE_8M : (pshift == PAGE_SHIFT_8M ? _PMD_PAGE_8M :
_PMD_PAGE_512K) | _PMD_PAGE_512K) | _PMD_PRESENT);
_PMD_PRESENT;
#else #else
/* We use the old format for PPC_FSL_BOOK3E */ /* We use the old format for PPC_FSL_BOOK3E */
hpdp->pd = ((unsigned long)new & ~PD_HUGE) | pshift; *hpdp = __hugepd(((unsigned long)new & ~PD_HUGE) | pshift);
#endif #endif
}
} }
/* If we bailed from the for loop early, an error occurred, clean up */ /* If we bailed from the for loop early, an error occurred, clean up */
if (i < num_hugepd) { if (i < num_hugepd) {
for (i = i - 1 ; i >= 0; i--, hpdp--) for (i = i - 1 ; i >= 0; i--, hpdp--)
hpdp->pd = 0; *hpdp = __hugepd(0);
kmem_cache_free(cachep, new); kmem_cache_free(cachep, new);
} }
spin_unlock(&mm->page_table_lock); spin_unlock(&mm->page_table_lock);
@ -454,7 +454,7 @@ static void free_hugepd_range(struct mmu_gather *tlb, hugepd_t *hpdp, int pdshif
return; return;
for (i = 0; i < num_hugepd; i++, hpdp++) for (i = 0; i < num_hugepd; i++, hpdp++)
hpdp->pd = 0; *hpdp = __hugepd(0);
if (shift >= pdshift) if (shift >= pdshift)
hugepd_free(tlb, hugepte); hugepd_free(tlb, hugepte);