* [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation
@ 2026-05-07 11:48 Jill Ravaliya
2026-05-07 11:48 ` [PATCH 2/2] selftests/mm: add test for vrealloc() shrink page freeing Jill Ravaliya
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Jill Ravaliya @ 2026-05-07 11:48 UTC (permalink / raw)
To: akpm, urezki; +Cc: linux-mm, linux-kernel, Jill Ravaliya
vrealloc() shrink path zeros unused memory and updates
vm->requested_size, but never frees the physical pages,
removes page table mappings, or flushes the TLB for the
unused range.
When a caller shrinks a vmalloc allocation, physical pages
backing the unused portion remain allocated until vfree()
is eventually called, wasting real RAM.
Fix this by unmapping the unused virtual range using
vunmap_range() which also flushes the TLB, freeing each
unused physical page back to the buddy allocator, and
updating vm->nr_pages to reflect the new page count.
Signed-off-by: Jill Ravaliya <jillravaliya@gmail.com>
---
mm/vmalloc.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index aa08651ec..a8cedfc5d 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -4336,6 +4336,27 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
memset((void *)p + size, 0, old_size - size);
vm->requested_size = size;
kasan_vrealloc(p, old_size, size);
+
+ /* Shrink the vm_area: unmap and free unused pages. */
+ if (size < alloced_size) {
+ unsigned long new_nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ unsigned long i;
+
+ /* Unmap unused virtual range and flush TLB. */
+ vunmap_range((unsigned long)p + PAGE_ALIGN(size),
+ (unsigned long)p + alloced_size);
+
+ /* Free unused physical pages back to buddy allocator. */
+ for (i = new_nr_pages; i < vm->nr_pages; i++) {
+ mod_lruvec_page_state(vm->pages[i],
+ NR_VMALLOC, -1);
+ __free_page(vm->pages[i]);
+ vm->pages[i] = NULL;
+ }
+
+ vm->nr_pages = new_nr_pages;
+ }
+
return (void *)p;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] selftests/mm: add test for vrealloc() shrink page freeing
2026-05-07 11:48 [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation Jill Ravaliya
@ 2026-05-07 11:48 ` Jill Ravaliya
2026-05-07 17:17 ` [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation Uladzislau Rezki
2026-05-07 20:26 ` [syzbot ci] " syzbot ci
2 siblings, 0 replies; 4+ messages in thread
From: Jill Ravaliya @ 2026-05-07 11:48 UTC (permalink / raw)
To: akpm, urezki; +Cc: linux-mm, linux-kernel, Jill Ravaliya
Add a selftest that verifies vrealloc() frees physical pages
when shrinking an allocation.
The test loads a kernel module that:
1. Allocates 10MB with vmalloc()
2. Touches all pages to force physical allocation
3. Shrinks to 2MB with vrealloc()
4. Verifies free page count increased after shrink
Without the fix, the test fails because no pages are freed.
With the fix applied, the test passes confirming ~2048 pages
are returned to the system after shrinking from 10MB to 2MB.
Tested on kernel 7.0.0 in QEMU.
Signed-off-by: Jill Ravaliya <jillravaliya@gmail.com>
---
tools/testing/selftests/mm/Makefile | 5 ++
.../selftests/mm/vrealloc_shrink_test.c | 65 +++++++++++++++++++
2 files changed, 70 insertions(+)
create mode 100644 tools/testing/selftests/mm/vrealloc_shrink_test.c
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index cd24596cd..4eab7c76c 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -104,6 +104,7 @@ TEST_GEN_FILES += hugetlb_dio
TEST_GEN_FILES += droppable
TEST_GEN_FILES += guard-regions
TEST_GEN_FILES += merge
+TEST_GEN_FILES += vrealloc_shrink_test
TEST_GEN_FILES += rmap
TEST_GEN_FILES += folio_split_race_test
@@ -282,3 +283,7 @@ warn_missing_page_frag:
echo "Warning: $(PAGE_FRAG_WARNING). page_frag test will be skipped." ; \
echo
endif
+
+# vrealloc shrink test module
+vrealloc_shrink_mod.ko: vrealloc_shrink_mod.c
+ $(MAKE) -C $(KDIR) M=$(PWD) modules
diff --git a/tools/testing/selftests/mm/vrealloc_shrink_test.c b/tools/testing/selftests/mm/vrealloc_shrink_test.c
new file mode 100644
index 000000000..cf4263074
--- /dev/null
+++ b/tools/testing/selftests/mm/vrealloc_shrink_test.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test that vrealloc() frees physical pages when shrinking.
+ *
+ * vrealloc() shrink path previously zeroed unused memory and updated
+ * vm->requested_size, but never freed the physical pages backing the
+ * unused portion of the allocation. This test verifies the fix by
+ * loading a kernel module that directly measures nr_pages before and
+ * after vrealloc() shrink.
+ *
+ * Copyright (C) 2026 Jill Ravaliya
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "../kselftest.h"
+
+#define MODULE_NAME "vrealloc_shrink_mod"
+#define DMESG_PASS "vrealloc_shrink: PASS"
+#define DMESG_FAIL "vrealloc_shrink: FAIL"
+
+static int run_cmd(const char *cmd)
+{
+ return system(cmd);
+}
+
+static int check_dmesg_for(const char *pattern)
+{
+ char cmd[256];
+ snprintf(cmd, sizeof(cmd),
+ "dmesg | grep -q '%s'", pattern);
+ return system(cmd) == 0;
+}
+
+int main(void)
+{
+ ksft_print_header();
+ ksft_set_plan(1);
+
+ /* Insert the test module */
+ if (run_cmd("insmod " MODULE_NAME ".ko") != 0) {
+ ksft_test_result_skip(
+ "could not load %s.ko - is it built?\n",
+ MODULE_NAME);
+ ksft_finished();
+ }
+
+ /* Check dmesg for pass/fail */
+ if (check_dmesg_for(DMESG_PASS)) {
+ ksft_test_result_pass(
+ "vrealloc shrink frees physical pages\n");
+ } else if (check_dmesg_for(DMESG_FAIL)) {
+ ksft_test_result_fail(
+ "vrealloc shrink did NOT free physical pages\n");
+ } else {
+ ksft_test_result_fail(
+ "could not find test result in dmesg\n");
+ }
+
+ run_cmd("rmmod " MODULE_NAME);
+ ksft_finished();
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation
2026-05-07 11:48 [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation Jill Ravaliya
2026-05-07 11:48 ` [PATCH 2/2] selftests/mm: add test for vrealloc() shrink page freeing Jill Ravaliya
@ 2026-05-07 17:17 ` Uladzislau Rezki
2026-05-07 20:26 ` [syzbot ci] " syzbot ci
2 siblings, 0 replies; 4+ messages in thread
From: Uladzislau Rezki @ 2026-05-07 17:17 UTC (permalink / raw)
To: Jill Ravaliya; +Cc: akpm, urezki, linux-mm, linux-kernel, Shivam Kalra
On Thu, May 07, 2026 at 05:18:53PM +0530, Jill Ravaliya wrote:
> vrealloc() shrink path zeros unused memory and updates
> vm->requested_size, but never frees the physical pages,
> removes page table mappings, or flushes the TLB for the
> unused range.
>
> When a caller shrinks a vmalloc allocation, physical pages
> backing the unused portion remain allocated until vfree()
> is eventually called, wasting real RAM.
>
> Fix this by unmapping the unused virtual range using
> vunmap_range() which also flushes the TLB, freeing each
> unused physical page back to the buddy allocator, and
> updating vm->nr_pages to reflect the new page count.
>
> Signed-off-by: Jill Ravaliya <jillravaliya@gmail.com>
> ---
> mm/vmalloc.c | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index aa08651ec..a8cedfc5d 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -4336,6 +4336,27 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
> memset((void *)p + size, 0, old_size - size);
> vm->requested_size = size;
> kasan_vrealloc(p, old_size, size);
> +
> + /* Shrink the vm_area: unmap and free unused pages. */
> + if (size < alloced_size) {
> + unsigned long new_nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
> + unsigned long i;
> +
> + /* Unmap unused virtual range and flush TLB. */
> + vunmap_range((unsigned long)p + PAGE_ALIGN(size),
> + (unsigned long)p + alloced_size);
> +
> + /* Free unused physical pages back to buddy allocator. */
> + for (i = new_nr_pages; i < vm->nr_pages; i++) {
> + mod_lruvec_page_state(vm->pages[i],
> + NR_VMALLOC, -1);
> + __free_page(vm->pages[i]);
> + vm->pages[i] = NULL;
> + }
> +
> + vm->nr_pages = new_nr_pages;
> + }
> +
> return (void *)p;
> }
>
> --
> 2.43.0
>
There is already work to address this: https://lore.kernel.org/all/20260428-vmalloc-shrink-v12-0-3c18c9172eb1@zohomail.in/
--
Uladzislau Rezki
^ permalink raw reply [flat|nested] 4+ messages in thread
* [syzbot ci] Re: mm/vmalloc: free unused pages when shrinking vrealloc() allocation
2026-05-07 11:48 [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation Jill Ravaliya
2026-05-07 11:48 ` [PATCH 2/2] selftests/mm: add test for vrealloc() shrink page freeing Jill Ravaliya
2026-05-07 17:17 ` [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation Uladzislau Rezki
@ 2026-05-07 20:26 ` syzbot ci
2 siblings, 0 replies; 4+ messages in thread
From: syzbot ci @ 2026-05-07 20:26 UTC (permalink / raw)
To: akpm, jillravaliya, linux-kernel, linux-mm, urezki; +Cc: syzbot, syzkaller-bugs
syzbot ci has tested the following series
[v1] mm/vmalloc: free unused pages when shrinking vrealloc() allocation
https://lore.kernel.org/all/20260507114854.41117-1-jillravaliya@gmail.com
* [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation
* [PATCH 2/2] selftests/mm: add test for vrealloc() shrink page freeing
and found the following issue:
kernel BUG in __vunmap_range_noflush
Full report is available here:
https://ci.syzbot.org/series/13b0874e-a9f8-4992-be93-e93cc88e5e44
***
kernel BUG in __vunmap_range_noflush
tree: torvalds
URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
base: 2c340aab5485ebe9e33c01437dd4815ef33c8df5
arch: amd64
compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
config: https://ci.syzbot.org/builds/625f7138-9b20-4205-b0e7-02ed1219bd31/config
syz repro: https://ci.syzbot.org/findings/13e8dc07-d697-4345-a27f-319e9c1fe3d6/syz_repro
------------[ cut here ]------------
kernel BUG at mm/vmalloc.c:488!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 1 UID: 0 PID: 5824 Comm: syz.2.19 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:__vunmap_range_noflush+0xb4d/0xb70 mm/vmalloc.c:488
Code: 00 e9 64 f5 ff ff e8 12 8d a6 ff 48 c7 c7 a0 0e a8 8e 48 8b 74 24 48 48 89 da e8 0e c5 cf 02 e9 67 f5 ff ff e8 f4 8c a6 ff 90 <0f> 0b e8 ec 8c a6 ff e9 53 ff ff ff e8 e2 8c a6 ff bb 02 00 00 00
RSP: 0018:ffffc90003b575e0 EFLAGS: 00010293
RAX: ffffffff821f16bc RBX: ffffc900036fa000 RCX: ffff8881072a1d80
RDX: 0000000000000000 RSI: ffffc900036fa000 RDI: ffffc900036fa000
RBP: ffff88816ebb3980 R08: ffff88810007f1bb R09: 0000000000000000
R10: ffffc900036f9bb0 R11: ffffed102000fe38 R12: 0000000000000001
R13: ffffc900036fa000 R14: ffffc900036fa000 R15: dffffc0000000000
FS: 00007f4d6b8c46c0(0000) GS:ffff8882a9293000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000055924f0dd8c0 CR3: 00000001057f2000 CR4: 00000000000006f0
Call Trace:
<TASK>
vunmap_range_noflush mm/vmalloc.c:506 [inline]
vunmap_range mm/vmalloc.c:521 [inline]
vrealloc_node_align_noprof+0x4fc/0x880 mm/vmalloc.c:4346
bpf_patch_insn_data+0xeb/0x10a0 kernel/bpf/fixups.c:254
bpf_convert_ctx_accesses+0x213f/0x2d70 kernel/bpf/fixups.c:974
bpf_check+0x2b8e/0x49f0 kernel/bpf/verifier.c:20094
bpf_prog_load+0x1406/0x1a10 kernel/bpf/syscall.c:3082
__sys_bpf+0x618/0x950 kernel/bpf/syscall.c:6248
__do_sys_bpf kernel/bpf/syscall.c:6361 [inline]
__se_sys_bpf kernel/bpf/syscall.c:6359 [inline]
__x64_sys_bpf+0x7c/0x90 kernel/bpf/syscall.c:6359
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f4d6a99cdd9
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f4d6b8c4028 EFLAGS: 00000246 ORIG_RAX: 0000000000000141
RAX: ffffffffffffffda RBX: 00007f4d6ac15fa0 RCX: 00007f4d6a99cdd9
RDX: 0000000000000048 RSI: 00002000000054c0 RDI: 0000000000000005
RBP: 00007f4d6aa32d69 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f4d6ac16038 R14: 00007f4d6ac15fa0 R15: 00007ffff714fc08
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:__vunmap_range_noflush+0xb4d/0xb70 mm/vmalloc.c:488
Code: 00 e9 64 f5 ff ff e8 12 8d a6 ff 48 c7 c7 a0 0e a8 8e 48 8b 74 24 48 48 89 da e8 0e c5 cf 02 e9 67 f5 ff ff e8 f4 8c a6 ff 90 <0f> 0b e8 ec 8c a6 ff e9 53 ff ff ff e8 e2 8c a6 ff bb 02 00 00 00
RSP: 0018:ffffc90003b575e0 EFLAGS: 00010293
RAX: ffffffff821f16bc RBX: ffffc900036fa000 RCX: ffff8881072a1d80
RDX: 0000000000000000 RSI: ffffc900036fa000 RDI: ffffc900036fa000
RBP: ffff88816ebb3980 R08: ffff88810007f1bb R09: 0000000000000000
R10: ffffc900036f9bb0 R11: ffffed102000fe38 R12: 0000000000000001
R13: ffffc900036fa000 R14: ffffc900036fa000 R15: dffffc0000000000
FS: 00007f4d6b8c46c0(0000) GS:ffff8882a9293000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffdaf55afd8 CR3: 00000001057f2000 CR4: 00000000000006f0
***
If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
Tested-by: syzbot@syzkaller.appspotmail.com
---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.
To test a patch for this bug, please reply with `#syz test`
(should be on a separate line).
The patch should be attached to the email.
Note: arguments like custom git repos and branches are not supported.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-05-07 20:26 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 11:48 [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation Jill Ravaliya
2026-05-07 11:48 ` [PATCH 2/2] selftests/mm: add test for vrealloc() shrink page freeing Jill Ravaliya
2026-05-07 17:17 ` [PATCH 1/2] mm/vmalloc: free unused pages when shrinking vrealloc() allocation Uladzislau Rezki
2026-05-07 20:26 ` [syzbot ci] " syzbot ci
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox