aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arc/mm/highmem.c
diff options
context:
space:
mode:
authorVineet Gupta <vgupta@synopsys.com>2015-03-09 09:23:49 -0400
committerVineet Gupta <vgupta@synopsys.com>2015-10-28 10:19:04 -0400
commit45890f6d34e70d9dd194bd1729eba3ff72cabf78 (patch)
tree11ebe206ba2fe233edc91d49e334dcef46dcf890 /arch/arc/mm/highmem.c
parent6101be5ad439806c70b54bdd083e7db9e3affb3d (diff)
ARC: mm: HIGHMEM: kmap API implementation
Implement kmap* API for ARC. This enables - permanent kernel maps (pkmaps): :kmap() API - fixmap : kmap_atomic() We use a very simple/uniform approach for both (unlike some of the other arches). So fixmap doesn't use the customary compile time address stuff. The important semantic is sleep'ability (pkmap) vs. not (fixmap) which the API guarantees. Note that this patch only enables highmem for subsequent PAE40 support as there is no real highmem for ARC in pure 32-bit paradigm as explained below. ARC has 2:2 address split of the 32-bit address space with lower half being translated (virtual) while upper half unstranslated (0x8000_0000 to 0xFFFF_FFFF). kernel itself is linked at base of unstranslated space (i.e. 0x8000_0000 onwards), which is mapped to say DDR 0x0 by external Bus Glue logic (outside the core). So kernel can potentially access 1.75G worth of memory directly w/o need for highmem. (the top 256M is taken by uncached peripheral space from 0xF000_0000 to 0xFFFF_FFFF) In PAE40, hardware can address memory beyond 4G (0x1_0000_0000) while the logical/virtual addresses remain 32-bits. Thus highmem is required for kernel proper to be able to access these pages for it's own purposes (user space is agnostic to this anyways). Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com> Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Diffstat (limited to 'arch/arc/mm/highmem.c')
-rw-r--r--arch/arc/mm/highmem.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/arch/arc/mm/highmem.c b/arch/arc/mm/highmem.c
new file mode 100644
index 000000000000..065ee6bfa82a
--- /dev/null
+++ b/arch/arc/mm/highmem.c
@@ -0,0 +1,140 @@
1/*
2 * Copyright (C) 2015 Synopsys, Inc. (www.synopsys.com)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/bootmem.h>
11#include <linux/export.h>
12#include <linux/highmem.h>
13#include <asm/processor.h>
14#include <asm/pgtable.h>
15#include <asm/pgalloc.h>
16#include <asm/tlbflush.h>
17
18/*
19 * HIGHMEM API:
20 *
21 * kmap() API provides sleep semantics hence refered to as "permanent maps"
22 * It allows mapping LAST_PKMAP pages, using @last_pkmap_nr as the cursor
23 * for book-keeping
24 *
25 * kmap_atomic() can't sleep (calls pagefault_disable()), thus it provides
26 * shortlived ala "temporary mappings" which historically were implemented as
27 * fixmaps (compile time addr etc). Their book-keeping is done per cpu.
28 *
29 * Both these facts combined (preemption disabled and per-cpu allocation)
30 * means the total number of concurrent fixmaps will be limited to max
31 * such allocations in a single control path. Thus KM_TYPE_NR (another
32 * historic relic) is a small'ish number which caps max percpu fixmaps
33 *
34 * ARC HIGHMEM Details
35 *
36 * - the kernel vaddr space from 0x7z to 0x8z (currently used by vmalloc/module)
37 * is now shared between vmalloc and kmap (non overlapping though)
38 *
39 * - Both fixmap/pkmap use a dedicated page table each, hooked up to swapper PGD
40 * This means each only has 1 PGDIR_SIZE worth of kvaddr mappings, which means
41 * 2M of kvaddr space for typical config (8K page and 11:8:13 traversal split)
42 *
43 * - fixmap anyhow needs a limited number of mappings. So 2M kvaddr == 256 PTE
44 * slots across NR_CPUS would be more than sufficient (generic code defines
45 * KM_TYPE_NR as 20).
46 *
47 * - pkmap being preemptible, in theory could do with more than 256 concurrent
48 * mappings. However, generic pkmap code: map_new_virtual(), doesn't traverse
49 * the PGD and only works with a single page table @pkmap_page_table, hence
50 * sets the limit
51 */
52
53extern pte_t * pkmap_page_table;
54static pte_t * fixmap_page_table;
55
56void *kmap(struct page *page)
57{
58 BUG_ON(in_interrupt());
59 if (!PageHighMem(page))
60 return page_address(page);
61
62 return kmap_high(page);
63}
64
65void *kmap_atomic(struct page *page)
66{
67 int idx, cpu_idx;
68 unsigned long vaddr;
69
70 preempt_disable();
71 pagefault_disable();
72 if (!PageHighMem(page))
73 return page_address(page);
74
75 cpu_idx = kmap_atomic_idx_push();
76 idx = cpu_idx + KM_TYPE_NR * smp_processor_id();
77 vaddr = FIXMAP_ADDR(idx);
78
79 set_pte_at(&init_mm, vaddr, fixmap_page_table + idx,
80 mk_pte(page, kmap_prot));
81
82 return (void *)vaddr;
83}
84EXPORT_SYMBOL(kmap_atomic);
85
86void __kunmap_atomic(void *kv)
87{
88 unsigned long kvaddr = (unsigned long)kv;
89
90 if (kvaddr >= FIXMAP_BASE && kvaddr < (FIXMAP_BASE + FIXMAP_SIZE)) {
91
92 /*
93 * Because preemption is disabled, this vaddr can be associated
94 * with the current allocated index.
95 * But in case of multiple live kmap_atomic(), it still relies on
96 * callers to unmap in right order.
97 */
98 int cpu_idx = kmap_atomic_idx();
99 int idx = cpu_idx + KM_TYPE_NR * smp_processor_id();
100
101 WARN_ON(kvaddr != FIXMAP_ADDR(idx));
102
103 pte_clear(&init_mm, kvaddr, fixmap_page_table + idx);
104 local_flush_tlb_kernel_range(kvaddr, kvaddr + PAGE_SIZE);
105
106 kmap_atomic_idx_pop();
107 }
108
109 pagefault_enable();
110 preempt_enable();
111}
112EXPORT_SYMBOL(__kunmap_atomic);
113
114noinline pte_t *alloc_kmap_pgtable(unsigned long kvaddr)
115{
116 pgd_t *pgd_k;
117 pud_t *pud_k;
118 pmd_t *pmd_k;
119 pte_t *pte_k;
120
121 pgd_k = pgd_offset_k(kvaddr);
122 pud_k = pud_offset(pgd_k, kvaddr);
123 pmd_k = pmd_offset(pud_k, kvaddr);
124
125 pte_k = (pte_t *)alloc_bootmem_low_pages(PAGE_SIZE);
126 pmd_populate_kernel(&init_mm, pmd_k, pte_k);
127 return pte_k;
128}
129
130void kmap_init(void)
131{
132 /* Due to recursive include hell, we can't do this in processor.h */
133 BUILD_BUG_ON(PAGE_OFFSET < (VMALLOC_END + FIXMAP_SIZE + PKMAP_SIZE));
134
135 BUILD_BUG_ON(KM_TYPE_NR > PTRS_PER_PTE);
136 pkmap_page_table = alloc_kmap_pgtable(PKMAP_BASE);
137
138 BUILD_BUG_ON(LAST_PKMAP > PTRS_PER_PTE);
139 fixmap_page_table = alloc_kmap_pgtable(FIXMAP_BASE);
140}