[PATCH] ARM: mm: use kmap_local_page() in copypage-v6
Danish Khateeb
danishkhateeb03 at gmail.com
Tue Sep 22 11:30:54 PDT 2026
kmap_atomic() is deprecated in favour of kmap_local_page(), as described
in Documentation/mm/highmem.rst.
ARMv6 and ARMv7 CPUs whose data cache does not alias use
v6_copy_user_highpage_nonaliasing() and
v6_clear_user_highpage_nonaliasing() as copy_user_highpage() and
clear_user_highpage(), to copy a page on a copy-on-write fault and to
zero new anonymous pages. Both map the pages, call copy_page() or
clear_page() and unmap them again, just like the generic copy_highpage()
and clear_highpage(), which use kmap_local_page() since commit
d2c20e51e396 ("mm/highmem: remove deprecated kmap_atomic").
Neither function needs preemption or page faults disabled: they only
access the kernel mappings of the pages, and do no cache maintenance.
kmap_atomic() is __kmap_local_page_prot() with page faults and
preemption (migration on PREEMPT_RT) disabled around it, so the mappings
are made and removed as before, and a highmem page is still mapped with
migration disabled.
Convert both functions to kmap_local_page() and kunmap_local(). Apart
from dropping the deprecated calls, this lets a preemptible kernel
preempt the copy or clear of a page, as it can in the generic helpers.
The aliasing variants use their own fixed mappings and are unchanged.
Assisted-by: LLM sparse
Signed-off-by: Danish Khateeb <danishkhateeb03 at gmail.com>
---
Notes:
Tested on QEMU virt (cortex-a15, 2 CPUs, 2 GB, of which 1.25 GB is
HighMem) with multi_v7_defconfig, which has HIGHMEM=y, plus PREEMPT,
DEBUG_PREEMPT, PROVE_LOCKING, DEBUG_ATOMIC_SLEEP and DEBUG_HIGHMEM, on
v7.3-rc4 with and without this patch. A test init checked every word of
every page after:
- copy-on-write of 8192 anonymous pages after fork(), written by the
child and then by the parent, and first writes to 2048 pages of a
MAP_PRIVATE file mapping (copy_user_highpage());
- write faults, and read-then-write faults, on 16384 new anonymous
pages each, right after 16384 pages were filled with a pattern and
freed; 89-98% of the new pages reused one of those frames
(clear_user_highpage()).
pagemap showed all the source pages, copies and new pages in HighMem,
and kprobes counted at least one call per page into each function.
Both kernels passed with no splats. With CPU-bound tasks on both CPUs,
a stack-trace histogram on preempt_schedule_irq() counted no
preemptions inside copy_page() or inside memset() under
v6_clear_user_highpage_nonaliasing() without the patch, and 9-54 and
40-95 with it (two runs). A control that flips a byte of each test page
in both functions made all five checks fail.
Also built with W=1 for imx_v6_v7_defconfig, which builds the aliasing
variants too.
The ARMv4/v5 copypage-*.c files also use kmap_atomic(). I've left them
alone: they do cache maintenance on VIVT caches while the pages are
mapped, and QEMU doesn't model caches, so I can't test them.
arch/arm/mm/copypage-v6.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c
index 0710dba5c0bf..81d8471dbf00 100644
--- a/arch/arm/mm/copypage-v6.c
+++ b/arch/arm/mm/copypage-v6.c
@@ -32,11 +32,11 @@ static void v6_copy_user_highpage_nonaliasing(struct page *to,
{
void *kto, *kfrom;
- kfrom = kmap_atomic(from);
- kto = kmap_atomic(to);
+ kfrom = kmap_local_page(from);
+ kto = kmap_local_page(to);
copy_page(kto, kfrom);
- kunmap_atomic(kto);
- kunmap_atomic(kfrom);
+ kunmap_local(kto);
+ kunmap_local(kfrom);
}
/*
@@ -45,9 +45,9 @@ static void v6_copy_user_highpage_nonaliasing(struct page *to,
*/
static void v6_clear_user_highpage_nonaliasing(struct page *page, unsigned long vaddr)
{
- void *kaddr = kmap_atomic(page);
+ void *kaddr = kmap_local_page(page);
clear_page(kaddr);
- kunmap_atomic(kaddr);
+ kunmap_local(kaddr);
}
/*
base-commit: 93f51579e7df248780214094418f205253383cc5
--
2.55.0
More information about the linux-arm-kernel
mailing list