mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
mm/execmem: free ROX cache chunks only when they span an entire vm area
Patch series "mm/execmem: fixes and cleanups for the ROX cache".
Sashiko review of the ROX cache refill path asked what happens when the
PMD_SIZE allocation fails there. Pulling that thread uncovered a bit more
than the fallback.
Patch 1 fixes a real bug, although rare bug: execmem_cache_clean() can
free a chunk that is still partially in use, because a PMD sized and PMD
aligned free range in the middle of a larger chunk looks exactly like a
chunk that nobody uses.
Patch 2 handles maple tree allocation failures in the cache. They are
unlikely, but silently dropping an area from the tree is not a great way
to deal with them.
Patch 3 deals with the fallback that started all this. The cache exists
to keep the direct map free of unnecessary splits, and the fallback
happily filled it with base page mapped areas that could never be freed
from the cache again. Ask vmalloc for a huge mapping or nothing, and
serve whatever does not fit outside the cache.
Patches 4 and 5 convert the ROX cache to scope based cleanup.
The series was tested on x86 with module load/unload cycles, with PTDUMP
confirming that the cache is using 2M mappings, and with nohugevmalloc to
exercise the uncached fallback.
This patch (of 5):
When execmem refills the ROX cache, it vmalloc()s multiples of PMD_SIZE
aligned to PMD_SIZE. For every such allocation vmalloc creates a vm area.
The first part of the vmalloc()ed chunk is returned to the allocation that
triggered the cache refill and the remaining part is added to the cache
and handed out for subsequent allocations with execmem_alloc().
When only the first part is freed, the entire vm area remains in the ROX
cache and can be handed out again.
In the case when the first allocation is larger than PMD_SIZE and the
second allocation from the freed first part of the chunk is exactly
PMD_SIZE, execmem_cache_clean() will free the entire chunk while part of
it is still in use.
For example:
/*
* vmalloc(4M), return p0 to the caller
* add [p0 + 3M, p0 + 4M) to the cache
*/
p0 = execmem_alloc(3M);
/* return p0 + 3M from the cache to the caller */
p1 = execmem_alloc(1M);
/* put [p0, p0 + 3M) back into the cache */
execmem_free(p0);
/* return p0 from the cache to the caller */
p2 = execmem_alloc(2M);
/* return p0 + 2M from the cache to the caller */
p3 = execmem_alloc(1M);
/* bah! execmem_cache_clean() frees the entire 4M chunk */
execmem_free(p2);
Make sure that the ranges that execmem_cache_clean() frees cover the
entire vm area.
Link: https://lore.kernel.org/20260903-execmem-rox-cache-pmd-v1-v1-0-11beb2a3d249@kernel.org
Link: https://lore.kernel.org/20260903-execmem-rox-cache-pmd-v1-v1-1-11beb2a3d249@kernel.org
Fixes: 2e45474ab1 ("execmem: add support for cache of large ROX pages")
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Assisted-by: copilot:claude-opus-5
Cc: Benjamin Tissoires <bentiss@kernel.org>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Luis Chamberalin <mcgrof@kernel.org>
Cc: "Uladzislau Rezki (Sony)" <urezki@gmail.com>
Cc: <stable@vger.kernel.org>
This commit is contained in:
parent
154c16a783
commit
52d95a55a0
|
|
@ -143,9 +143,11 @@ static void execmem_cache_clean(struct work_struct *work)
|
|||
|
||||
mutex_lock(mutex);
|
||||
mas_for_each(&mas, area, ULONG_MAX) {
|
||||
struct vm_struct *vm = find_vm_area(area);
|
||||
size_t size = mas_range_len(&mas);
|
||||
|
||||
if (IS_ALIGNED(size, PMD_SIZE) &&
|
||||
if (vm && get_vm_area_size(vm) == size &&
|
||||
IS_ALIGNED(size, PMD_SIZE) &&
|
||||
IS_ALIGNED(mas.index, PMD_SIZE)) {
|
||||
mas_store_gfp(&mas, NULL, GFP_KERNEL);
|
||||
vfree(area);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user