From 736d2169338a50c8814efc186b5423aee43b0c68 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sun, 22 Nov 2015 00:07:06 +0100 Subject: parisc: Add Huge Page and HUGETLBFS support This patch adds huge page support to allow userspace to allocate huge pages and to use hugetlbfs filesystem on 32- and 64-bit Linux kernels. A later patch will add kernel support to map kernel text and data on huge pages. The only requirement is, that the kernel needs to be compiled for a PA8X00 CPU (PA2.0 architecture). Older PA1.X CPUs do not support variable page sizes. 64bit Kernels are compiled for PA2.0 by default. Technically on parisc multiple physical huge pages may be needed to emulate standard 2MB huge pages. Signed-off-by: Helge Deller --- arch/parisc/include/asm/hugetlb.h | 85 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 arch/parisc/include/asm/hugetlb.h (limited to 'arch/parisc/include/asm/hugetlb.h') diff --git a/arch/parisc/include/asm/hugetlb.h b/arch/parisc/include/asm/hugetlb.h new file mode 100644 index 000000000000..7d56a9ccb752 --- /dev/null +++ b/arch/parisc/include/asm/hugetlb.h @@ -0,0 +1,85 @@ +#ifndef _ASM_PARISC64_HUGETLB_H +#define _ASM_PARISC64_HUGETLB_H + +#include +#include + + +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte); + +pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, + pte_t *ptep); + +static inline int is_hugepage_only_range(struct mm_struct *mm, + unsigned long addr, + unsigned long len) { + return 0; +} + +/* + * If the arch doesn't supply something else, assume that hugepage + * size aligned regions are ok without further preparation. + */ +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) +{ + if (len & ~HPAGE_MASK) + return -EINVAL; + if (addr & ~HPAGE_MASK) + return -EINVAL; + return 0; +} + +static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, + unsigned long addr, unsigned long end, + unsigned long floor, + unsigned long ceiling) +{ + free_pgd_range(tlb, addr, end, floor, ceiling); +} + +static inline void huge_ptep_clear_flush(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ +} + +static inline int huge_pte_none(pte_t pte) +{ + return pte_none(pte); +} + +static inline pte_t huge_pte_wrprotect(pte_t pte) +{ + return pte_wrprotect(pte); +} + +static inline void huge_ptep_set_wrprotect(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + pte_t old_pte = *ptep; + set_huge_pte_at(mm, addr, ptep, pte_wrprotect(old_pte)); +} + +static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + pte_t pte, int dirty) +{ + int changed = !pte_same(*ptep, pte); + if (changed) { + set_huge_pte_at(vma->vm_mm, addr, ptep, pte); + flush_tlb_page(vma, addr); + } + return changed; +} + +static inline pte_t huge_ptep_get(pte_t *ptep) +{ + return *ptep; +} + +static inline void arch_clear_hugepage_flags(struct page *page) +{ +} + +#endif /* _ASM_PARISC64_HUGETLB_H */ -- cgit v1.2.3 From b0e551313ebde17764f3a5ed273df524d1e7e690 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 26 Nov 2015 21:14:02 +0100 Subject: parisc: Protect huge page pte changes with spinlocks PA-RISC doesn't have atomic instructions to modify page table entries, so it takes spinlock in the TLB handler and modifies the page table entry non-atomically. If you modify the page table entry without the spinlock, you may race with TLB handler on another CPU and your modification may be lost. Protect against that with usage of purge_tlb_start() and purge_tlb_end() which handles the TLB spinlock. Signed-off-by: Helge Deller Cc: stable@vger.kernel.org # v4.4 --- arch/parisc/include/asm/hugetlb.h | 20 +++---------- arch/parisc/mm/hugetlbpage.c | 60 +++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 28 deletions(-) (limited to 'arch/parisc/include/asm/hugetlb.h') diff --git a/arch/parisc/include/asm/hugetlb.h b/arch/parisc/include/asm/hugetlb.h index 7d56a9ccb752..a65d888716c4 100644 --- a/arch/parisc/include/asm/hugetlb.h +++ b/arch/parisc/include/asm/hugetlb.h @@ -54,24 +54,12 @@ static inline pte_t huge_pte_wrprotect(pte_t pte) return pte_wrprotect(pte); } -static inline void huge_ptep_set_wrprotect(struct mm_struct *mm, - unsigned long addr, pte_t *ptep) -{ - pte_t old_pte = *ptep; - set_huge_pte_at(mm, addr, ptep, pte_wrprotect(old_pte)); -} +void huge_ptep_set_wrprotect(struct mm_struct *mm, + unsigned long addr, pte_t *ptep); -static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, +int huge_ptep_set_access_flags(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, - pte_t pte, int dirty) -{ - int changed = !pte_same(*ptep, pte); - if (changed) { - set_huge_pte_at(vma->vm_mm, addr, ptep, pte); - flush_tlb_page(vma, addr); - } - return changed; -} + pte_t pte, int dirty); static inline pte_t huge_ptep_get(pte_t *ptep) { diff --git a/arch/parisc/mm/hugetlbpage.c b/arch/parisc/mm/hugetlbpage.c index f6fdc77a72bd..54ba39262b82 100644 --- a/arch/parisc/mm/hugetlbpage.c +++ b/arch/parisc/mm/hugetlbpage.c @@ -105,15 +105,13 @@ static inline void purge_tlb_entries_huge(struct mm_struct *mm, unsigned long ad addr |= _HUGE_PAGE_SIZE_ENCODING_DEFAULT; for (i = 0; i < (1 << (HPAGE_SHIFT-REAL_HPAGE_SHIFT)); i++) { - mtsp(mm->context, 1); - pdtlb(addr); - if (unlikely(split_tlb)) - pitlb(addr); + purge_tlb_entries(mm, addr); addr += (1UL << REAL_HPAGE_SHIFT); } } -void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, +/* __set_huge_pte_at() must be called holding the pa_tlb_lock. */ +static void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t entry) { unsigned long addr_start; @@ -123,14 +121,9 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, addr_start = addr; for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { - /* Directly write pte entry. We could call set_pte_at(mm, addr, ptep, entry) - * instead, but then we get double locking on pa_tlb_lock. */ - *ptep = entry; + set_pte(ptep, entry); ptep++; - /* Drop the PAGE_SIZE/non-huge tlb entry */ - purge_tlb_entries(mm, addr); - addr += PAGE_SIZE; pte_val(entry) += PAGE_SIZE; } @@ -138,18 +131,61 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, purge_tlb_entries_huge(mm, addr_start); } +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t entry) +{ + unsigned long flags; + + purge_tlb_start(flags); + __set_huge_pte_at(mm, addr, ptep, entry); + purge_tlb_end(flags); +} + pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { + unsigned long flags; pte_t entry; + purge_tlb_start(flags); entry = *ptep; - set_huge_pte_at(mm, addr, ptep, __pte(0)); + __set_huge_pte_at(mm, addr, ptep, __pte(0)); + purge_tlb_end(flags); return entry; } + +void huge_ptep_set_wrprotect(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + unsigned long flags; + pte_t old_pte; + + purge_tlb_start(flags); + old_pte = *ptep; + __set_huge_pte_at(mm, addr, ptep, pte_wrprotect(old_pte)); + purge_tlb_end(flags); +} + +int huge_ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + pte_t pte, int dirty) +{ + unsigned long flags; + int changed; + + purge_tlb_start(flags); + changed = !pte_same(*ptep, pte); + if (changed) { + __set_huge_pte_at(vma->vm_mm, addr, ptep, pte); + } + purge_tlb_end(flags); + return changed; +} + + int pmd_huge(pmd_t pmd) { return 0; -- cgit v1.2.3