From nobody Thu Apr 2 18:47:47 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A7AC83DA7E9 for ; Fri, 27 Mar 2026 09:48:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774604920; cv=none; b=flttGp4yFdsXolyv9EQGfIvxRPc+JkTBTYz4JbAEaLEHDa/IKzB7pojiM0VVbRuHE6AnYE/QKP+/2ASWhkDKzrFELdBJs9GzNyKfvi06VbZvWPAcx30z2/3Un+0nAuEFKTlaznv3pzpjMMPF/cftp4W38Us6/kQUILq2sfIwbjk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774604920; c=relaxed/simple; bh=VAn5qbLsvUENIW/va6GuMZmrw3yUMFjIWRDfQx0oC18=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AlxRXhFVKc6zFY312y3Vq8hj1X42at+12u12At/KLyQ6OThL35EFmovyVyvw37iBQqu2gYHfgxx4gjC5Ey+pwgBQdaByfRRr0MsAugRlPupil2Xg97sfIGhY+/A0tRWGNSea6Yst4oV0VaxH0HsAKEbkmw8QjNJvCHuQ+mXmNA8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=rtEZrHO2; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="rtEZrHO2" Received: by smtp.kernel.org (Postfix) with ESMTPS id 7CB43C2BCB6; Fri, 27 Mar 2026 09:48:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774604920; bh=VAn5qbLsvUENIW/va6GuMZmrw3yUMFjIWRDfQx0oC18=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=rtEZrHO2SlFLOfw3fqO0+ywOHHW6vpglwQibetS+TFxd8N/FqQ2IOEmduenSBVq/I 7Q/TPqLOIFcpzLJI9QZ9VOakktLS6HT6CKgwAONPzB8fDyMz4+Y9ds/Jpy3E4lngk0 UyzClMeGYlAlfx44amUv91slA+w+rHumhv6WWynLn1+LM47ZycWs54wC215AwLqNQ0 vIYzT2gnVOkG497UQ3RkV1lLoA22cSmB7+QByGkh0qBD/884BCl8e+/9Ipqg6IWatI awofZ7mPS3GA1xOfSOr1LPc7BmdvCG3y6qsP0z8KXtHLRnWxcRTg1Dh9epNd8SXmKB uKR6qK1x2y5ag== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7155E10ED647; Fri, 27 Mar 2026 09:48:40 +0000 (UTC) From: Shivam Kalra via B4 Relay Date: Fri, 27 Mar 2026 15:18:41 +0530 Subject: [PATCH v8 5/6] mm/vmalloc: free unused pages on vrealloc() shrink Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260327-vmalloc-shrink-v8-5-cc6b57059ed7@zohomail.in> References: <20260327-vmalloc-shrink-v8-0-cc6b57059ed7@zohomail.in> In-Reply-To: <20260327-vmalloc-shrink-v8-0-cc6b57059ed7@zohomail.in> To: Andrew Morton , Uladzislau Rezki Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, Alice Ryhl , Danilo Krummrich , Shivam Kalra X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1774604917; l=4140; i=shivamkalra98@zohomail.in; s=20260212; h=from:subject:message-id; bh=yGSj5LIL6EaRfv6ZvzI9tnknpwNqR3EakaaeUvTgq0c=; b=yVF907VkAdaIM4lQvlpR88OTp+REg4u+jdY88y3PxpHAqm6Bz3MM0KyTpgnQwxXaWM1Ung3gd wuG0msrMscMDV1Owh6S2AakGeW6x5cnTOOdhyjOEnNbxd1L2FcwiDJD X-Developer-Key: i=shivamkalra98@zohomail.in; a=ed25519; pk=9Q+S1LD/xjbjL7bEaLIlwRADBwU/6LJq7lYm8LFrkQE= X-Endpoint-Received: by B4 Relay for shivamkalra98@zohomail.in/20260212 with auth_id=633 X-Original-From: Shivam Kalra Reply-To: shivamkalra98@zohomail.in From: Shivam Kalra When vrealloc() shrinks an allocation and the new size crosses a page boundary, unmap and free the tail pages that are no longer needed. This reclaims physical memory that was previously wasted for the lifetime of the allocation. The heuristic is simple: always free when at least one full page becomes unused. Huge page allocations (page_order > 0) are skipped, as partial freeing would require splitting. Allocations with VM_FLUSH_RESET_PERMS are also skipped, as their direct-map permissions must be reset before pages are returned to the page allocator, which is handled by vm_reset_perms() during vfree(). Additionally, allocations with VM_USERMAP are skipped because remap_vmalloc_range_partial() validates mapping requests against the unchanged vm->size; freeing tail pages would cause vmalloc_to_page() to return NULL for the unmapped range. To protect concurrent readers, the shrink path uses Node lock to synchronize before freeing the pages. Finally, we notify kmemleak of the reduced allocation size using kmemleak_free_part() to prevent the kmemleak scanner from faulting on the newly unmapped virtual addresses. The virtual address reservation (vm->size / vmap_area) is intentionally kept unchanged, preserving the address for potential future grow-in-place support. Suggested-by: Danilo Krummrich Signed-off-by: Shivam Kalra --- mm/vmalloc.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index c6bdddee6266..57aec552e038 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -4351,14 +4351,62 @@ void *vrealloc_node_align_noprof(const void *p, siz= e_t size, unsigned long align goto need_realloc; } =20 - /* - * TODO: Shrink the vm_area, i.e. unmap and free unused pages. What - * would be a good heuristic for when to shrink the vm_area? - */ if (size <=3D old_size) { + unsigned int new_nr_pages =3D PAGE_ALIGN(size) >> PAGE_SHIFT; + /* Zero out "freed" memory, potentially for future realloc. */ if (want_init_on_free() || want_init_on_alloc(flags)) memset((void *)p + size, 0, old_size - size); + + /* + * Free tail pages when shrink crosses a page boundary. + * + * Skip huge page allocations (page_order > 0) as partial + * freeing would require splitting. + * + * Skip VM_FLUSH_RESET_PERMS, as direct-map permissions must + * be reset before pages are returned to the allocator. + * + * Skip VM_USERMAP, as remap_vmalloc_range_partial() validates + * mapping requests against the unchanged vm->size; freeing + * tail pages would cause vmalloc_to_page() to return NULL for + * the unmapped range. + * + * Skip if either GFP_NOFS or GFP_NOIO are used. + * kmemleak_free_part() internally allocates with + * GFP_KERNEL, which could trigger a recursive deadlock + * if we are under filesystem or I/O reclaim. + */ + if (new_nr_pages < vm->nr_pages && !vm_area_page_order(vm) && + !(vm->flags & (VM_FLUSH_RESET_PERMS | VM_USERMAP)) && + gfp_has_io_fs(flags)) { + unsigned long addr =3D (unsigned long)kasan_reset_tag(p); + unsigned int old_nr_pages =3D vm->nr_pages; + + /* Notify kmemleak of the reduced allocation size before unmapping. */ + kmemleak_free_part( + (void *)addr + ((unsigned long)new_nr_pages + << PAGE_SHIFT), + (unsigned long)(old_nr_pages - new_nr_pages) + << PAGE_SHIFT); + + vunmap_range(addr + ((unsigned long)new_nr_pages + << PAGE_SHIFT), + addr + ((unsigned long)old_nr_pages + << PAGE_SHIFT)); + + /* + * Use the node lock to synchronize with concurrent + * readers (vmalloc_info_show). + */ + struct vmap_node *vn =3D addr_to_node(addr); + + spin_lock(&vn->busy.lock); + WRITE_ONCE(vm->nr_pages, new_nr_pages); + spin_unlock(&vn->busy.lock); + + vm_area_free_pages(vm, new_nr_pages, old_nr_pages); + } vm->requested_size =3D size; kasan_vrealloc(p, old_size, size); return (void *)p; --=20 2.43.0