diff options
Diffstat (limited to '')
-rw-r--r-- | debian/patches-rt/0045-highmem-High-implementation-details-and-document-API.patch | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/debian/patches-rt/0045-highmem-High-implementation-details-and-document-API.patch b/debian/patches-rt/0045-highmem-High-implementation-details-and-document-API.patch new file mode 100644 index 000000000..723dd45e7 --- /dev/null +++ b/debian/patches-rt/0045-highmem-High-implementation-details-and-document-API.patch @@ -0,0 +1,544 @@ +From e9f16e3d48ba9b1d37ae050de3e84e147133b84c Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner <tglx@linutronix.de> +Date: Tue, 3 Nov 2020 10:27:34 +0100 +Subject: [PATCH 045/323] highmem: High implementation details and document API +Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.10/older/patches-5.10.204-rt100.tar.xz + +Move the gory details of kmap & al into a private header and only document +the interfaces which are usable by drivers. + +Signed-off-by: Thomas Gleixner <tglx@linutronix.de> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + include/linux/highmem-internal.h | 174 ++++++++++++++++++++ + include/linux/highmem.h | 266 +++++++++++-------------------- + mm/highmem.c | 11 +- + 3 files changed, 274 insertions(+), 177 deletions(-) + create mode 100644 include/linux/highmem-internal.h + +diff --git a/include/linux/highmem-internal.h b/include/linux/highmem-internal.h +new file mode 100644 +index 000000000000..6ceed907b14e +--- /dev/null ++++ b/include/linux/highmem-internal.h +@@ -0,0 +1,174 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef _LINUX_HIGHMEM_INTERNAL_H ++#define _LINUX_HIGHMEM_INTERNAL_H ++ ++/* ++ * Outside of CONFIG_HIGHMEM to support X86 32bit iomap_atomic() cruft. ++ */ ++#ifdef CONFIG_KMAP_LOCAL ++void *__kmap_local_pfn_prot(unsigned long pfn, pgprot_t prot); ++void *__kmap_local_page_prot(struct page *page, pgprot_t prot); ++void kunmap_local_indexed(void *vaddr); ++#endif ++ ++#ifdef CONFIG_HIGHMEM ++#include <asm/highmem.h> ++ ++#ifndef ARCH_HAS_KMAP_FLUSH_TLB ++static inline void kmap_flush_tlb(unsigned long addr) { } ++#endif ++ ++#ifndef kmap_prot ++#define kmap_prot PAGE_KERNEL ++#endif ++ ++void *kmap_high(struct page *page); ++void kunmap_high(struct page *page); ++void __kmap_flush_unused(void); ++struct page *__kmap_to_page(void *addr); ++ ++static inline void *kmap(struct page *page) ++{ ++ void *addr; ++ ++ might_sleep(); ++ if (!PageHighMem(page)) ++ addr = page_address(page); ++ else ++ addr = kmap_high(page); ++ kmap_flush_tlb((unsigned long)addr); ++ return addr; ++} ++ ++static inline void kunmap(struct page *page) ++{ ++ might_sleep(); ++ if (!PageHighMem(page)) ++ return; ++ kunmap_high(page); ++} ++ ++static inline struct page *kmap_to_page(void *addr) ++{ ++ return __kmap_to_page(addr); ++} ++ ++static inline void kmap_flush_unused(void) ++{ ++ __kmap_flush_unused(); ++} ++ ++static inline void *kmap_atomic_prot(struct page *page, pgprot_t prot) ++{ ++ preempt_disable(); ++ pagefault_disable(); ++ return __kmap_local_page_prot(page, prot); ++} ++ ++static inline void *kmap_atomic(struct page *page) ++{ ++ return kmap_atomic_prot(page, kmap_prot); ++} ++ ++static inline void *kmap_atomic_pfn(unsigned long pfn) ++{ ++ preempt_disable(); ++ pagefault_disable(); ++ return __kmap_local_pfn_prot(pfn, kmap_prot); ++} ++ ++static inline void __kunmap_atomic(void *addr) ++{ ++ kunmap_local_indexed(addr); ++ pagefault_enable(); ++ preempt_enable(); ++} ++ ++unsigned int __nr_free_highpages(void); ++extern atomic_long_t _totalhigh_pages; ++ ++static inline unsigned int nr_free_highpages(void) ++{ ++ return __nr_free_highpages(); ++} ++ ++static inline unsigned long totalhigh_pages(void) ++{ ++ return (unsigned long)atomic_long_read(&_totalhigh_pages); ++} ++ ++static inline void totalhigh_pages_inc(void) ++{ ++ atomic_long_inc(&_totalhigh_pages); ++} ++ ++static inline void totalhigh_pages_add(long count) ++{ ++ atomic_long_add(count, &_totalhigh_pages); ++} ++ ++#else /* CONFIG_HIGHMEM */ ++ ++static inline struct page *kmap_to_page(void *addr) ++{ ++ return virt_to_page(addr); ++} ++ ++static inline void *kmap(struct page *page) ++{ ++ might_sleep(); ++ return page_address(page); ++} ++ ++static inline void kunmap_high(struct page *page) { } ++static inline void kmap_flush_unused(void) { } ++ ++static inline void kunmap(struct page *page) ++{ ++#ifdef ARCH_HAS_FLUSH_ON_KUNMAP ++ kunmap_flush_on_unmap(page_address(page)); ++#endif ++} ++ ++static inline void *kmap_atomic(struct page *page) ++{ ++ preempt_disable(); ++ pagefault_disable(); ++ return page_address(page); ++} ++ ++static inline void *kmap_atomic_prot(struct page *page, pgprot_t prot) ++{ ++ return kmap_atomic(page); ++} ++ ++static inline void *kmap_atomic_pfn(unsigned long pfn) ++{ ++ return kmap_atomic(pfn_to_page(pfn)); ++} ++ ++static inline void __kunmap_atomic(void *addr) ++{ ++#ifdef ARCH_HAS_FLUSH_ON_KUNMAP ++ kunmap_flush_on_unmap(addr); ++#endif ++ pagefault_enable(); ++ preempt_enable(); ++} ++ ++static inline unsigned int nr_free_highpages(void) { return 0; } ++static inline unsigned long totalhigh_pages(void) { return 0UL; } ++ ++#endif /* CONFIG_HIGHMEM */ ++ ++/* ++ * Prevent people trying to call kunmap_atomic() as if it were kunmap() ++ * kunmap_atomic() should get the return value of kmap_atomic, not the page. ++ */ ++#define kunmap_atomic(__addr) \ ++do { \ ++ BUILD_BUG_ON(__same_type((__addr), struct page *)); \ ++ __kunmap_atomic(__addr); \ ++} while (0) ++ ++#endif +diff --git a/include/linux/highmem.h b/include/linux/highmem.h +index e632774cce87..5c888525b4c5 100644 +--- a/include/linux/highmem.h ++++ b/include/linux/highmem.h +@@ -11,199 +11,125 @@ + + #include <asm/cacheflush.h> + +-#ifndef ARCH_HAS_FLUSH_ANON_PAGE +-static inline void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr) +-{ +-} +-#endif +- +-#ifndef ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE +-static inline void flush_kernel_dcache_page(struct page *page) +-{ +-} +-static inline void flush_kernel_vmap_range(void *vaddr, int size) +-{ +-} +-static inline void invalidate_kernel_vmap_range(void *vaddr, int size) +-{ +-} +-#endif ++#include "highmem-internal.h" + +-/* +- * Outside of CONFIG_HIGHMEM to support X86 32bit iomap_atomic() cruft. ++/** ++ * kmap - Map a page for long term usage ++ * @page: Pointer to the page to be mapped ++ * ++ * Returns: The virtual address of the mapping ++ * ++ * Can only be invoked from preemptible task context because on 32bit ++ * systems with CONFIG_HIGHMEM enabled this function might sleep. ++ * ++ * For systems with CONFIG_HIGHMEM=n and for pages in the low memory area ++ * this returns the virtual address of the direct kernel mapping. ++ * ++ * The returned virtual address is globally visible and valid up to the ++ * point where it is unmapped via kunmap(). The pointer can be handed to ++ * other contexts. ++ * ++ * For highmem pages on 32bit systems this can be slow as the mapping space ++ * is limited and protected by a global lock. In case that there is no ++ * mapping slot available the function blocks until a slot is released via ++ * kunmap(). + */ +-#ifdef CONFIG_KMAP_LOCAL +-void *__kmap_local_pfn_prot(unsigned long pfn, pgprot_t prot); +-void *__kmap_local_page_prot(struct page *page, pgprot_t prot); +-void kunmap_local_indexed(void *vaddr); +-#endif +- +-#ifdef CONFIG_HIGHMEM +-#include <asm/highmem.h> ++static inline void *kmap(struct page *page); + +-#ifndef ARCH_HAS_KMAP_FLUSH_TLB +-static inline void kmap_flush_tlb(unsigned long addr) { } +-#endif +- +-#ifndef kmap_prot +-#define kmap_prot PAGE_KERNEL +-#endif +- +-void *kmap_high(struct page *page); +-static inline void *kmap(struct page *page) +-{ +- void *addr; +- +- might_sleep(); +- if (!PageHighMem(page)) +- addr = page_address(page); +- else +- addr = kmap_high(page); +- kmap_flush_tlb((unsigned long)addr); +- return addr; +-} ++/** ++ * kunmap - Unmap the virtual address mapped by kmap() ++ * @addr: Virtual address to be unmapped ++ * ++ * Counterpart to kmap(). A NOOP for CONFIG_HIGHMEM=n and for mappings of ++ * pages in the low memory area. ++ */ ++static inline void kunmap(struct page *page); + +-void kunmap_high(struct page *page); ++/** ++ * kmap_to_page - Get the page for a kmap'ed address ++ * @addr: The address to look up ++ * ++ * Returns: The page which is mapped to @addr. ++ */ ++static inline struct page *kmap_to_page(void *addr); + +-static inline void kunmap(struct page *page) +-{ +- might_sleep(); +- if (!PageHighMem(page)) +- return; +- kunmap_high(page); +-} ++/** ++ * kmap_flush_unused - Flush all unused kmap mappings in order to ++ * remove stray mappings ++ */ ++static inline void kmap_flush_unused(void); + +-/* +- * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because +- * no global lock is needed and because the kmap code must perform a global TLB +- * invalidation when the kmap pool wraps. ++/** ++ * kmap_atomic - Atomically map a page for temporary usage ++ * @page: Pointer to the page to be mapped ++ * ++ * Returns: The virtual address of the mapping ++ * ++ * Side effect: On return pagefaults and preemption are disabled. ++ * ++ * Can be invoked from any context. + * +- * However when holding an atomic kmap it is not legal to sleep, so atomic +- * kmaps are appropriate for short, tight code paths only. ++ * Requires careful handling when nesting multiple mappings because the map ++ * management is stack based. The unmap has to be in the reverse order of ++ * the map operation: + * +- * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap +- * gives a more generic (and caching) interface. But kmap_atomic can +- * be used in IRQ contexts, so in some (very limited) cases we need +- * it. ++ * addr1 = kmap_atomic(page1); ++ * addr2 = kmap_atomic(page2); ++ * ... ++ * kunmap_atomic(addr2); ++ * kunmap_atomic(addr1); ++ * ++ * Unmapping addr1 before addr2 is invalid and causes malfunction. ++ * ++ * Contrary to kmap() mappings the mapping is only valid in the context of ++ * the caller and cannot be handed to other contexts. ++ * ++ * On CONFIG_HIGHMEM=n kernels and for low memory pages this returns the ++ * virtual address of the direct mapping. Only real highmem pages are ++ * temporarily mapped. ++ * ++ * While it is significantly faster than kmap() it comes with restrictions ++ * about the pointer validity and the side effects of disabling page faults ++ * and preemption. Use it only when absolutely necessary, e.g. from non ++ * preemptible contexts. + */ +-static inline void *kmap_atomic_prot(struct page *page, pgprot_t prot) +-{ +- preempt_disable(); +- pagefault_disable(); +- return __kmap_local_page_prot(page, prot); +-} ++static inline void *kmap_atomic(struct page *page); + +-static inline void *kmap_atomic(struct page *page) +-{ +- return kmap_atomic_prot(page, kmap_prot); +-} +- +-static inline void *kmap_atomic_pfn(unsigned long pfn) +-{ +- preempt_disable(); +- pagefault_disable(); +- return __kmap_local_pfn_prot(pfn, kmap_prot); +-} +- +-static inline void __kunmap_atomic(void *addr) +-{ +- kunmap_local_indexed(addr); +-} +- +-/* declarations for linux/mm/highmem.c */ +-unsigned int nr_free_highpages(void); +-extern atomic_long_t _totalhigh_pages; +-static inline unsigned long totalhigh_pages(void) +-{ +- return (unsigned long)atomic_long_read(&_totalhigh_pages); +-} +- +-static inline void totalhigh_pages_inc(void) +-{ +- atomic_long_inc(&_totalhigh_pages); +-} +- +-static inline void totalhigh_pages_add(long count) +-{ +- atomic_long_add(count, &_totalhigh_pages); +-} +- +-void kmap_flush_unused(void); +- +-struct page *kmap_to_page(void *addr); +- +-#else /* CONFIG_HIGHMEM */ +- +-static inline unsigned int nr_free_highpages(void) { return 0; } +- +-static inline struct page *kmap_to_page(void *addr) +-{ +- return virt_to_page(addr); +-} +- +-static inline unsigned long totalhigh_pages(void) { return 0UL; } ++/** ++ * kunmap_atomic - Unmap the virtual address mapped by kmap_atomic() ++ * @addr: Virtual address to be unmapped ++ * ++ * Counterpart to kmap_atomic(). ++ * ++ * Undoes the side effects of kmap_atomic(), i.e. reenabling pagefaults and ++ * preemption. ++ * ++ * Other than that a NOOP for CONFIG_HIGHMEM=n and for mappings of pages ++ * in the low memory area. For real highmen pages the mapping which was ++ * established with kmap_atomic() is destroyed. ++ */ + +-static inline void *kmap(struct page *page) +-{ +- might_sleep(); +- return page_address(page); +-} ++/* Highmem related interfaces for management code */ ++static inline unsigned int nr_free_highpages(void); ++static inline unsigned long totalhigh_pages(void); + +-static inline void kunmap_high(struct page *page) ++#ifndef ARCH_HAS_FLUSH_ANON_PAGE ++static inline void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr) + { + } +- +-static inline void kunmap(struct page *page) +-{ +-#ifdef ARCH_HAS_FLUSH_ON_KUNMAP +- kunmap_flush_on_unmap(page_address(page)); + #endif +-} + +-static inline void *kmap_atomic(struct page *page) ++#ifndef ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE ++static inline void flush_kernel_dcache_page(struct page *page) + { +- preempt_disable(); +- pagefault_disable(); +- return page_address(page); + } +- +-static inline void *kmap_atomic_prot(struct page *page, pgprot_t prot) ++static inline void flush_kernel_vmap_range(void *vaddr, int size) + { +- return kmap_atomic(page); + } +- +-static inline void *kmap_atomic_pfn(unsigned long pfn) ++static inline void invalidate_kernel_vmap_range(void *vaddr, int size) + { +- return kmap_atomic(pfn_to_page(pfn)); + } +- +-static inline void __kunmap_atomic(void *addr) +-{ +- /* +- * Mostly nothing to do in the CONFIG_HIGHMEM=n case as kunmap_atomic() +- * handles re-enabling faults and preemption +- */ +-#ifdef ARCH_HAS_FLUSH_ON_KUNMAP +- kunmap_flush_on_unmap(addr); + #endif +-} +- +-#define kmap_flush_unused() do {} while(0) +- +- +-#endif /* CONFIG_HIGHMEM */ +- +-/* +- * Prevent people trying to call kunmap_atomic() as if it were kunmap() +- * kunmap_atomic() should get the return value of kmap_atomic, not the page. +- */ +-#define kunmap_atomic(__addr) \ +-do { \ +- BUILD_BUG_ON(__same_type((__addr), struct page *)); \ +- __kunmap_atomic(__addr); \ +- pagefault_enable(); \ +- preempt_enable(); \ +-} while (0) + + /* when CONFIG_HIGHMEM is not set these will be plain clear/copy_page */ + #ifndef clear_user_highpage +diff --git a/mm/highmem.c b/mm/highmem.c +index 499dfafd36b7..54bd233846c9 100644 +--- a/mm/highmem.c ++++ b/mm/highmem.c +@@ -104,7 +104,7 @@ static inline wait_queue_head_t *get_pkmap_wait_queue_head(unsigned int color) + atomic_long_t _totalhigh_pages __read_mostly; + EXPORT_SYMBOL(_totalhigh_pages); + +-unsigned int nr_free_highpages (void) ++unsigned int __nr_free_highpages (void) + { + struct zone *zone; + unsigned int pages = 0; +@@ -141,7 +141,7 @@ pte_t * pkmap_page_table; + do { spin_unlock(&kmap_lock); (void)(flags); } while (0) + #endif + +-struct page *kmap_to_page(void *vaddr) ++struct page *__kmap_to_page(void *vaddr) + { + unsigned long addr = (unsigned long)vaddr; + +@@ -152,7 +152,7 @@ struct page *kmap_to_page(void *vaddr) + + return virt_to_page(addr); + } +-EXPORT_SYMBOL(kmap_to_page); ++EXPORT_SYMBOL(__kmap_to_page); + + static void flush_all_zero_pkmaps(void) + { +@@ -194,10 +194,7 @@ static void flush_all_zero_pkmaps(void) + flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP)); + } + +-/** +- * kmap_flush_unused - flush all unused kmap mappings in order to remove stray mappings +- */ +-void kmap_flush_unused(void) ++void __kmap_flush_unused(void) + { + lock_kmap(); + flush_all_zero_pkmaps(); +-- +2.43.0 + |