blob: f13f51003bd83c7db13c8e7f298b85c14e4a962f [file] [log] [blame]
Ralf Baechleb99fbc12012-09-06 11:29:53 +02001#include <linux/compiler.h>
Paul Gortmakerd9ba5772016-08-21 15:58:14 -04002#include <linux/init.h>
3#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07004#include <linux/highmem.h>
Yoichi Yuasa52ab3202010-02-20 21:23:22 +09005#include <linux/sched.h>
Ralf Baechle631330f2009-06-19 14:05:26 +01006#include <linux/smp.h>
Ralf Baechlebb86bf22009-04-25 11:25:34 +02007#include <asm/fixmap.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <asm/tlbflush.h>
9
Ralf Baechlebb86bf22009-04-25 11:25:34 +020010static pte_t *kmap_pte;
11
12unsigned long highstart_pfn, highend_pfn;
13
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070014void *kmap(struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -070015{
16 void *addr;
17
18 might_sleep();
19 if (!PageHighMem(page))
20 return page_address(page);
21 addr = kmap_high(page);
22 flush_tlb_one((unsigned long)addr);
23
24 return addr;
25}
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070026EXPORT_SYMBOL(kmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070028void kunmap(struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -070029{
Ralf Baechleb72b7092009-03-30 14:49:44 +020030 BUG_ON(in_interrupt());
Linus Torvalds1da177e2005-04-16 15:20:36 -070031 if (!PageHighMem(page))
32 return;
33 kunmap_high(page);
34}
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070035EXPORT_SYMBOL(kunmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37/*
38 * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because
39 * no global lock is needed and because the kmap code must perform a global TLB
40 * invalidation when the kmap pool wraps.
41 *
42 * However when holding an atomic kmap is is not legal to sleep, so atomic
43 * kmaps are appropriate for short, tight code paths only.
44 */
45
Cong Wanga24401b2011-11-26 10:53:39 +080046void *kmap_atomic(struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047{
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 unsigned long vaddr;
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070049 int idx, type;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
David Hildenbrand2cb7c9c2015-05-11 17:52:09 +020051 preempt_disable();
Peter Zijlstraa8663742006-12-06 20:32:20 -080052 pagefault_disable();
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 if (!PageHighMem(page))
54 return page_address(page);
55
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070056 type = kmap_atomic_idx_push();
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 idx = type + KM_TYPE_NR*smp_processor_id();
58 vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
59#ifdef CONFIG_DEBUG_HIGHMEM
Ralf Baechleb72b7092009-03-30 14:49:44 +020060 BUG_ON(!pte_none(*(kmap_pte - idx)));
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#endif
Ralf Baechlebb86bf22009-04-25 11:25:34 +020062 set_pte(kmap_pte-idx, mk_pte(page, PAGE_KERNEL));
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 local_flush_tlb_one((unsigned long)vaddr);
64
65 return (void*) vaddr;
66}
Cong Wanga24401b2011-11-26 10:53:39 +080067EXPORT_SYMBOL(kmap_atomic);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070069void __kunmap_atomic(void *kvaddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -070070{
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
Ralf Baechleb99fbc12012-09-06 11:29:53 +020072 int type __maybe_unused;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74 if (vaddr < FIXADDR_START) { // FIXME
Peter Zijlstraa8663742006-12-06 20:32:20 -080075 pagefault_enable();
David Hildenbrand2cb7c9c2015-05-11 17:52:09 +020076 preempt_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 return;
78 }
79
Peter Zijlstra20273942010-10-27 15:32:58 -070080 type = kmap_atomic_idx();
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070081#ifdef CONFIG_DEBUG_HIGHMEM
82 {
83 int idx = type + KM_TYPE_NR * smp_processor_id();
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070085 BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
86
87 /*
88 * force other mappings to Oops if they'll try to access
89 * this pte without first remap it
90 */
91 pte_clear(&init_mm, vaddr, kmap_pte-idx);
92 local_flush_tlb_one(vaddr);
93 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070094#endif
Peter Zijlstra20273942010-10-27 15:32:58 -070095 kmap_atomic_idx_pop();
Peter Zijlstraa8663742006-12-06 20:32:20 -080096 pagefault_enable();
David Hildenbrand2cb7c9c2015-05-11 17:52:09 +020097 preempt_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -070098}
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -070099EXPORT_SYMBOL(__kunmap_atomic);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
Ralf Baechle60080262005-07-11 20:45:51 +0000101/*
102 * This is the same as kmap_atomic() but can map memory that doesn't
103 * have a struct page associated with it.
104 */
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -0700105void *kmap_atomic_pfn(unsigned long pfn)
Ralf Baechle60080262005-07-11 20:45:51 +0000106{
Ralf Baechle60080262005-07-11 20:45:51 +0000107 unsigned long vaddr;
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -0700108 int idx, type;
Ralf Baechle60080262005-07-11 20:45:51 +0000109
David Hildenbrand2cb7c9c2015-05-11 17:52:09 +0200110 preempt_disable();
Peter Zijlstraa8663742006-12-06 20:32:20 -0800111 pagefault_disable();
Ralf Baechle60080262005-07-11 20:45:51 +0000112
Peter Zijlstra3e4d3af2010-10-26 14:21:51 -0700113 type = kmap_atomic_idx_push();
Ralf Baechle60080262005-07-11 20:45:51 +0000114 idx = type + KM_TYPE_NR*smp_processor_id();
115 vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
Ralf Baechlebb86bf22009-04-25 11:25:34 +0200116 set_pte(kmap_pte-idx, pfn_pte(pfn, PAGE_KERNEL));
Ralf Baechle60080262005-07-11 20:45:51 +0000117 flush_tlb_one(vaddr);
118
119 return (void*) vaddr;
120}
121
Ralf Baechlebb86bf22009-04-25 11:25:34 +0200122void __init kmap_init(void)
123{
124 unsigned long kmap_vstart;
125
126 /* cache the first kmap pte */
127 kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
128 kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
129}