Messages in this thread Patch in this message | | | From | Eric Lin <> | Subject | [PATCH 2/3] riscv/mm: Implement kmap() and kmap_atomic() | Date | Tue, 31 Mar 2020 17:32:40 +0800 |
| |
Both kmap() and kmap_atomic() help kernel to create temporary mappings from a highmem page.
Be aware that use kmap() might put calling function to sleep and it cannot use in interrupt context. kmap_atomic() is an atomic version of kmap() which can be used in interrupt context and it is faster than kmap() because it doesn't hold a lock.
Here we preserve some memory slots from fixmap region for kmap_atomic() and kmap() will use pkmap region.
Signed-off-by: Eric Lin <tesheng@andestech.com> Cc: Alan Kao <alankao@andestech.com> --- arch/riscv/include/asm/fixmap.h | 9 +++- arch/riscv/include/asm/highmem.h | 30 +++++++++++++ arch/riscv/include/asm/pgtable.h | 5 +++ arch/riscv/mm/Makefile | 1 + arch/riscv/mm/highmem.c | 74 ++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/mm/highmem.c
diff --git a/arch/riscv/include/asm/fixmap.h b/arch/riscv/include/asm/fixmap.h index 42d2c42f3cc9..8dedc2bf2917 100644 --- a/arch/riscv/include/asm/fixmap.h +++ b/arch/riscv/include/asm/fixmap.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * Copyright (C) 2020 Andes Technology Corporation */ #ifndef _ASM_RISCV_FIXMAP_H @@ -10,6 +11,7 @@ #include <linux/sizes.h> #include <asm/page.h> #include <asm/pgtable.h> +#include <asm/kmap_types.h> #ifdef CONFIG_MMU /* @@ -28,7 +30,12 @@ enum fixed_addresses { FIX_PTE, FIX_PMD, FIX_EARLYCON_MEM_BASE, - __end_of_fixed_addresses +#ifdef CONFIG_HIGHMEM + FIX_KMAP_RESERVED, + FIX_KMAP_BEGIN, + FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS), +#endif + __end_of_fixed_addresses, }; #define FIXMAP_PAGE_IO PAGE_KERNEL diff --git a/arch/riscv/include/asm/highmem.h b/arch/riscv/include/asm/highmem.h index 7fc79e58f607..ec7c83d55830 100644 --- a/arch/riscv/include/asm/highmem.h +++ b/arch/riscv/include/asm/highmem.h @@ -17,3 +17,33 @@ #define PKMAP_NR(virt) (((virt) - (PKMAP_BASE)) >> PAGE_SHIFT) #define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT)) #define kmap_prot PAGE_KERNEL + +static inline void flush_cache_kmaps(void) +{ + flush_cache_all(); +} + +/* Declarations for highmem.c */ +extern unsigned long highstart_pfn, highend_pfn; + +extern pte_t *pkmap_page_table; + +extern void *kmap_high(struct page *page); +extern void kunmap_high(struct page *page); + +extern void kmap_init(void); + +/* + * The following functions are already defined by <linux/highmem.h> + * when CONFIG_HIGHMEM is not set. + */ +#ifdef CONFIG_HIGHMEM +extern void *kmap(struct page *page); +extern void kunmap(struct page *page); +extern void *kmap_atomic(struct page *page); +extern void __kunmap_atomic(void *kvaddr); +extern void *kmap_atomic_pfn(unsigned long pfn); +extern struct page *kmap_atomic_to_page(void *ptr); +#endif + +#endif diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index d9a3769f1f4e..1a774d5a8bbc 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -200,6 +200,11 @@ static inline pgd_t *pgd_offset(const struct mm_struct *mm, unsigned long addr) /* Locate an entry in the kernel page global directory */ #define pgd_offset_k(addr) pgd_offset(&init_mm, (addr)) +#ifdef CONFIG_HIGHMEM +/* Locate an entry in the second-level page table */ +#define pmd_off_k(addr) pmd_offset((pud_t *)pgd_offset_k(addr), addr) +#endif + static inline struct page *pmd_page(pmd_t pmd) { return pfn_to_page(pmd_val(pmd) >> _PAGE_PFN_SHIFT); diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile index 50b7af58c566..6f9305afc632 100644 --- a/arch/riscv/mm/Makefile +++ b/arch/riscv/mm/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_SMP) += tlbflush.o endif obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o obj-$(CONFIG_KASAN) += kasan_init.o +obj-$(CONFIG_HIGHMEM) += highmem.o ifdef CONFIG_KASAN KASAN_SANITIZE_kasan_init.o := n diff --git a/arch/riscv/mm/highmem.c b/arch/riscv/mm/highmem.c new file mode 100644 index 000000000000..b01ebe34619e --- /dev/null +++ b/arch/riscv/mm/highmem.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2020 Andes Technology Corporation + */ + +#include <linux/export.h> +#include <linux/highmem.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/interrupt.h> +#include <asm/fixmap.h> +#include <asm/pgtable.h> +#include <asm/tlbflush.h> + +void *kmap(struct page *page) +{ + unsigned long vaddr; + + might_sleep(); + if (!PageHighMem(page)) + return page_address(page); + vaddr = (unsigned long)kmap_high(page); + return (void *)vaddr; +} +EXPORT_SYMBOL(kmap); + +void kunmap(struct page *page) +{ + BUG_ON(in_interrupt()); + if (!PageHighMem(page)) + return; + kunmap_high(page); +} +EXPORT_SYMBOL(kunmap); + +void *kmap_atomic(struct page *page) +{ + unsigned int idx; + unsigned long vaddr; + int type; + pte_t *ptep; + + preempt_disable(); + pagefault_disable(); + + if (!PageHighMem(page)) + return page_address(page); + + type = kmap_atomic_idx_push(); + + idx = type + KM_TYPE_NR * smp_processor_id(); + vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); + + ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr); + set_pte(ptep, mk_pte(page, kmap_prot)); + + return (void *)vaddr; +} +EXPORT_SYMBOL(kmap_atomic); + +void __kunmap_atomic(void *kvaddr) +{ + if (kvaddr >= (void *)FIXADDR_START && kvaddr < (void *)FIXADDR_TOP) { + unsigned long vaddr = (unsigned long)kvaddr; + pte_t *ptep; + + kmap_atomic_idx_pop(); + ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr); + set_pte(ptep, __pte(0)); + } + pagefault_enable(); + preempt_enable(); +} +EXPORT_SYMBOL(__kunmap_atomic); -- 2.17.0
| |