| /* 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(const void *vaddr); | |
| void kmap_local_fork(struct task_struct *tsk); | |
| void __kmap_local_sched_out(void); | |
| void __kmap_local_sched_in(void); | |
| static inline void kmap_assert_nomap(void) | |
| { | |
| DEBUG_LOCKS_WARN_ON(current->kmap_ctrl.idx); | |
| } | |
| #else | |
| static inline void kmap_local_fork(struct task_struct *tsk) { } | |
| static inline void kmap_assert_nomap(void) { } | |
| #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_local_page(struct page *page) | |
| { | |
| return __kmap_local_page_prot(page, kmap_prot); | |
| } | |
| static inline void *kmap_local_folio(struct folio *folio, size_t offset) | |
| { | |
| struct page *page = folio_page(folio, offset / PAGE_SIZE); | |
| return __kmap_local_page_prot(page, kmap_prot) + offset % PAGE_SIZE; | |
| } | |
| static inline void *kmap_local_page_prot(struct page *page, pgprot_t prot) | |
| { | |
| return __kmap_local_page_prot(page, prot); | |
| } | |
| static inline void *kmap_local_pfn(unsigned long pfn) | |
| { | |
| return __kmap_local_pfn_prot(pfn, kmap_prot); | |
| } | |
| static inline void __kunmap_local(const void *vaddr) | |
| { | |
| kunmap_local_indexed(vaddr); | |
| } | |
| static inline void *kmap_atomic_prot(struct page *page, pgprot_t prot) | |
| { | |
| if (IS_ENABLED(CONFIG_PREEMPT_RT)) | |
| migrate_disable(); | |
| else | |
| 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) | |
| { | |
| if (IS_ENABLED(CONFIG_PREEMPT_RT)) | |
| migrate_disable(); | |
| else | |
| preempt_disable(); | |
| pagefault_disable(); | |
| return __kmap_local_pfn_prot(pfn, kmap_prot); | |
| } | |
| static inline void __kunmap_atomic(const void *addr) | |
| { | |
| kunmap_local_indexed(addr); | |
| pagefault_enable(); | |
| if (IS_ENABLED(CONFIG_PREEMPT_RT)) | |
| migrate_enable(); | |
| else | |
| 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_add(long count) | |
| { | |
| atomic_long_add(count, &_totalhigh_pages); | |
| } | |
| static inline bool is_kmap_addr(const void *x) | |
| { | |
| unsigned long addr = (unsigned long)x; | |
| return (addr >= PKMAP_ADDR(0) && addr < PKMAP_ADDR(LAST_PKMAP)) || | |
| (addr >= __fix_to_virt(FIX_KMAP_END) && | |
| addr < __fix_to_virt(FIX_KMAP_BEGIN)); | |
| } | |
| #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_local_page(struct page *page) | |
| { | |
| return page_address(page); | |
| } | |
| static inline void *kmap_local_folio(struct folio *folio, size_t offset) | |
| { | |
| return page_address(&folio->page) + offset; | |
| } | |
| static inline void *kmap_local_page_prot(struct page *page, pgprot_t prot) | |
| { | |
| return kmap_local_page(page); | |
| } | |
| static inline void *kmap_local_pfn(unsigned long pfn) | |
| { | |
| return kmap_local_page(pfn_to_page(pfn)); | |
| } | |
| static inline void __kunmap_local(const void *addr) | |
| { | |
| #ifdef ARCH_HAS_FLUSH_ON_KUNMAP | |
| kunmap_flush_on_unmap(PTR_ALIGN_DOWN(addr, PAGE_SIZE)); | |
| #endif | |
| } | |
| static inline void *kmap_atomic(struct page *page) | |
| { | |
| if (IS_ENABLED(CONFIG_PREEMPT_RT)) | |
| migrate_disable(); | |
| else | |
| 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(const void *addr) | |
| { | |
| #ifdef ARCH_HAS_FLUSH_ON_KUNMAP | |
| kunmap_flush_on_unmap(PTR_ALIGN_DOWN(addr, PAGE_SIZE)); | |
| #endif | |
| pagefault_enable(); | |
| if (IS_ENABLED(CONFIG_PREEMPT_RT)) | |
| migrate_enable(); | |
| else | |
| preempt_enable(); | |
| } | |
| static inline unsigned int nr_free_highpages(void) { return 0; } | |
| static inline unsigned long totalhigh_pages(void) { return 0UL; } | |
| static inline bool is_kmap_addr(const void *x) | |
| { | |
| return false; | |
| } | |
| #endif /* CONFIG_HIGHMEM */ | |
| /** | |
| * kunmap_atomic - Unmap the virtual address mapped by kmap_atomic() - deprecated! | |
| * @__addr: Virtual address to be unmapped | |
| * | |
| * Unmaps an address previously mapped by kmap_atomic() and re-enables | |
| * pagefaults. Depending on PREEMP_RT configuration, re-enables also | |
| * migration and preemption. Users should not count on these side effects. | |
| * | |
| * Mappings should be unmapped in the reverse order that they were mapped. | |
| * See kmap_local_page() for details on nesting. | |
| * | |
| * @__addr can be any address within the mapped page, so there is no need | |
| * to subtract any offset that has been added. In contrast to kunmap(), | |
| * this function takes the address returned from kmap_atomic(), not the | |
| * page passed to it. The compiler will warn you if you pass the page. | |
| */ | |
| #define kunmap_atomic(__addr) \ | |
| do { \ | |
| BUILD_BUG_ON(__same_type((__addr), struct page *)); \ | |
| __kunmap_atomic(__addr); \ | |
| } while (0) | |
| /** | |
| * kunmap_local - Unmap a page mapped via kmap_local_page(). | |
| * @__addr: An address within the page mapped | |
| * | |
| * @__addr can be any address within the mapped page. Commonly it is the | |
| * address return from kmap_local_page(), but it can also include offsets. | |
| * | |
| * Unmapping should be done in the reverse order of the mapping. See | |
| * kmap_local_page() for details. | |
| */ | |
| #define kunmap_local(__addr) \ | |
| do { \ | |
| BUILD_BUG_ON(__same_type((__addr), struct page *)); \ | |
| __kunmap_local(__addr); \ | |
| } while (0) | |
| #endif |