diff options
author | Paul Mundt <lethal@linux-sh.org> | 2008-11-10 06:00:45 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2008-11-10 06:00:45 -0500 |
commit | acca4f4d9bd657e8bc7e1665ba5077465138f133 (patch) | |
tree | 39b107c67d9dc59e6064e528457ca97ba4396ba2 /arch/sh/mm | |
parent | f7160c7573615ec82c691e294cf80d920b5d588d (diff) |
sh: Handle fixmap TLB eviction more coherently.
There was a race in the kmap_coherent() implementation. While we
guarded against preemption, there was nothing preventing eviction of
the pre-faulted fixmap entry from the UTLB. Under certain workloads
this would result in the fixmap entries used for cache colouring being
evicted from the UTLB in the midst of a copy_page().
In addition to pre-faulting, we also make sure to preserve the PTEs
in the kernel page table and introduce a cached PTE for kmap_coherent()
usage. This follows a similar change on MIPS ("[MIPS] Fix aliasing bug
in copy_to_user_page / copy_from_user_page").
Reported-by: Hideo Saito <saito@densan.co.jp>
Reported-by: CHIKAMA Masaki <masaki.chikama@gmail.com>
Tested-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/mm')
-rw-r--r-- | arch/sh/mm/init.c | 12 | ||||
-rw-r--r-- | arch/sh/mm/pg-sh4.c | 17 |
2 files changed, 26 insertions, 3 deletions
diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 4abf00031dae..6cbef8caeb56 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c | |||
@@ -137,6 +137,7 @@ void __init page_table_range_init(unsigned long start, unsigned long end, | |||
137 | void __init paging_init(void) | 137 | void __init paging_init(void) |
138 | { | 138 | { |
139 | unsigned long max_zone_pfns[MAX_NR_ZONES]; | 139 | unsigned long max_zone_pfns[MAX_NR_ZONES]; |
140 | unsigned long vaddr; | ||
140 | int nid; | 141 | int nid; |
141 | 142 | ||
142 | /* We don't need to map the kernel through the TLB, as | 143 | /* We don't need to map the kernel through the TLB, as |
@@ -148,10 +149,15 @@ void __init paging_init(void) | |||
148 | * check for a null value. */ | 149 | * check for a null value. */ |
149 | set_TTB(swapper_pg_dir); | 150 | set_TTB(swapper_pg_dir); |
150 | 151 | ||
151 | /* Populate the relevant portions of swapper_pg_dir so that | 152 | /* |
153 | * Populate the relevant portions of swapper_pg_dir so that | ||
152 | * we can use the fixmap entries without calling kmalloc. | 154 | * we can use the fixmap entries without calling kmalloc. |
153 | * pte's will be filled in by __set_fixmap(). */ | 155 | * pte's will be filled in by __set_fixmap(). |
154 | page_table_range_init(FIXADDR_START, FIXADDR_TOP, swapper_pg_dir); | 156 | */ |
157 | vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; | ||
158 | page_table_range_init(vaddr, 0, swapper_pg_dir); | ||
159 | |||
160 | kmap_coherent_init(); | ||
155 | 161 | ||
156 | memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); | 162 | memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); |
157 | 163 | ||
diff --git a/arch/sh/mm/pg-sh4.c b/arch/sh/mm/pg-sh4.c index 38870e0fc182..2fe14da1f839 100644 --- a/arch/sh/mm/pg-sh4.c +++ b/arch/sh/mm/pg-sh4.c | |||
@@ -7,6 +7,7 @@ | |||
7 | * Released under the terms of the GNU GPL v2.0. | 7 | * Released under the terms of the GNU GPL v2.0. |
8 | */ | 8 | */ |
9 | #include <linux/mm.h> | 9 | #include <linux/mm.h> |
10 | #include <linux/init.h> | ||
10 | #include <linux/mutex.h> | 11 | #include <linux/mutex.h> |
11 | #include <linux/fs.h> | 12 | #include <linux/fs.h> |
12 | #include <linux/highmem.h> | 13 | #include <linux/highmem.h> |
@@ -16,6 +17,20 @@ | |||
16 | 17 | ||
17 | #define CACHE_ALIAS (current_cpu_data.dcache.alias_mask) | 18 | #define CACHE_ALIAS (current_cpu_data.dcache.alias_mask) |
18 | 19 | ||
20 | #define kmap_get_fixmap_pte(vaddr) \ | ||
21 | pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)), (vaddr)) | ||
22 | |||
23 | static pte_t *kmap_coherent_pte; | ||
24 | |||
25 | void __init kmap_coherent_init(void) | ||
26 | { | ||
27 | unsigned long vaddr; | ||
28 | |||
29 | /* cache the first coherent kmap pte */ | ||
30 | vaddr = __fix_to_virt(FIX_CMAP_BEGIN); | ||
31 | kmap_coherent_pte = kmap_get_fixmap_pte(vaddr); | ||
32 | } | ||
33 | |||
19 | static inline void *kmap_coherent(struct page *page, unsigned long addr) | 34 | static inline void *kmap_coherent(struct page *page, unsigned long addr) |
20 | { | 35 | { |
21 | enum fixed_addresses idx; | 36 | enum fixed_addresses idx; |
@@ -34,6 +49,8 @@ static inline void *kmap_coherent(struct page *page, unsigned long addr) | |||
34 | 49 | ||
35 | update_mmu_cache(NULL, vaddr, pte); | 50 | update_mmu_cache(NULL, vaddr, pte); |
36 | 51 | ||
52 | set_pte(kmap_coherent_pte - (FIX_CMAP_END - idx), pte); | ||
53 | |||
37 | return (void *)vaddr; | 54 | return (void *)vaddr; |
38 | } | 55 | } |
39 | 56 | ||