Skip to content

Commit 1eb0616

Browse files
committed
xtensa/mm/highmem: Make generic kmap_atomic() work correctly
The conversion to the generic kmap_atomic() implementation missed the fact that xtensa's fixmap works bottom up while all other implementations work top down. There is no real reason why xtensa needs to work that way. Cure it by: - Using the generic fix_to_virt()/virt_to_fix() functions which work top down - Adjusting the mapping defines - Using the generic index calculation for the non cache aliasing case - Making the cache colour offset reverse so the effective index is correct While at it, remove the outdated and misleading comment above the fixmap enum which originates from the initial copy&pasta of this code from i386. [ Max: Fixed the off by one in the index calculation ] Fixes: 629ed3f ("xtensa/mm/highmem: Switch to generic kmap atomic") Reported-by: Max Filippov <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Max Filippov <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Max Filippov <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 2a656ca commit 1eb0616

File tree

5 files changed

+31
-64
lines changed

5 files changed

+31
-64
lines changed

arch/xtensa/include/asm/fixmap.h

Lines changed: 7 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,63 +17,22 @@
1717
#include <linux/threads.h>
1818
#include <linux/pgtable.h>
1919
#include <asm/kmap_size.h>
20-
#endif
2120

22-
/*
23-
* Here we define all the compile-time 'special' virtual
24-
* addresses. The point is to have a constant address at
25-
* compile time, but to set the physical address only
26-
* in the boot process. We allocate these special addresses
27-
* from the start of the consistent memory region upwards.
28-
* Also this lets us do fail-safe vmalloc(), we
29-
* can guarantee that these special addresses and
30-
* vmalloc()-ed addresses never overlap.
31-
*
32-
* these 'compile-time allocated' memory buffers are
33-
* fixed-size 4k pages. (or larger if used with an increment
34-
* higher than 1) use fixmap_set(idx,phys) to associate
35-
* physical memory with fixmap indices.
36-
*/
21+
/* The map slots for temporary mappings via kmap_atomic/local(). */
3722
enum fixed_addresses {
38-
#ifdef CONFIG_HIGHMEM
39-
/* reserved pte's for temporary kernel mappings */
4023
FIX_KMAP_BEGIN,
4124
FIX_KMAP_END = FIX_KMAP_BEGIN +
4225
(KM_MAX_IDX * NR_CPUS * DCACHE_N_COLORS) - 1,
43-
#endif
4426
__end_of_fixed_addresses
4527
};
4628

47-
#define FIXADDR_TOP (XCHAL_KSEG_CACHED_VADDR - PAGE_SIZE)
29+
#define FIXADDR_END (XCHAL_KSEG_CACHED_VADDR - PAGE_SIZE)
4830
#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
49-
#define FIXADDR_START ((FIXADDR_TOP - FIXADDR_SIZE) & PMD_MASK)
31+
/* Enforce that FIXADDR_START is PMD aligned to handle cache aliasing */
32+
#define FIXADDR_START ((FIXADDR_END - FIXADDR_SIZE) & PMD_MASK)
33+
#define FIXADDR_TOP (FIXADDR_START + FIXADDR_SIZE - PAGE_SIZE)
5034

51-
#define __fix_to_virt(x) (FIXADDR_START + ((x) << PAGE_SHIFT))
52-
#define __virt_to_fix(x) (((x) - FIXADDR_START) >> PAGE_SHIFT)
53-
54-
#ifndef __ASSEMBLY__
55-
/*
56-
* 'index to address' translation. If anyone tries to use the idx
57-
* directly without translation, we catch the bug with a NULL-deference
58-
* kernel oops. Illegal ranges of incoming indices are caught too.
59-
*/
60-
static __always_inline unsigned long fix_to_virt(const unsigned int idx)
61-
{
62-
/* Check if this memory layout is broken because fixmap overlaps page
63-
* table.
64-
*/
65-
BUILD_BUG_ON(FIXADDR_START <
66-
TLBTEMP_BASE_1 + TLBTEMP_SIZE);
67-
BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
68-
return __fix_to_virt(idx);
69-
}
70-
71-
static inline unsigned long virt_to_fix(const unsigned long vaddr)
72-
{
73-
BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START);
74-
return __virt_to_fix(vaddr);
75-
}
76-
77-
#endif
35+
#include <asm-generic/fixmap.h>
7836

37+
#endif /* CONFIG_HIGHMEM */
7938
#endif

arch/xtensa/include/asm/highmem.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#ifndef _XTENSA_HIGHMEM_H
1313
#define _XTENSA_HIGHMEM_H
1414

15+
#ifdef CONFIG_HIGHMEM
1516
#include <linux/wait.h>
1617
#include <linux/pgtable.h>
1718
#include <asm/cacheflush.h>
@@ -58,6 +59,13 @@ static inline wait_queue_head_t *get_pkmap_wait_queue_head(unsigned int color)
5859
{
5960
return pkmap_map_wait_arr + color;
6061
}
62+
63+
enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn);
64+
#define arch_kmap_local_map_idx kmap_local_map_idx
65+
66+
enum fixed_addresses kmap_local_unmap_idx(int type, unsigned long addr);
67+
#define arch_kmap_local_unmap_idx kmap_local_unmap_idx
68+
6169
#endif
6270

6371
extern pte_t *pkmap_page_table;
@@ -67,15 +75,10 @@ static inline void flush_cache_kmaps(void)
6775
flush_cache_all();
6876
}
6977

70-
enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn);
71-
#define arch_kmap_local_map_idx kmap_local_map_idx
72-
73-
enum fixed_addresses kmap_local_unmap_idx(int type, unsigned long addr);
74-
#define arch_kmap_local_unmap_idx kmap_local_unmap_idx
75-
7678
#define arch_kmap_local_post_unmap(vaddr) \
7779
local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE)
7880

7981
void kmap_init(void);
8082

83+
#endif /* CONFIG_HIGHMEM */
8184
#endif

arch/xtensa/mm/highmem.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ static void __init kmap_waitqueues_init(void)
2323
for (i = 0; i < ARRAY_SIZE(pkmap_map_wait_arr); ++i)
2424
init_waitqueue_head(pkmap_map_wait_arr + i);
2525
}
26-
#else
27-
static inline void kmap_waitqueues_init(void)
28-
{
29-
}
30-
#endif
3126

3227
static inline enum fixed_addresses kmap_idx(int type, unsigned long color)
3328
{
34-
return (type + KM_MAX_IDX * smp_processor_id()) * DCACHE_N_COLORS +
35-
color;
29+
int idx = (type + KM_MAX_IDX * smp_processor_id()) * DCACHE_N_COLORS;
30+
31+
/*
32+
* The fixmap operates top down, so the color offset needs to be
33+
* reverse as well.
34+
*/
35+
return idx + DCACHE_N_COLORS - 1 - color;
3636
}
3737

3838
enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn)
@@ -45,6 +45,10 @@ enum fixed_addresses kmap_local_unmap_idx(int type, unsigned long addr)
4545
return kmap_idx(type, DCACHE_ALIAS(addr));
4646
}
4747

48+
#else
49+
static inline void kmap_waitqueues_init(void) { }
50+
#endif
51+
4852
void __init kmap_init(void)
4953
{
5054
/* Check if this memory layout is broken because PKMAP overlaps

arch/xtensa/mm/init.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ void __init mem_init(void)
147147
#ifdef CONFIG_HIGHMEM
148148
PKMAP_BASE, PKMAP_BASE + LAST_PKMAP * PAGE_SIZE,
149149
(LAST_PKMAP*PAGE_SIZE) >> 10,
150-
FIXADDR_START, FIXADDR_TOP,
151-
(FIXADDR_TOP - FIXADDR_START) >> 10,
150+
FIXADDR_START, FIXADDR_END,
151+
(FIXADDR_END - FIXADDR_START) >> 10,
152152
#endif
153153
PAGE_OFFSET, PAGE_OFFSET +
154154
(max_low_pfn - min_low_pfn) * PAGE_SIZE,

arch/xtensa/mm/mmu.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ static void * __init init_pmd(unsigned long vaddr, unsigned long n_pages)
5252

5353
static void __init fixedrange_init(void)
5454
{
55-
init_pmd(__fix_to_virt(0), __end_of_fixed_addresses);
55+
BUILD_BUG_ON(FIXADDR_START < TLBTEMP_BASE_1 + TLBTEMP_SIZE);
56+
init_pmd(FIXADDR_START, __end_of_fixed_addresses);
5657
}
5758
#endif
5859

0 commit comments

Comments
 (0)