Skip to content

Commit

Permalink
Merge tag 'core-mm-2020-12-14' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/tip/tip

Pull kmap updates from Thomas Gleixner:
 "The new preemtible kmap_local() implementation:

   - Consolidate all kmap_atomic() internals into a generic
     implementation which builds the base for the kmap_local() API and
     make the kmap_atomic() interface wrappers which handle the
     disabling/enabling of preemption and pagefaults.

   - Switch the storage from per-CPU to per task and provide scheduler
     support for clearing mapping when scheduling out and restoring them
     when scheduling back in.

   - Merge the migrate_disable/enable() code, which is also part of the
     scheduler pull request. This was required to make the kmap_local()
     interface available which does not disable preemption when a
     mapping is established. It has to disable migration instead to
     guarantee that the virtual address of the mapped slot is the same
     across preemption.

   - Provide better debug facilities: guard pages and enforced
     utilization of the mapping mechanics on 64bit systems when the
     architecture allows it.

   - Provide the new kmap_local() API which can now be used to cleanup
     the kmap_atomic() usage sites all over the place. Most of the usage
     sites do not require the implicit disabling of preemption and
     pagefaults so the penalty on 64bit and 32bit non-highmem systems is
     removed and quite some of the code can be simplified. A wholesale
     conversion is not possible because some usage depends on the
     implicit side effects and some need to be cleaned up because they
     work around these side effects.

     The migrate disable side effect is only effective on highmem
     systems and when enforced debugging is enabled. On 64bit and 32bit
     non-highmem systems the overhead is completely avoided"

* tag 'core-mm-2020-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (33 commits)
  ARM: highmem: Fix cache_is_vivt() reference
  x86/crashdump/32: Simplify copy_oldmem_page()
  io-mapping: Provide iomap_local variant
  mm/highmem: Provide kmap_local*
  sched: highmem: Store local kmaps in task struct
  x86: Support kmap_local() forced debugging
  mm/highmem: Provide CONFIG_DEBUG_KMAP_LOCAL_FORCE_MAP
  mm/highmem: Provide and use CONFIG_DEBUG_KMAP_LOCAL
  microblaze/mm/highmem: Add dropped #ifdef back
  xtensa/mm/highmem: Make generic kmap_atomic() work correctly
  mm/highmem: Take kmap_high_get() properly into account
  highmem: High implementation details and document API
  Documentation/io-mapping: Remove outdated blurb
  io-mapping: Cleanup atomic iomap
  mm/highmem: Remove the old kmap_atomic cruft
  highmem: Get rid of kmap_types.h
  xtensa/mm/highmem: Switch to generic kmap atomic
  sparc/mm/highmem: Switch to generic kmap atomic
  powerpc/mm/highmem: Switch to generic kmap atomic
  nds32/mm/highmem: Switch to generic kmap atomic
  ...
  • Loading branch information
torvalds committed Dec 15, 2020
2 parents adb35e8 + 68061c0 commit edd7ab7
Show file tree
Hide file tree
Showing 88 changed files with 946 additions and 1,423 deletions.
96 changes: 45 additions & 51 deletions Documentation/driver-api/io-mapping.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,78 +20,72 @@ A mapping object is created during driver initialization using::
mappable, while 'size' indicates how large a mapping region to
enable. Both are in bytes.

This _wc variant provides a mapping which may only be used
with the io_mapping_map_atomic_wc or io_mapping_map_wc.
This _wc variant provides a mapping which may only be used with
io_mapping_map_atomic_wc(), io_mapping_map_local_wc() or
io_mapping_map_wc().

With this mapping object, individual pages can be mapped either atomically
or not, depending on the necessary scheduling environment. Of course, atomic
maps are more efficient::
With this mapping object, individual pages can be mapped either temporarily
or long term, depending on the requirements. Of course, temporary maps are
more efficient. They come in two flavours::

void *io_mapping_map_local_wc(struct io_mapping *mapping,
unsigned long offset)

void *io_mapping_map_atomic_wc(struct io_mapping *mapping,
unsigned long offset)

'offset' is the offset within the defined mapping region.
Accessing addresses beyond the region specified in the
creation function yields undefined results. Using an offset
which is not page aligned yields an undefined result. The
return value points to a single page in CPU address space.
'offset' is the offset within the defined mapping region. Accessing
addresses beyond the region specified in the creation function yields
undefined results. Using an offset which is not page aligned yields an
undefined result. The return value points to a single page in CPU address
space.

This _wc variant returns a write-combining map to the
page and may only be used with mappings created by
io_mapping_create_wc
This _wc variant returns a write-combining map to the page and may only be
used with mappings created by io_mapping_create_wc()

Note that the task may not sleep while holding this page
mapped.
Temporary mappings are only valid in the context of the caller. The mapping
is not guaranteed to be globaly visible.

::
io_mapping_map_local_wc() has a side effect on X86 32bit as it disables
migration to make the mapping code work. No caller can rely on this side
effect.

void io_mapping_unmap_atomic(void *vaddr)
io_mapping_map_atomic_wc() has the side effect of disabling preemption and
pagefaults. Don't use in new code. Use io_mapping_map_local_wc() instead.

Nested mappings need to be undone in reverse order because the mapping
code uses a stack for keeping track of them::

'vaddr' must be the value returned by the last
io_mapping_map_atomic_wc call. This unmaps the specified
page and allows the task to sleep once again.
addr1 = io_mapping_map_local_wc(map1, offset1);
addr2 = io_mapping_map_local_wc(map2, offset2);
...
io_mapping_unmap_local(addr2);
io_mapping_unmap_local(addr1);

The mappings are released with::

void io_mapping_unmap_local(void *vaddr)
void io_mapping_unmap_atomic(void *vaddr)

If you need to sleep while holding the lock, you can use the non-atomic
variant, although they may be significantly slower.
'vaddr' must be the value returned by the last io_mapping_map_local_wc() or
io_mapping_map_atomic_wc() call. This unmaps the specified mapping and
undoes the side effects of the mapping functions.

::
If you need to sleep while holding a mapping, you can use the regular
variant, although this may be significantly slower::

void *io_mapping_map_wc(struct io_mapping *mapping,
unsigned long offset)

This works like io_mapping_map_atomic_wc except it allows
the task to sleep while holding the page mapped.
This works like io_mapping_map_atomic/local_wc() except it has no side
effects and the pointer is globaly visible.


::
The mappings are released with::

void io_mapping_unmap(void *vaddr)

This works like io_mapping_unmap_atomic, except it is used
for pages mapped with io_mapping_map_wc.
Use for pages mapped with io_mapping_map_wc().

At driver close time, the io_mapping object must be freed::

void io_mapping_free(struct io_mapping *mapping)

Current Implementation
======================

The initial implementation of these functions uses existing mapping
mechanisms and so provides only an abstraction layer and no new
functionality.

On 64-bit processors, io_mapping_create_wc calls ioremap_wc for the whole
range, creating a permanent kernel-visible mapping to the resource. The
map_atomic and map functions add the requested offset to the base of the
virtual address returned by ioremap_wc.

On 32-bit processors with HIGHMEM defined, io_mapping_map_atomic_wc uses
kmap_atomic_pfn to map the specified page in an atomic fashion;
kmap_atomic_pfn isn't really supposed to be used with device pages, but it
provides an efficient mapping for this usage.

On 32-bit processors without HIGHMEM defined, io_mapping_map_atomic_wc and
io_mapping_map_wc both use ioremap_wc, a terribly inefficient function which
performs an IPI to inform all processors about the new mapping. This results
in a significant performance penalty.
15 changes: 0 additions & 15 deletions arch/alpha/include/asm/kmap_types.h

This file was deleted.

1 change: 1 addition & 0 deletions arch/arc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ config LINUX_RAM_BASE
config HIGHMEM
bool "High Memory Support"
select ARCH_DISCONTIGMEM_ENABLE
select KMAP_LOCAL
help
With ARC 2G:2G address split, only upper 2G is directly addressable by
kernel. Enable this to potentially allow access to rest of 2G and PAE
Expand Down
26 changes: 20 additions & 6 deletions arch/arc/include/asm/highmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,29 @@
#ifdef CONFIG_HIGHMEM

#include <uapi/asm/page.h>
#include <asm/kmap_types.h>
#include <asm/kmap_size.h>

#define FIXMAP_SIZE PGDIR_SIZE
#define PKMAP_SIZE PGDIR_SIZE

/* start after vmalloc area */
#define FIXMAP_BASE (PAGE_OFFSET - FIXMAP_SIZE - PKMAP_SIZE)
#define FIXMAP_SIZE PGDIR_SIZE /* only 1 PGD worth */
#define KM_TYPE_NR ((FIXMAP_SIZE >> PAGE_SHIFT)/NR_CPUS)
#define FIXMAP_ADDR(nr) (FIXMAP_BASE + ((nr) << PAGE_SHIFT))

#define FIX_KMAP_SLOTS (KM_MAX_IDX * NR_CPUS)
#define FIX_KMAP_BEGIN (0UL)
#define FIX_KMAP_END ((FIX_KMAP_BEGIN + FIX_KMAP_SLOTS) - 1)

#define FIXADDR_TOP (FIXMAP_BASE + (FIX_KMAP_END << PAGE_SHIFT))

/*
* This should be converted to the asm-generic version, but of course this
* is needlessly different from all other architectures. Sigh - tglx
*/
#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
#define __virt_to_fix(x) (((FIXADDR_TOP - ((x) & PAGE_MASK))) >> PAGE_SHIFT)

/* start after fixmap area */
#define PKMAP_BASE (FIXMAP_BASE + FIXMAP_SIZE)
#define PKMAP_SIZE PGDIR_SIZE
#define LAST_PKMAP (PKMAP_SIZE >> PAGE_SHIFT)
#define LAST_PKMAP_MASK (LAST_PKMAP - 1)
#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
Expand All @@ -29,11 +41,13 @@

extern void kmap_init(void);

#define arch_kmap_local_post_unmap(vaddr) \
local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE)

static inline void flush_cache_kmaps(void)
{
flush_cache_all();
}

#endif

#endif
14 changes: 0 additions & 14 deletions arch/arc/include/asm/kmap_types.h

This file was deleted.

54 changes: 5 additions & 49 deletions arch/arc/mm/highmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@
* This means each only has 1 PGDIR_SIZE worth of kvaddr mappings, which means
* 2M of kvaddr space for typical config (8K page and 11:8:13 traversal split)
*
* - fixmap anyhow needs a limited number of mappings. So 2M kvaddr == 256 PTE
* slots across NR_CPUS would be more than sufficient (generic code defines
* KM_TYPE_NR as 20).
* - The fixed KMAP slots for kmap_local/atomic() require KM_MAX_IDX slots per
* CPU. So the number of CPUs sharing a single PTE page is limited.
*
* - pkmap being preemptible, in theory could do with more than 256 concurrent
* mappings. However, generic pkmap code: map_new_virtual(), doesn't traverse
Expand All @@ -47,48 +46,6 @@
*/

extern pte_t * pkmap_page_table;
static pte_t * fixmap_page_table;

void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
{
int idx, cpu_idx;
unsigned long vaddr;

cpu_idx = kmap_atomic_idx_push();
idx = cpu_idx + KM_TYPE_NR * smp_processor_id();
vaddr = FIXMAP_ADDR(idx);

set_pte_at(&init_mm, vaddr, fixmap_page_table + idx,
mk_pte(page, prot));

return (void *)vaddr;
}
EXPORT_SYMBOL(kmap_atomic_high_prot);

void kunmap_atomic_high(void *kv)
{
unsigned long kvaddr = (unsigned long)kv;

if (kvaddr >= FIXMAP_BASE && kvaddr < (FIXMAP_BASE + FIXMAP_SIZE)) {

/*
* Because preemption is disabled, this vaddr can be associated
* with the current allocated index.
* But in case of multiple live kmap_atomic(), it still relies on
* callers to unmap in right order.
*/
int cpu_idx = kmap_atomic_idx();
int idx = cpu_idx + KM_TYPE_NR * smp_processor_id();

WARN_ON(kvaddr != FIXMAP_ADDR(idx));

pte_clear(&init_mm, kvaddr, fixmap_page_table + idx);
local_flush_tlb_kernel_range(kvaddr, kvaddr + PAGE_SIZE);

kmap_atomic_idx_pop();
}
}
EXPORT_SYMBOL(kunmap_atomic_high);

static noinline pte_t * __init alloc_kmap_pgtable(unsigned long kvaddr)
{
Expand All @@ -108,10 +65,9 @@ void __init kmap_init(void)
{
/* Due to recursive include hell, we can't do this in processor.h */
BUILD_BUG_ON(PAGE_OFFSET < (VMALLOC_END + FIXMAP_SIZE + PKMAP_SIZE));
BUILD_BUG_ON(LAST_PKMAP > PTRS_PER_PTE);
BUILD_BUG_ON(FIX_KMAP_SLOTS > PTRS_PER_PTE);

BUILD_BUG_ON(KM_TYPE_NR > PTRS_PER_PTE);
pkmap_page_table = alloc_kmap_pgtable(PKMAP_BASE);

BUILD_BUG_ON(LAST_PKMAP > PTRS_PER_PTE);
fixmap_page_table = alloc_kmap_pgtable(FIXMAP_BASE);
alloc_kmap_pgtable(FIXMAP_BASE);
}
1 change: 1 addition & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,7 @@ config HAVE_ARCH_PFN_VALID
config HIGHMEM
bool "High Memory Support"
depends on MMU
select KMAP_LOCAL
help
The address space of ARM processors is only 4 Gigabytes large
and it has to accommodate user address space, kernel address
Expand Down
4 changes: 2 additions & 2 deletions arch/arm/include/asm/fixmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
#define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE)

#include <linux/pgtable.h>
#include <asm/kmap_types.h>
#include <asm/kmap_size.h>

enum fixed_addresses {
FIX_EARLYCON_MEM_BASE,
__end_of_permanent_fixed_addresses,

FIX_KMAP_BEGIN = __end_of_permanent_fixed_addresses,
FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1,
FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1,

/* Support writing RO kernel text via kprobes, jump labels, etc. */
FIX_TEXT_POKE0,
Expand Down
34 changes: 24 additions & 10 deletions arch/arm/include/asm/highmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
#ifndef _ASM_HIGHMEM_H
#define _ASM_HIGHMEM_H

#include <asm/kmap_types.h>
#include <asm/cachetype.h>
#include <asm/fixmap.h>

#define PKMAP_BASE (PAGE_OFFSET - PMD_SIZE)
#define LAST_PKMAP PTRS_PER_PTE
Expand Down Expand Up @@ -46,19 +47,32 @@ extern pte_t *pkmap_page_table;

#ifdef ARCH_NEEDS_KMAP_HIGH_GET
extern void *kmap_high_get(struct page *page);
#else

static inline void *arch_kmap_local_high_get(struct page *page)
{
if (IS_ENABLED(CONFIG_DEBUG_HIGHMEM) && !cache_is_vivt())
return NULL;
return kmap_high_get(page);
}
#define arch_kmap_local_high_get arch_kmap_local_high_get

#else /* ARCH_NEEDS_KMAP_HIGH_GET */
static inline void *kmap_high_get(struct page *page)
{
return NULL;
}
#endif
#endif /* !ARCH_NEEDS_KMAP_HIGH_GET */

/*
* The following functions are already defined by <linux/highmem.h>
* when CONFIG_HIGHMEM is not set.
*/
#ifdef CONFIG_HIGHMEM
extern void *kmap_atomic_pfn(unsigned long pfn);
#endif
#define arch_kmap_local_post_map(vaddr, pteval) \
local_flush_tlb_kernel_page(vaddr)

#define arch_kmap_local_pre_unmap(vaddr) \
do { \
if (cache_is_vivt()) \
__cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); \
} while (0)

#define arch_kmap_local_post_unmap(vaddr) \
local_flush_tlb_kernel_page(vaddr)

#endif
10 changes: 0 additions & 10 deletions arch/arm/include/asm/kmap_types.h

This file was deleted.

1 change: 0 additions & 1 deletion arch/arm/mm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ obj-$(CONFIG_MODULES) += proc-syms.o
obj-$(CONFIG_DEBUG_VIRTUAL) += physaddr.o

obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o
obj-$(CONFIG_HIGHMEM) += highmem.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_ARM_PV_FIXUP) += pv-fixup-asm.o

Expand Down
Loading

0 comments on commit edd7ab7

Please sign in to comment.