linux/arch/xtensa/mm/highmem.c
Thomas Gleixner 1eb0616c2d 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: 629ed3f7da ("xtensa/mm/highmem: Switch to generic kmap atomic")
Reported-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Max Filippov <jcmvbkbc@gmail.com>
Link: https://lore.kernel.org/r/20201116193253.23875-1-jcmvbkbc@gmail.com
2020-11-16 21:19:24 +01:00

60 lines
1.4 KiB
C

/*
* High memory support for Xtensa architecture
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of
* this archive for more details.
*
* Copyright (C) 2014 Cadence Design Systems Inc.
*/
#include <linux/export.h>
#include <linux/highmem.h>
#include <asm/tlbflush.h>
#if DCACHE_WAY_SIZE > PAGE_SIZE
unsigned int last_pkmap_nr_arr[DCACHE_N_COLORS];
wait_queue_head_t pkmap_map_wait_arr[DCACHE_N_COLORS];
static void __init kmap_waitqueues_init(void)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(pkmap_map_wait_arr); ++i)
init_waitqueue_head(pkmap_map_wait_arr + i);
}
static inline enum fixed_addresses kmap_idx(int type, unsigned long color)
{
int idx = (type + KM_MAX_IDX * smp_processor_id()) * DCACHE_N_COLORS;
/*
* The fixmap operates top down, so the color offset needs to be
* reverse as well.
*/
return idx + DCACHE_N_COLORS - 1 - color;
}
enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn)
{
return kmap_idx(type, DCACHE_ALIAS(pfn << PAGE_SHIFT));
}
enum fixed_addresses kmap_local_unmap_idx(int type, unsigned long addr)
{
return kmap_idx(type, DCACHE_ALIAS(addr));
}
#else
static inline void kmap_waitqueues_init(void) { }
#endif
void __init kmap_init(void)
{
/* Check if this memory layout is broken because PKMAP overlaps
* page table.
*/
BUILD_BUG_ON(PKMAP_BASE < TLBTEMP_BASE_1 + TLBTEMP_SIZE);
kmap_waitqueues_init();
}