* [PATCH 0/6] arch, mm/execmem: resolve confusion about set_direct_map_valid_noflush()
@ 2026-08-16 10:59 Mike Rapoport (Microsoft)
2026-08-16 10:59 ` [PATCH 1/6] set_memory: add number of pages parameter to set_direct_map APIs Mike Rapoport (Microsoft)
` (5 more replies)
0 siblings, 6 replies; 13+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-08-16 10:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Adrian Barnaś, Albert Ou, Alexander Gordeev, Alexandre Ghiti,
Andy Lutomirski, Borislav Petkov, Brendan Jackman,
Catalin Marinas, Christian Borntraeger, Dave Hansen,
David Hildenbrand, Gerald Schaefer, Heiko Carstens, Huacai Chen,
Ingo Molnar, Len Brown, Mike Rapoport, Palmer Dabbelt,
Paul Walmsley, Pavel Machek, Peter Zijlstra, H. Peter Anvin,
Rafael J. Wysocki, Ryan Roberts, Sven Schnelle, Thomas Gleixner,
Uladzislau Rezki, Vasily Gorbik, WANG Xuerui, Will Deacon, x86,
linux-arm-kernel, linux-kernel, linux-mm, linux-pm, linux-riscv,
linux-s390, loongarch
Recent discussion about implementation of execmem's ROX caches on arm64
revealed a confusion about how set_direct_map_valid_noflush()
implemented on different architectures.
On arm64 it sets or clears the PTE_VALID bit marking a PTE as present or
not present.
On other architectures it's a range version of
set_direct_map_invalid_noflush() and set_direct_map_default_noflush()
Unlike arm64::set_direct_map_valid_noflush(),
set_direct_map_default_noflush() not only marks PTE as present, but also
sets its default protection mode.
Other than that, initial design of execmem ROX caches didn't rely on
restoration of large mappings that's now available on x86, but
completely removed the memory allocated for the ROX cache from the
direct map to ensure that large mappings are not split. This precluded
usage of VM_FLUSH_RESET_PERMS for the ROX cache allocations and required
execmem to implement manipulation of the direct map alias.
Current implementation of ROX caches does not remove the direct map
alias but simply calls set_memory_rox() that updates the permissions in
both vmalloc address space and the direct map and relies on
collapse_large_pages() in x86 CPA to keep large mappings.
This allow using VM_FLUSH_RESET_PERMS for execmem ROX cache allocations
with small adjustments to set_direct_map APIs and vmalloc::reset_perms()
behaviour: adding number of pages parameter to set_direct_map APIs and
making resetting of the direct map permissions in vmalloc VMAP_HUGE
friendly.
Implement these adjustments, make execmem always use
VM_FLUSH_RESET_PERMS and revert set_direct_map_valid_noflush() changes.
[1] https://lore.kernel.org/all/20260611130144.1385343-4-abarnas@google.com
---
Mike Rapoport (Microsoft) (6):
set_memory: add number of pages parameter to set_direct_map APIs
mm/vmalloc: set area's page_order after allocation succeeds
mm/vmalloc: constify vm parameter of get_vm_area_page_order()
mm/vmalloc: make set_area_direct_map HUGE_VMAP friendly
mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations
Revert "arch: introduce set_direct_map_valid_noflush()"
arch/arm64/include/asm/set_memory.h | 5 ++--
arch/arm64/mm/pageattr.c | 24 ++++---------------
arch/loongarch/include/asm/set_memory.h | 5 ++--
arch/loongarch/mm/pageattr.c | 27 ++++-----------------
arch/riscv/include/asm/set_memory.h | 5 ++--
arch/riscv/mm/pageattr.c | 23 ++++--------------
arch/s390/include/asm/set_memory.h | 5 ++--
arch/s390/mm/pageattr.c | 20 ++++------------
arch/x86/include/asm/set_memory.h | 5 ++--
arch/x86/mm/pat/set_memory.c | 16 ++++---------
include/linux/set_memory.h | 12 ++++------
kernel/power/snapshot.c | 4 ++--
mm/execmem.c | 42 ++++++---------------------------
mm/secretmem.c | 6 ++---
mm/vmalloc.c | 31 ++++++++++++++++--------
mm/vmalloc.h | 2 +-
16 files changed, 68 insertions(+), 164 deletions(-)
---
base-commit: 1029098ee3275ea5b78e329ce132262affa2f8cc
change-id: 20260816-execmem-set-vm-perms-v0-2-bae847a4f64f
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/6] set_memory: add number of pages parameter to set_direct_map APIs
2026-08-16 10:59 [PATCH 0/6] arch, mm/execmem: resolve confusion about set_direct_map_valid_noflush() Mike Rapoport (Microsoft)
@ 2026-08-16 10:59 ` Mike Rapoport (Microsoft)
2026-08-16 11:12 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 2/6] mm/vmalloc: set area's page_order after allocation succeeds Mike Rapoport (Microsoft)
` (4 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-08-16 10:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Adrian Barnaś, Albert Ou, Alexander Gordeev, Alexandre Ghiti,
Andy Lutomirski, Borislav Petkov, Brendan Jackman,
Catalin Marinas, Christian Borntraeger, Dave Hansen,
David Hildenbrand, Gerald Schaefer, Heiko Carstens, Huacai Chen,
Ingo Molnar, Len Brown, Mike Rapoport, Palmer Dabbelt,
Paul Walmsley, Pavel Machek, Peter Zijlstra, H. Peter Anvin,
Rafael J. Wysocki, Ryan Roberts, Sven Schnelle, Thomas Gleixner,
Uladzislau Rezki, Vasily Gorbik, WANG Xuerui, Will Deacon, x86,
linux-arm-kernel, linux-kernel, linux-mm, linux-pm, linux-riscv,
linux-s390, loongarch
When set_direct_map APIs were introduced by the commit d253ca0c3865
("x86/mm/cpa: Add set_direct_map_*() functions") the single page
parameter made sense because the initial callers (vmalloc and
hibernation) had sets of unsorted struct pages that required changes of
their mappings in the direct map.
Since there is an increasing demand for direct map manipulation and it
is also desirable to be able to update larger physically contiguous
mappings, for example an entire large folio, extend set_direct_map APIs
to receive number of pages parameter.
As there is still only a handful of callers, change the existing
functions directly and update all the call sites rather than adding
wrappers for single page case.
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
arch/arm64/include/asm/set_memory.h | 4 ++--
arch/arm64/mm/pageattr.c | 8 ++++----
arch/loongarch/include/asm/set_memory.h | 4 ++--
arch/loongarch/mm/pageattr.c | 8 ++++----
arch/riscv/include/asm/set_memory.h | 4 ++--
arch/riscv/mm/pageattr.c | 8 ++++----
arch/s390/include/asm/set_memory.h | 4 ++--
arch/s390/mm/pageattr.c | 8 ++++----
arch/x86/include/asm/set_memory.h | 4 ++--
arch/x86/mm/pat/set_memory.c | 8 ++++----
include/linux/set_memory.h | 6 ++++--
kernel/power/snapshot.c | 4 ++--
mm/secretmem.c | 6 +++---
mm/vmalloc.c | 5 +++--
14 files changed, 42 insertions(+), 39 deletions(-)
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
index 90f61b17275e..b07fd4e026ea 100644
--- a/arch/arm64/include/asm/set_memory.h
+++ b/arch/arm64/include/asm/set_memory.h
@@ -11,8 +11,8 @@ bool can_set_direct_map(void);
int set_memory_valid(unsigned long addr, int numpages, int enable);
-int set_direct_map_invalid_noflush(struct page *page);
-int set_direct_map_default_noflush(struct page *page);
+int set_direct_map_invalid_noflush(struct page *page, unsigned int numpages);
+int set_direct_map_default_noflush(struct page *page, unsigned int numpages);
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c..db8d60a84d14 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -251,7 +251,7 @@ int set_memory_valid(unsigned long addr, int numpages, int enable)
__pgprot(PTE_PRESENT_VALID_KERNEL));
}
-int set_direct_map_invalid_noflush(struct page *page)
+int set_direct_map_invalid_noflush(struct page *page, unsigned int numpages)
{
pgprot_t clear_mask = __pgprot(PTE_PRESENT_VALID_KERNEL);
pgprot_t set_mask = __pgprot(PTE_PRESENT_INVALID);
@@ -260,10 +260,10 @@ int set_direct_map_invalid_noflush(struct page *page)
return 0;
return update_range_prot((unsigned long)page_address(page),
- PAGE_SIZE, set_mask, clear_mask);
+ PAGE_SIZE * numpages, set_mask, clear_mask);
}
-int set_direct_map_default_noflush(struct page *page)
+int set_direct_map_default_noflush(struct page *page, unsigned int numpages)
{
pgprot_t set_mask = __pgprot(PTE_PRESENT_VALID_KERNEL | PTE_WRITE);
pgprot_t clear_mask = __pgprot(PTE_PRESENT_INVALID | PTE_RDONLY);
@@ -272,7 +272,7 @@ int set_direct_map_default_noflush(struct page *page)
return 0;
return update_range_prot((unsigned long)page_address(page),
- PAGE_SIZE, set_mask, clear_mask);
+ PAGE_SIZE * numpages, set_mask, clear_mask);
}
static int __set_memory_enc_dec(unsigned long addr,
diff --git a/arch/loongarch/include/asm/set_memory.h b/arch/loongarch/include/asm/set_memory.h
index 55dfaefd02c8..563aab92896e 100644
--- a/arch/loongarch/include/asm/set_memory.h
+++ b/arch/loongarch/include/asm/set_memory.h
@@ -15,8 +15,8 @@ int set_memory_ro(unsigned long addr, int numpages);
int set_memory_rw(unsigned long addr, int numpages);
bool kernel_page_present(struct page *page);
-int set_direct_map_default_noflush(struct page *page);
-int set_direct_map_invalid_noflush(struct page *page);
+int set_direct_map_default_noflush(struct page *page, unsigned int nr);
+int set_direct_map_invalid_noflush(struct page *page, unsigned int nr);
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
#endif /* _ASM_LOONGARCH_SET_MEMORY_H */
diff --git a/arch/loongarch/mm/pageattr.c b/arch/loongarch/mm/pageattr.c
index 614ccc7afccb..43ad2a104f19 100644
--- a/arch/loongarch/mm/pageattr.c
+++ b/arch/loongarch/mm/pageattr.c
@@ -198,24 +198,24 @@ bool kernel_page_present(struct page *page)
return pte_present(ptep_get(pte));
}
-int set_direct_map_default_noflush(struct page *page)
+int set_direct_map_default_noflush(struct page *page, unsigned int nr)
{
unsigned long addr = (unsigned long)page_address(page);
if (addr < vm_map_base)
return 0;
- return __set_memory(addr, 1, PAGE_KERNEL, __pgprot(0));
+ return __set_memory(addr, nr, PAGE_KERNEL, __pgprot(0));
}
-int set_direct_map_invalid_noflush(struct page *page)
+int set_direct_map_invalid_noflush(struct page *page, unsigned int nr)
{
unsigned long addr = (unsigned long)page_address(page);
if (addr < vm_map_base)
return 0;
- return __set_memory(addr, 1, __pgprot(0), __pgprot(_PAGE_PRESENT | _PAGE_VALID));
+ return __set_memory(addr, nr, __pgprot(0), __pgprot(_PAGE_PRESENT | _PAGE_VALID));
}
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
diff --git a/arch/riscv/include/asm/set_memory.h b/arch/riscv/include/asm/set_memory.h
index ef59e1716a2c..db1d0ed82b69 100644
--- a/arch/riscv/include/asm/set_memory.h
+++ b/arch/riscv/include/asm/set_memory.h
@@ -40,8 +40,8 @@ static inline int set_kernel_memory(char *startp, char *endp,
}
#endif
-int set_direct_map_invalid_noflush(struct page *page);
-int set_direct_map_default_noflush(struct page *page);
+int set_direct_map_invalid_noflush(struct page *page, unsigned int nr);
+int set_direct_map_default_noflush(struct page *page, unsigned int nr);
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c
index 3f76db3d2769..20ef95b1d0c3 100644
--- a/arch/riscv/mm/pageattr.c
+++ b/arch/riscv/mm/pageattr.c
@@ -374,15 +374,15 @@ int set_memory_nx(unsigned long addr, int numpages)
return __set_memory(addr, numpages, __pgprot(0), __pgprot(_PAGE_EXEC));
}
-int set_direct_map_invalid_noflush(struct page *page)
+int set_direct_map_invalid_noflush(struct page *page, unsigned int nr)
{
- return __set_memory((unsigned long)page_address(page), 1,
+ return __set_memory((unsigned long)page_address(page), nr,
__pgprot(0), __pgprot(_PAGE_PRESENT));
}
-int set_direct_map_default_noflush(struct page *page)
+int set_direct_map_default_noflush(struct page *page, unsigned int nr)
{
- return __set_memory((unsigned long)page_address(page), 1,
+ return __set_memory((unsigned long)page_address(page), nr,
PAGE_KERNEL, __pgprot(_PAGE_EXEC));
}
diff --git a/arch/s390/include/asm/set_memory.h b/arch/s390/include/asm/set_memory.h
index 94092f4ae764..6b0aa9147ed8 100644
--- a/arch/s390/include/asm/set_memory.h
+++ b/arch/s390/include/asm/set_memory.h
@@ -60,8 +60,8 @@ __SET_MEMORY_FUNC(set_memory_rox, SET_MEMORY_RO | SET_MEMORY_X)
__SET_MEMORY_FUNC(set_memory_rwnx, SET_MEMORY_RW | SET_MEMORY_NX)
__SET_MEMORY_FUNC(set_memory_4k, SET_MEMORY_4K)
-int set_direct_map_invalid_noflush(struct page *page);
-int set_direct_map_default_noflush(struct page *page);
+int set_direct_map_invalid_noflush(struct page *page, unsigned int nr);
+int set_direct_map_default_noflush(struct page *page, unsigned int nr);
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c
index e6f788696dd1..02d992b251cb 100644
--- a/arch/s390/mm/pageattr.c
+++ b/arch/s390/mm/pageattr.c
@@ -387,14 +387,14 @@ int __set_memory(unsigned long addr, unsigned long numpages, unsigned long flags
return rc;
}
-int set_direct_map_invalid_noflush(struct page *page)
+int set_direct_map_invalid_noflush(struct page *page, unsigned int nr)
{
- return __set_memory((unsigned long)page_to_virt(page), 1, SET_MEMORY_INV);
+ return __set_memory((unsigned long)page_to_virt(page), nr, SET_MEMORY_INV);
}
-int set_direct_map_default_noflush(struct page *page)
+int set_direct_map_default_noflush(struct page *page, unsigned int nr)
{
- return __set_memory((unsigned long)page_to_virt(page), 1, SET_MEMORY_DEF);
+ return __set_memory((unsigned long)page_to_virt(page), nr, SET_MEMORY_DEF);
}
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
diff --git a/arch/x86/include/asm/set_memory.h b/arch/x86/include/asm/set_memory.h
index 4362c26aa992..0c4235d159f4 100644
--- a/arch/x86/include/asm/set_memory.h
+++ b/arch/x86/include/asm/set_memory.h
@@ -86,8 +86,8 @@ int set_pages_wb(struct page *page, int numpages);
int set_pages_ro(struct page *page, int numpages);
int set_pages_rw(struct page *page, int numpages);
-int set_direct_map_invalid_noflush(struct page *page);
-int set_direct_map_default_noflush(struct page *page);
+int set_direct_map_invalid_noflush(struct page *page, unsigned int nr);
+int set_direct_map_default_noflush(struct page *page, unsigned int nr);
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
index 422ce7fba00c..9022354e26e9 100644
--- a/arch/x86/mm/pat/set_memory.c
+++ b/arch/x86/mm/pat/set_memory.c
@@ -2673,14 +2673,14 @@ static int __set_pages_np(struct page *page, int numpages)
return __change_page_attr_set_clr(&cpa, 1);
}
-int set_direct_map_invalid_noflush(struct page *page)
+int set_direct_map_invalid_noflush(struct page *page, unsigned int nr)
{
- return __set_pages_np(page, 1);
+ return __set_pages_np(page, nr);
}
-int set_direct_map_default_noflush(struct page *page)
+int set_direct_map_default_noflush(struct page *page, unsigned int nr)
{
- return __set_pages_p(page, 1);
+ return __set_pages_p(page, nr);
}
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h
index 3030d9245f5a..0b77f1d7d8b9 100644
--- a/include/linux/set_memory.h
+++ b/include/linux/set_memory.h
@@ -25,11 +25,13 @@ static inline int set_memory_rox(unsigned long addr, int numpages)
#endif
#ifndef CONFIG_ARCH_HAS_SET_DIRECT_MAP
-static inline int set_direct_map_invalid_noflush(struct page *page)
+static inline int set_direct_map_invalid_noflush(struct page *page,
+ unsigned int nr)
{
return 0;
}
-static inline int set_direct_map_default_noflush(struct page *page)
+static inline int set_direct_map_default_noflush(struct page *page,
+ unsigned int nr)
{
return 0;
}
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index d933b5b2c05d..89a8894dd260 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -88,7 +88,7 @@ static inline int hibernate_restore_unprotect_page(void *page_address) {return 0
static inline void hibernate_map_page(struct page *page)
{
if (IS_ENABLED(CONFIG_ARCH_HAS_SET_DIRECT_MAP)) {
- int ret = set_direct_map_default_noflush(page);
+ int ret = set_direct_map_default_noflush(page, 1);
if (ret)
pr_warn_once("Failed to remap page\n");
@@ -101,7 +101,7 @@ static inline void hibernate_unmap_page(struct page *page)
{
if (IS_ENABLED(CONFIG_ARCH_HAS_SET_DIRECT_MAP)) {
unsigned long addr = (unsigned long)page_address(page);
- int ret = set_direct_map_invalid_noflush(page);
+ int ret = set_direct_map_invalid_noflush(page, 1);
if (ret)
pr_warn_once("Failed to remap page\n");
diff --git a/mm/secretmem.c b/mm/secretmem.c
index d29865075b6e..b073b780bd10 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -72,7 +72,7 @@ static vm_fault_t secretmem_fault(struct vm_fault *vmf)
goto out;
}
- err = set_direct_map_invalid_noflush(folio_page(folio, 0));
+ err = set_direct_map_invalid_noflush(folio_page(folio, 0), 1);
if (err) {
folio_put(folio);
ret = vmf_error(err);
@@ -87,7 +87,7 @@ static vm_fault_t secretmem_fault(struct vm_fault *vmf)
* already happened when we marked the page invalid
* which guarantees that this call won't fail
*/
- set_direct_map_default_noflush(folio_page(folio, 0));
+ set_direct_map_default_noflush(folio_page(folio, 0), 1);
folio_put(folio);
if (err == -EEXIST)
goto retry;
@@ -151,7 +151,7 @@ static int secretmem_migrate_folio(struct address_space *mapping,
static void secretmem_free_folio(struct folio *folio)
{
- set_direct_map_default_noflush(folio_page(folio, 0));
+ set_direct_map_default_noflush(folio_page(folio, 0), 1);
folio_zero_segment(folio, 0, folio_size(folio));
}
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index bea9f76ed7e7..22566e0b6e38 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3358,14 +3358,15 @@ struct vm_struct *remove_vm_area(const void *addr)
}
static inline void set_area_direct_map(const struct vm_struct *area,
- int (*set_direct_map)(struct page *page))
+ int (*set_direct_map)(struct page *page,
+ unsigned int nr))
{
unsigned long i;
/* HUGE_VMALLOC passes small pages to set_direct_map */
for (i = 0; i < area->nr_pages; i++)
if (page_address(area->pages[i]))
- set_direct_map(area->pages[i]);
+ set_direct_map(area->pages[i], 1);
}
/*
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/6] mm/vmalloc: set area's page_order after allocation succeeds
2026-08-16 10:59 [PATCH 0/6] arch, mm/execmem: resolve confusion about set_direct_map_valid_noflush() Mike Rapoport (Microsoft)
2026-08-16 10:59 ` [PATCH 1/6] set_memory: add number of pages parameter to set_direct_map APIs Mike Rapoport (Microsoft)
@ 2026-08-16 10:59 ` Mike Rapoport (Microsoft)
2026-08-16 11:08 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 3/6] mm/vmalloc: constify vm parameter of get_vm_area_page_order() Mike Rapoport (Microsoft)
` (3 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-08-16 10:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Adrian Barnaś, Albert Ou, Alexander Gordeev, Alexandre Ghiti,
Andy Lutomirski, Borislav Petkov, Brendan Jackman,
Catalin Marinas, Christian Borntraeger, Dave Hansen,
David Hildenbrand, Gerald Schaefer, Heiko Carstens, Huacai Chen,
Ingo Molnar, Len Brown, Mike Rapoport, Palmer Dabbelt,
Paul Walmsley, Pavel Machek, Peter Zijlstra, H. Peter Anvin,
Rafael J. Wysocki, Ryan Roberts, Sven Schnelle, Thomas Gleixner,
Uladzislau Rezki, Vasily Gorbik, WANG Xuerui, Will Deacon, x86,
linux-arm-kernel, linux-kernel, linux-mm, linux-pm, linux-riscv,
linux-s390, loongarch
__vmalloc_area_node() calls set_vm_area_page_order() to set area's
page_order before actually allocating pages to populate the area.
If allocation of large pages in HUGE_VMAP case fails midway, this leaves
the area with elevated page_order throughout the cleanup path.
There is no actual issue with this because the only place that currently
relies on area->page_order on the cleanup path is the loop calculating
the direct map alias range in vm_reset_perms() and it anyway skips
unpopulated pages.
But having set_vm_area_page_order() in the middle of __vmalloc_area_node()
makes things very obscure, hard to reason about and error prone against
future changes of the cleanup path.
Move the call to set_vm_area_page_order() after __vmalloc_area_node()
succeeded where page order is guaranteed.
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
mm/vmalloc.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 22566e0b6e38..6822f0fe9583 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3901,8 +3901,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
goto fail;
}
- set_vm_area_page_order(area, page_shift - PAGE_SHIFT);
- page_order = vm_area_page_order(area);
+ page_order = page_shift - PAGE_SHIFT;
/*
* High-order nofail allocations are really expensive and
@@ -4106,6 +4105,14 @@ void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align,
if (!ret)
goto fail;
+ /*
+ * Set area->page_order once it's known exactly that the order of the
+ * pages the area contains.
+ * Even if we succeeded to partially populate the area with large pages,
+ * still treat the area as populated with order-0 pages.
+ */
+ set_vm_area_page_order(area, shift - PAGE_SHIFT);
+
/*
* Mark the pages as accessible, now that they are mapped.
* The condition for setting KASAN_VMALLOC_INIT should complement the
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 3/6] mm/vmalloc: constify vm parameter of get_vm_area_page_order()
2026-08-16 10:59 [PATCH 0/6] arch, mm/execmem: resolve confusion about set_direct_map_valid_noflush() Mike Rapoport (Microsoft)
2026-08-16 10:59 ` [PATCH 1/6] set_memory: add number of pages parameter to set_direct_map APIs Mike Rapoport (Microsoft)
2026-08-16 10:59 ` [PATCH 2/6] mm/vmalloc: set area's page_order after allocation succeeds Mike Rapoport (Microsoft)
@ 2026-08-16 10:59 ` Mike Rapoport (Microsoft)
2026-08-16 11:03 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 4/6] mm/vmalloc: make set_area_direct_map HUGE_VMAP friendly Mike Rapoport (Microsoft)
` (2 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-08-16 10:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Adrian Barnaś, Albert Ou, Alexander Gordeev, Alexandre Ghiti,
Andy Lutomirski, Borislav Petkov, Brendan Jackman,
Catalin Marinas, Christian Borntraeger, Dave Hansen,
David Hildenbrand, Gerald Schaefer, Heiko Carstens, Huacai Chen,
Ingo Molnar, Len Brown, Mike Rapoport, Palmer Dabbelt,
Paul Walmsley, Pavel Machek, Peter Zijlstra, H. Peter Anvin,
Rafael J. Wysocki, Ryan Roberts, Sven Schnelle, Thomas Gleixner,
Uladzislau Rezki, Vasily Gorbik, WANG Xuerui, Will Deacon, x86,
linux-arm-kernel, linux-kernel, linux-mm, linux-pm, linux-riscv,
linux-s390, loongarch
get_vm_area_page_order() and vm_area_page_order() do not need to modify
struct vm_struct passed to them.
Constify the parameter.
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
mm/vmalloc.c | 4 ++--
mm/vmalloc.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 6822f0fe9583..fc7993db4152 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3124,7 +3124,7 @@ EXPORT_SYMBOL(vm_map_ram);
static struct vm_struct *vmlist __initdata;
-static inline unsigned int vm_area_page_order(struct vm_struct *vm)
+static inline unsigned int vm_area_page_order(const struct vm_struct *vm)
{
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
return vm->page_order;
@@ -3133,7 +3133,7 @@ static inline unsigned int vm_area_page_order(struct vm_struct *vm)
#endif
}
-unsigned int get_vm_area_page_order(struct vm_struct *vm)
+unsigned int get_vm_area_page_order(const struct vm_struct *vm)
{
return vm_area_page_order(vm);
}
diff --git a/mm/vmalloc.h b/mm/vmalloc.h
index 8866ddcff668..211869f36509 100644
--- a/mm/vmalloc.h
+++ b/mm/vmalloc.h
@@ -12,7 +12,7 @@ void __init vmalloc_init(void);
int __must_check vmap_pages_range_noflush(unsigned long addr, unsigned long end,
pgprot_t prot, struct page **pages,
unsigned int page_shift, gfp_t gfp_mask);
-unsigned int get_vm_area_page_order(struct vm_struct *vm);
+unsigned int get_vm_area_page_order(const struct vm_struct *vm);
#else
static inline void vmalloc_init(void) {}
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 4/6] mm/vmalloc: make set_area_direct_map HUGE_VMAP friendly
2026-08-16 10:59 [PATCH 0/6] arch, mm/execmem: resolve confusion about set_direct_map_valid_noflush() Mike Rapoport (Microsoft)
` (2 preceding siblings ...)
2026-08-16 10:59 ` [PATCH 3/6] mm/vmalloc: constify vm parameter of get_vm_area_page_order() Mike Rapoport (Microsoft)
@ 2026-08-16 10:59 ` Mike Rapoport (Microsoft)
2026-08-16 11:10 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 5/6] mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations Mike Rapoport (Microsoft)
2026-08-16 10:59 ` [PATCH 6/6] Revert "arch: introduce set_direct_map_valid_noflush()" Mike Rapoport (Microsoft)
5 siblings, 1 reply; 13+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-08-16 10:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Adrian Barnaś, Albert Ou, Alexander Gordeev, Alexandre Ghiti,
Andy Lutomirski, Borislav Petkov, Brendan Jackman,
Catalin Marinas, Christian Borntraeger, Dave Hansen,
David Hildenbrand, Gerald Schaefer, Heiko Carstens, Huacai Chen,
Ingo Molnar, Len Brown, Mike Rapoport, Palmer Dabbelt,
Paul Walmsley, Pavel Machek, Peter Zijlstra, H. Peter Anvin,
Rafael J. Wysocki, Ryan Roberts, Sven Schnelle, Thomas Gleixner,
Uladzislau Rezki, Vasily Gorbik, WANG Xuerui, Will Deacon, x86,
linux-arm-kernel, linux-kernel, linux-mm, linux-pm, linux-riscv,
linux-s390, loongarch
set_area_direct_map() always updates direct map alias permissions in
single page increments.
For HUGE_VMAP areas it's suboptimal. Not only the loop in
set_area_direct_map() needlessly has more iterations (e.g times 512 on
x86), but it also causes fragmentation of the direct map that could be
avoided for the HUGE_VMAP areas populated with large pages.
All pages in an area are always of the same order: either same-order
large pages when VM_ALLOW_HUGE_VMAP is set and all huge pages were
successfully allocated, or order-0 page when VM_ALLOW_HUGE_VMAP is
cleared or when huge pages allocation fails and fallback path is taken.
Instead of updating the direct map permissions for every order-0 page in
an area, use the area's page_order as the loop increment and update the
large pages in one call to set_direct_map_{invalid,default}_noflush().
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
mm/vmalloc.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index fc7993db4152..11170d1ee5be 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3361,12 +3361,15 @@ static inline void set_area_direct_map(const struct vm_struct *area,
int (*set_direct_map)(struct page *page,
unsigned int nr))
{
- unsigned long i;
+ unsigned int nr = (1U << vm_area_page_order(area));
+
+ for (unsigned long i = 0; i < area->nr_pages; i += nr) {
+ if (page_address(area->pages[i])) {
+ int err = set_direct_map(area->pages[i], nr);
- /* HUGE_VMALLOC passes small pages to set_direct_map */
- for (i = 0; i < area->nr_pages; i++)
- if (page_address(area->pages[i]))
- set_direct_map(area->pages[i], 1);
+ WARN_ON_ONCE(err);
+ }
+ }
}
/*
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 5/6] mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations
2026-08-16 10:59 [PATCH 0/6] arch, mm/execmem: resolve confusion about set_direct_map_valid_noflush() Mike Rapoport (Microsoft)
` (3 preceding siblings ...)
2026-08-16 10:59 ` [PATCH 4/6] mm/vmalloc: make set_area_direct_map HUGE_VMAP friendly Mike Rapoport (Microsoft)
@ 2026-08-16 10:59 ` Mike Rapoport (Microsoft)
2026-08-16 11:17 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 6/6] Revert "arch: introduce set_direct_map_valid_noflush()" Mike Rapoport (Microsoft)
5 siblings, 1 reply; 13+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-08-16 10:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Adrian Barnaś, Albert Ou, Alexander Gordeev, Alexandre Ghiti,
Andy Lutomirski, Borislav Petkov, Brendan Jackman,
Catalin Marinas, Christian Borntraeger, Dave Hansen,
David Hildenbrand, Gerald Schaefer, Heiko Carstens, Huacai Chen,
Ingo Molnar, Len Brown, Mike Rapoport, Palmer Dabbelt,
Paul Walmsley, Pavel Machek, Peter Zijlstra, H. Peter Anvin,
Rafael J. Wysocki, Ryan Roberts, Sven Schnelle, Thomas Gleixner,
Uladzislau Rezki, Vasily Gorbik, WANG Xuerui, Will Deacon, x86,
linux-arm-kernel, linux-kernel, linux-mm, linux-pm, linux-riscv,
linux-s390, loongarch
Initially execmem completely removed direct map alias for the memory
allocated for the ROX cache in PMD_SIZE chunks. When that memory was
freed, its direct map was restored also in PMD_SIZE chunks to avoid
fragmentation of the direct map caused by vmalloc::vm_reset_perms().
This required execmem to implement the wrappers for set_direct_map APIs for
proper sequencing of removal and restoration of the direct map aliases.
Since then x86's CPA gained support for collapsing the direct map page
tables for ROX pages and execmem switched from removing ROX caches from the
direct map to making them ROX there, so execmem only needs to update direct
map alias permissions when freeing the ROX cache memory.
vmalloc already handles those updates for areas with VM_FLUSH_RESET_PERMS
set and vmalloc::vm_reset_perms() does not force split of the direct map
for PMD_SIZE chunks.
Make all execmem vmalloc allocations use VM_FLUSH_RESET_PERMS and remove
custom wrappers for set_direct_map APIs.
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
mm/execmem.c | 42 +++++++-----------------------------------
1 file changed, 7 insertions(+), 35 deletions(-)
diff --git a/mm/execmem.c b/mm/execmem.c
index 74a178a87e75..d35f1d0ea54a 100644
--- a/mm/execmem.c
+++ b/mm/execmem.c
@@ -36,6 +36,7 @@ static void *execmem_vmalloc(struct execmem_range *range, size_t size,
unsigned long end = range->end;
void *p;
+ vm_flags |= VM_FLUSH_RESET_PERMS;
if (kasan)
vm_flags |= VM_DEFER_KMEMLEAK;
@@ -113,28 +114,6 @@ static inline unsigned long mas_range_len(struct ma_state *mas)
return mas->last - mas->index + 1;
}
-static int execmem_set_direct_map_valid(struct vm_struct *vm, bool valid)
-{
- unsigned int nr = (1 << get_vm_area_page_order(vm));
- unsigned int updated = 0;
- int err = 0;
-
- for (int i = 0; i < vm->nr_pages; i += nr) {
- err = set_direct_map_valid_noflush(vm->pages[i], nr, valid);
- if (err)
- goto err_restore;
- updated += nr;
- }
-
- return 0;
-
-err_restore:
- for (int i = 0; i < updated; i += nr)
- set_direct_map_valid_noflush(vm->pages[i], nr, !valid);
-
- return err;
-}
-
static int execmem_force_rw(void *ptr, size_t size)
{
unsigned int nr = PAGE_ALIGN(size) >> PAGE_SHIFT;
@@ -169,9 +148,6 @@ static void execmem_cache_clean(struct work_struct *work)
if (IS_ALIGNED(size, PMD_SIZE) &&
IS_ALIGNED(mas.index, PMD_SIZE)) {
- struct vm_struct *vm = find_vm_area(area);
-
- execmem_set_direct_map_valid(vm, true);
mas_store_gfp(&mas, NULL, GFP_KERNEL);
vfree(area);
}
@@ -312,18 +288,15 @@ static void *execmem_cache_populate_alloc(struct execmem_range *range, size_t si
*/
mutex_lock(mutex);
err = execmem_cache_add_locked(p, alloc_size, GFP_KERNEL);
- if (err)
- goto err_reset_direct_map;
-
- p = execmem_cache_alloc_locked(range, size);
-
+ if (!err)
+ p = execmem_cache_alloc_locked(range, size);
mutex_unlock(mutex);
+ if (err)
+ goto err_free_mem;
+
return p;
-err_reset_direct_map:
- mutex_unlock(mutex);
- execmem_set_direct_map_valid(vm, true);
err_free_mem:
vfree(p);
return NULL;
@@ -466,7 +439,6 @@ void *execmem_alloc(enum execmem_type type, size_t size)
{
struct execmem_range *range = &execmem_info->ranges[type];
bool use_cache = range->flags & EXECMEM_ROX_CACHE;
- unsigned long vm_flags = VM_FLUSH_RESET_PERMS;
pgprot_t pgprot = range->pgprot;
void *p = NULL;
@@ -475,7 +447,7 @@ void *execmem_alloc(enum execmem_type type, size_t size)
if (use_cache)
p = execmem_cache_alloc(range, size);
else
- p = execmem_vmalloc(range, size, pgprot, vm_flags);
+ p = execmem_vmalloc(range, size, pgprot, 0);
return kasan_reset_tag(p);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 6/6] Revert "arch: introduce set_direct_map_valid_noflush()"
2026-08-16 10:59 [PATCH 0/6] arch, mm/execmem: resolve confusion about set_direct_map_valid_noflush() Mike Rapoport (Microsoft)
` (4 preceding siblings ...)
2026-08-16 10:59 ` [PATCH 5/6] mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations Mike Rapoport (Microsoft)
@ 2026-08-16 10:59 ` Mike Rapoport (Microsoft)
2026-08-16 11:03 ` sashiko-bot
5 siblings, 1 reply; 13+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-08-16 10:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Adrian Barnaś, Albert Ou, Alexander Gordeev, Alexandre Ghiti,
Andy Lutomirski, Borislav Petkov, Brendan Jackman,
Catalin Marinas, Christian Borntraeger, Dave Hansen,
David Hildenbrand, Gerald Schaefer, Heiko Carstens, Huacai Chen,
Ingo Molnar, Len Brown, Mike Rapoport, Palmer Dabbelt,
Paul Walmsley, Pavel Machek, Peter Zijlstra, H. Peter Anvin,
Rafael J. Wysocki, Ryan Roberts, Sven Schnelle, Thomas Gleixner,
Uladzislau Rezki, Vasily Gorbik, WANG Xuerui, Will Deacon, x86,
linux-arm-kernel, linux-kernel, linux-mm, linux-pm, linux-riscv,
linux-s390, loongarch
Commit 0c6378a71574 ("arch: introduce set_direct_map_valid_noflush()")
added set_direct_map_valid_noflush() to allow updating the direct map
for a physically contiguous range in execmem.
As Brendan recently pointed out [1], this API is confusing because on
arm64 it means that is sets VALID bit in ptes, while on other
architectures it is an analog of set_direct_map_default_noflush().
The only user of set_direct_map_valid_noflush() was execmem's ROX cache
freeing path and it was switched to utilize VM_FLUSH_RESET_PERMS for
resetting permissions of the direct map alias.
With the last user gone and with set_direct_map_{invalid,default}_noflush()
accepting number of pages as a parameter, set_direct_map_valid_noflush()
become a copy of set_memory_valid() on arm64 and a duplicate of
set_direct_map_{invalid,default}_noflush() on other architecture, it is
safe to remove set_direct_map_valid_noflush().
Also drop a stale comment in arm64::__kernel_map_pages() that Linus
bothered to add when merging changes containing set_direct_map_valid_noflush()
to his tree.
This reverts commit 0c6378a71574daa6cd1534ad42a956e3262756c7.
[1] https://lore.kernel.org/all/DJ69RCVRBO0Y.3JCYSW50IC4RC@linux.dev
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
arch/arm64/include/asm/set_memory.h | 1 -
arch/arm64/mm/pageattr.c | 16 ----------------
arch/loongarch/include/asm/set_memory.h | 1 -
arch/loongarch/mm/pageattr.c | 19 -------------------
arch/riscv/include/asm/set_memory.h | 1 -
arch/riscv/mm/pageattr.c | 15 ---------------
arch/s390/include/asm/set_memory.h | 1 -
arch/s390/mm/pageattr.c | 12 ------------
arch/x86/include/asm/set_memory.h | 1 -
arch/x86/mm/pat/set_memory.c | 8 --------
include/linux/set_memory.h | 6 ------
11 files changed, 81 deletions(-)
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
index b07fd4e026ea..0091ba12200e 100644
--- a/arch/arm64/include/asm/set_memory.h
+++ b/arch/arm64/include/asm/set_memory.h
@@ -13,7 +13,6 @@ int set_memory_valid(unsigned long addr, int numpages, int enable);
int set_direct_map_invalid_noflush(struct page *page, unsigned int numpages);
int set_direct_map_default_noflush(struct page *page, unsigned int numpages);
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
int set_memory_encrypted(unsigned long addr, int numpages);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index db8d60a84d14..132938b32eb1 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -355,23 +355,7 @@ int realm_register_memory_enc_ops(void)
return arm64_mem_crypt_ops_register(&realm_crypt_ops);
}
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
-{
- unsigned long addr = (unsigned long)page_address(page);
-
- if (!can_set_direct_map())
- return 0;
-
- return set_memory_valid(addr, nr, valid);
-}
-
#ifdef CONFIG_DEBUG_PAGEALLOC
-/*
- * This is - apart from the return value - doing the same
- * thing as the new set_direct_map_valid_noflush() function.
- *
- * Unify? Explain the conceptual differences?
- */
void __kernel_map_pages(struct page *page, int numpages, int enable)
{
if (!can_set_direct_map())
diff --git a/arch/loongarch/include/asm/set_memory.h b/arch/loongarch/include/asm/set_memory.h
index 563aab92896e..4bb01172fbc2 100644
--- a/arch/loongarch/include/asm/set_memory.h
+++ b/arch/loongarch/include/asm/set_memory.h
@@ -17,6 +17,5 @@ int set_memory_rw(unsigned long addr, int numpages);
bool kernel_page_present(struct page *page);
int set_direct_map_default_noflush(struct page *page, unsigned int nr);
int set_direct_map_invalid_noflush(struct page *page, unsigned int nr);
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
#endif /* _ASM_LOONGARCH_SET_MEMORY_H */
diff --git a/arch/loongarch/mm/pageattr.c b/arch/loongarch/mm/pageattr.c
index 43ad2a104f19..a7dcff40f759 100644
--- a/arch/loongarch/mm/pageattr.c
+++ b/arch/loongarch/mm/pageattr.c
@@ -217,22 +217,3 @@ int set_direct_map_invalid_noflush(struct page *page, unsigned int nr)
return __set_memory(addr, nr, __pgprot(0), __pgprot(_PAGE_PRESENT | _PAGE_VALID));
}
-
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
-{
- unsigned long addr = (unsigned long)page_address(page);
- pgprot_t set, clear;
-
- if (addr < vm_map_base)
- return 0;
-
- if (valid) {
- set = PAGE_KERNEL;
- clear = __pgprot(0);
- } else {
- set = __pgprot(0);
- clear = __pgprot(_PAGE_PRESENT | _PAGE_VALID);
- }
-
- return __set_memory(addr, nr, set, clear);
-}
diff --git a/arch/riscv/include/asm/set_memory.h b/arch/riscv/include/asm/set_memory.h
index db1d0ed82b69..e9f9960c1947 100644
--- a/arch/riscv/include/asm/set_memory.h
+++ b/arch/riscv/include/asm/set_memory.h
@@ -42,7 +42,6 @@ static inline int set_kernel_memory(char *startp, char *endp,
int set_direct_map_invalid_noflush(struct page *page, unsigned int nr);
int set_direct_map_default_noflush(struct page *page, unsigned int nr);
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
#endif /* __ASSEMBLER__ */
diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c
index 20ef95b1d0c3..5b3cf326455d 100644
--- a/arch/riscv/mm/pageattr.c
+++ b/arch/riscv/mm/pageattr.c
@@ -386,21 +386,6 @@ int set_direct_map_default_noflush(struct page *page, unsigned int nr)
PAGE_KERNEL, __pgprot(_PAGE_EXEC));
}
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
-{
- pgprot_t set, clear;
-
- if (valid) {
- set = PAGE_KERNEL;
- clear = __pgprot(_PAGE_EXEC);
- } else {
- set = __pgprot(0);
- clear = __pgprot(_PAGE_PRESENT);
- }
-
- return __set_memory((unsigned long)page_address(page), nr, set, clear);
-}
-
#ifdef CONFIG_DEBUG_PAGEALLOC
static int debug_pagealloc_set_page(pte_t *pte, unsigned long addr, void *data)
{
diff --git a/arch/s390/include/asm/set_memory.h b/arch/s390/include/asm/set_memory.h
index 6b0aa9147ed8..e3562bf0c1aa 100644
--- a/arch/s390/include/asm/set_memory.h
+++ b/arch/s390/include/asm/set_memory.h
@@ -62,7 +62,6 @@ __SET_MEMORY_FUNC(set_memory_4k, SET_MEMORY_4K)
int set_direct_map_invalid_noflush(struct page *page, unsigned int nr);
int set_direct_map_default_noflush(struct page *page, unsigned int nr);
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
#endif
diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c
index 02d992b251cb..f46a666bc5c7 100644
--- a/arch/s390/mm/pageattr.c
+++ b/arch/s390/mm/pageattr.c
@@ -397,18 +397,6 @@ int set_direct_map_default_noflush(struct page *page, unsigned int nr)
return __set_memory((unsigned long)page_to_virt(page), nr, SET_MEMORY_DEF);
}
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
-{
- unsigned long flags;
-
- if (valid)
- flags = SET_MEMORY_DEF;
- else
- flags = SET_MEMORY_INV;
-
- return __set_memory((unsigned long)page_to_virt(page), nr, flags);
-}
-
bool kernel_page_present(struct page *page)
{
unsigned long addr;
diff --git a/arch/x86/include/asm/set_memory.h b/arch/x86/include/asm/set_memory.h
index 0c4235d159f4..39271a5ea925 100644
--- a/arch/x86/include/asm/set_memory.h
+++ b/arch/x86/include/asm/set_memory.h
@@ -88,7 +88,6 @@ int set_pages_rw(struct page *page, int numpages);
int set_direct_map_invalid_noflush(struct page *page, unsigned int nr);
int set_direct_map_default_noflush(struct page *page, unsigned int nr);
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
extern int kernel_set_to_readonly;
diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
index 9022354e26e9..a6b2912a4837 100644
--- a/arch/x86/mm/pat/set_memory.c
+++ b/arch/x86/mm/pat/set_memory.c
@@ -2683,14 +2683,6 @@ int set_direct_map_default_noflush(struct page *page, unsigned int nr)
return __set_pages_p(page, nr);
}
-int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
-{
- if (valid)
- return __set_pages_p(page, nr);
-
- return __set_pages_np(page, nr);
-}
-
#ifdef CONFIG_DEBUG_PAGEALLOC
void __kernel_map_pages(struct page *page, int numpages, int enable)
{
diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h
index 0b77f1d7d8b9..3fe293cfed8c 100644
--- a/include/linux/set_memory.h
+++ b/include/linux/set_memory.h
@@ -36,12 +36,6 @@ static inline int set_direct_map_default_noflush(struct page *page,
return 0;
}
-static inline int set_direct_map_valid_noflush(struct page *page,
- unsigned nr, bool valid)
-{
- return 0;
-}
-
static inline bool kernel_page_present(struct page *page)
{
return true;
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 3/6] mm/vmalloc: constify vm parameter of get_vm_area_page_order()
2026-08-16 10:59 ` [PATCH 3/6] mm/vmalloc: constify vm parameter of get_vm_area_page_order() Mike Rapoport (Microsoft)
@ 2026-08-16 11:03 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-16 11:03 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Christian Borntraeger, linux-s390, Alexander Gordeev,
Heiko Carstens, Vasily Gorbik
> get_vm_area_page_order() and vm_area_page_order() do not need to modify
> struct vm_struct passed to them.
>
> Constify the parameter.
>
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260816-execmem-set-vm-perms-v0-2-v1-0-90944a3ad43f@kernel.org?part=3
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 6/6] Revert "arch: introduce set_direct_map_valid_noflush()"
2026-08-16 10:59 ` [PATCH 6/6] Revert "arch: introduce set_direct_map_valid_noflush()" Mike Rapoport (Microsoft)
@ 2026-08-16 11:03 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-16 11:03 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Vasily Gorbik, linux-s390, Alexander Gordeev,
Christian Borntraeger, Heiko Carstens
> Commit 0c6378a71574 ("arch: introduce set_direct_map_valid_noflush()")
> added set_direct_map_valid_noflush() to allow updating the direct map
> for a physically contiguous range in execmem.
>
> As Brendan recently pointed out [1], this API is confusing because on
> arm64 it means that is sets VALID bit in ptes, while on other
> architectures it is an analog of set_direct_map_default_noflush().
>
> The only user of set_direct_map_valid_noflush() was execmem's ROX cache
> freeing path and it was switched to utilize VM_FLUSH_RESET_PERMS for
> resetting permissions of the direct map alias.
>
> With the last user gone and with set_direct_map_{invalid,default}_noflush()
> accepting number of pages as a parameter, set_direct_map_valid_noflush()
> become a copy of set_memory_valid() on arm64 and a duplicate of
> set_direct_map_{invalid,default}_noflush() on other architecture, it is
> safe to remove set_direct_map_valid_noflush().
>
> Also drop a stale comment in arm64::__kernel_map_pages() that Linus
> bothered to add when merging changes containing set_direct_map_valid_noflush()
> to his tree.
>
> This reverts commit 0c6378a71574daa6cd1534ad42a956e3262756c7.
>
> [1] https://lore.kernel.org/all/DJ69RCVRBO0Y.3JCYSW50IC4RC@linux.dev
>
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260816-execmem-set-vm-perms-v0-2-v1-0-90944a3ad43f@kernel.org?part=6
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/6] mm/vmalloc: set area's page_order after allocation succeeds
2026-08-16 10:59 ` [PATCH 2/6] mm/vmalloc: set area's page_order after allocation succeeds Mike Rapoport (Microsoft)
@ 2026-08-16 11:08 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-16 11:08 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev, linux-s390,
Christian Borntraeger
> __vmalloc_area_node() calls set_vm_area_page_order() to set area's
> page_order before actually allocating pages to populate the area.
>
> If allocation of large pages in HUGE_VMAP case fails midway, this leaves
> the area with elevated page_order throughout the cleanup path.
>
> There is no actual issue with this because the only place that currently
> relies on area->page_order on the cleanup path is the loop calculating
> the direct map alias range in vm_reset_perms() and it anyway skips
> unpopulated pages.
>
> But having set_vm_area_page_order() in the middle of __vmalloc_area_node()
> makes things very obscure, hard to reason about and error prone against
> future changes of the cleanup path.
>
> Move the call to set_vm_area_page_order() after __vmalloc_area_node()
> succeeded where page order is guaranteed.
>
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260816-execmem-set-vm-perms-v0-2-v1-0-90944a3ad43f@kernel.org?part=2
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/6] mm/vmalloc: make set_area_direct_map HUGE_VMAP friendly
2026-08-16 10:59 ` [PATCH 4/6] mm/vmalloc: make set_area_direct_map HUGE_VMAP friendly Mike Rapoport (Microsoft)
@ 2026-08-16 11:10 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-16 11:10 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
Christian Borntraeger
> set_area_direct_map() always updates direct map alias permissions in
> single page increments.
>
> For HUGE_VMAP areas it's suboptimal. Not only the loop in
> set_area_direct_map() needlessly has more iterations (e.g times 512 on
> x86), but it also causes fragmentation of the direct map that could be
> avoided for the HUGE_VMAP areas populated with large pages.
>
> All pages in an area are always of the same order: either same-order
> large pages when VM_ALLOW_HUGE_VMAP is set and all huge pages were
> successfully allocated, or order-0 page when VM_ALLOW_HUGE_VMAP is
> cleared or when huge pages allocation fails and fallback path is taken.
>
> Instead of updating the direct map permissions for every order-0 page in
> an area, use the area's page_order as the loop increment and update the
> large pages in one call to set_direct_map_{invalid,default}_noflush().
>
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260816-execmem-set-vm-perms-v0-2-v1-0-90944a3ad43f@kernel.org?part=4
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/6] set_memory: add number of pages parameter to set_direct_map APIs
2026-08-16 10:59 ` [PATCH 1/6] set_memory: add number of pages parameter to set_direct_map APIs Mike Rapoport (Microsoft)
@ 2026-08-16 11:12 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-16 11:12 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: linux-s390, Christian Borntraeger, Heiko Carstens, Vasily Gorbik,
Alexander Gordeev
> When set_direct_map APIs were introduced by the commit d253ca0c3865
> ("x86/mm/cpa: Add set_direct_map_*() functions") the single page
> parameter made sense because the initial callers (vmalloc and
> hibernation) had sets of unsorted struct pages that required changes of
> their mappings in the direct map.
>
> Since there is an increasing demand for direct map manipulation and it
> is also desirable to be able to update larger physically contiguous
> mappings, for example an entire large folio, extend set_direct_map APIs
> to receive number of pages parameter.
>
> As there is still only a handful of callers, change the existing
> functions directly and update all the call sites rather than adding
> wrappers for single page case.
>
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260816-execmem-set-vm-perms-v0-2-v1-0-90944a3ad43f@kernel.org?part=1
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 5/6] mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations
2026-08-16 10:59 ` [PATCH 5/6] mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations Mike Rapoport (Microsoft)
@ 2026-08-16 11:17 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-08-16 11:17 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Alexander Gordeev, Vasily Gorbik, Christian Borntraeger,
Heiko Carstens, linux-s390
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [Critical] execmem_cache_clean() calls vfree() on partial or interior chunks of a larger vmalloc allocation, causing a use-after-free for actively used regions.
- [High] Unchecked return value of mas_store_gfp() during maple tree deletion can leave a dangling pointer in the free tree if deletion fails.
- [Medium] Cache memory may never be freed because execmem_vmalloc() does not guarantee PMD_SIZE alignment on all architectures, preventing execmem_cache_clean() from matching its alignment checks.
--
--- Patch [5]: [PATCH 5/6] mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations ---
commit 32d82345ceeccc2881319ef4ff8f3be90e5de6df
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations
Initially execmem removed direct map aliases for ROX cache allocations and
restored them manually. This patch leverages vmalloc's VM_FLUSH_RESET_PERMS
to handle these updates without splitting the direct map for PMD_SIZE chunks,
removing the need for custom set_direct_map wrappers.
> diff --git a/mm/execmem.c b/mm/execmem.c
> index 74a178a87e758..d35f1d0ea54a4 100644
> --- a/mm/execmem.c
> +++ b/mm/execmem.c
[ ... ]
> @@ -169,9 +148,6 @@ static void execmem_cache_clean(struct work_struct *work)
>
> if (IS_ALIGNED(size, PMD_SIZE) &&
> IS_ALIGNED(mas.index, PMD_SIZE)) {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Severity: Medium]
This is a pre-existing issue, but does this strict alignment check prevent
unaligned vmalloc allocations from ever being cleaned up?
execmem_cache_populate_alloc() rounds the allocation size up to PMD_SIZE,
but calls execmem_vmalloc() with vm_flags |= VM_ALLOW_HUGE_VMAP. If the
architecture does not support arch_vmap_pmd_supported(PAGE_KERNEL),
__vmalloc_node_range() falls back to PAGE_SIZE alignment.
If the returned base address is not PMD_SIZE aligned, the free block's
index will never be PMD-aligned, potentially permanently leaking the cache
block even when it is completely empty.
> - struct vm_struct *vm = find_vm_area(area);
> -
> - execmem_set_direct_map_valid(vm, true);
> mas_store_gfp(&mas, NULL, GFP_KERNEL);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Severity: High]
This isn't a bug introduced by this patch, but what happens if
mas_store_gfp() fails here?
Modifying a maple tree under RCU can require allocating a new node. If this
node allocation fails and returns -ENOMEM, the entry is not removed from
free_areas.
Since the return value is ignored, the memory is subsequently freed via
vfree(area), leaving a dangling pointer in the free tree that could be
returned by a subsequent allocation.
> vfree(area);
^^^^^^^^^^^
[Severity: Critical]
This is a pre-existing issue, but does calling vfree() on a partial or
interior chunk of a larger vmalloc allocation cause a use-after-free for
actively used regions?
If a multi-PMD block is allocated (e.g., 4MB) and an interior PMD_SIZE free
block is formed (e.g., area = p + 2MB), execmem_cache_clean() blindly calls
vfree(area) on it.
However, vfree() relies on find_unlink_vmap_area(), which matches any
interior pointer to the containing vmap_area:
mm/vmalloc.c:find_unlink_vmap_area() {
...
va = __find_vmap_area(addr, &vn->busy.root);
...
}
Won't this completely unmap and free the entire original 4MB block,
destroying the actively used portions alongside the free chunk? This could
potentially be triggered by userspace loading and unloading BPF programs
or kernel modules of specific sizes.
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260816-execmem-set-vm-perms-v0-2-v1-0-90944a3ad43f@kernel.org?part=5
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-08-16 11:17 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-16 10:59 [PATCH 0/6] arch, mm/execmem: resolve confusion about set_direct_map_valid_noflush() Mike Rapoport (Microsoft)
2026-08-16 10:59 ` [PATCH 1/6] set_memory: add number of pages parameter to set_direct_map APIs Mike Rapoport (Microsoft)
2026-08-16 11:12 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 2/6] mm/vmalloc: set area's page_order after allocation succeeds Mike Rapoport (Microsoft)
2026-08-16 11:08 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 3/6] mm/vmalloc: constify vm parameter of get_vm_area_page_order() Mike Rapoport (Microsoft)
2026-08-16 11:03 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 4/6] mm/vmalloc: make set_area_direct_map HUGE_VMAP friendly Mike Rapoport (Microsoft)
2026-08-16 11:10 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 5/6] mm/execmem: use VM_FLUSH_RESET_PERMS for ROX cache allocations Mike Rapoport (Microsoft)
2026-08-16 11:17 ` sashiko-bot
2026-08-16 10:59 ` [PATCH 6/6] Revert "arch: introduce set_direct_map_valid_noflush()" Mike Rapoport (Microsoft)
2026-08-16 11:03 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox