From: Pranjal Arya <pranjal.arya@oss.qualcomm.com>
To: Andrew Morton <akpm@linux-foundation.org>,
Uladzislau Rezki <urezki@gmail.com>,
"Liam R. Howlett" <liam@infradead.org>,
Alice Ryhl <aliceryhl@google.com>,
Andrew Ballance <andrewjballance@gmail.com>
Cc: linux-arm-msm@vger.kernel.org, linux-mm@kvack.org,
linux-kernel@vger.kernel.org, maple-tree@lists.infradead.org,
Lorenzo Stoakes <ljs@kernel.org>,
Pranjal Shrivastava <praan@google.com>,
Will Deacon <will@kernel.org>,
Suzuki K Poulose <Suzuki.Poulose@arm.com>,
Neil Armstrong <neil.armstrong@linaro.org>,
Mostafa Saleh <smostafa@google.com>,
Balbir Singh <balbirs@nvidia.com>,
Suren Baghdasaryan <surenb@google.com>,
Marco Elver <elver@google.com>,
Dmitry Vyukov <dvyukov@google.com>,
Alexander Potapenko <glider@google.com>,
Shuah Khan <shuah@kernel.org>, Dev Jain <dev.jain@arm.com>,
Brendan Jackman <jackmanb@google.com>,
Puranjay Mohan <puranjay@kernel.org>,
Santosh Shukla <santosh.shukla@amd.com>,
Wyes Karny <wkarny@gmail.com>,
Pranjal Arya <pranjal.arya@oss.qualcomm.com>,
Sudeep Holla <sudeep.holla@kernel.org>
Subject: [PATCH RFC 08/12] mm/vmalloc: track lazy-purge queue as a list_head
Date: Sat, 13 Jun 2026 22:49:50 +0530 [thread overview]
Message-ID: <20260613-vmalloc_maple-v1-8-0aa740bb944b@oss.qualcomm.com> (raw)
In-Reply-To: <20260613-vmalloc_maple-v1-0-0aa740bb944b@oss.qualcomm.com>
The lazy queue is bulk-drained from the purge worker; nothing
queries it by address. publish_vmap_area_lazy() inserts into the
queue and purge_vmap_areas_lazy() walks it linearly.
A list_head expresses the actual usage and saves the per-publish
maple insert. Per-node vn->lazy.mt becomes vn->lazy_list. The
locking discipline (vn->lazy.lock still serialises inserts) is
unchanged.
Signed-off-by: Pranjal Arya <pranjal.arya@oss.qualcomm.com>
---
mm/vmalloc.c | 133 +++++++++++++++++++++++++----------------------------------
1 file changed, 57 insertions(+), 76 deletions(-)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 73a40a88dbf6..1b73001e197e 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -942,6 +942,16 @@ static struct vmap_node {
struct mt_list busy;
struct mt_list lazy;
+ /*
+ * Lazy list. The lazy index is no longer queried by address on the
+ * hot path: free_vmap_area_noflush() pushes the VA via list_add and
+ * purge drains it via list_splice. Keeping a list head sidesteps a
+ * mas_store on every vfree and a mas_for_each + per-entry
+ * mas_store(NULL) during purge. lazy.mt is retained for the rare
+ * non-perf_mode rollback path inside publish_vmap_area_lazy().
+ */
+ struct list_head lazy_list;
+
/*
* Ready-to-free areas.
*/
@@ -1510,52 +1520,37 @@ unlink_vmap_area_busy_locked(struct vmap_area *va, struct vmap_node *vn)
static __always_inline bool
insert_vmap_area_lazy_locked(struct vmap_area *va, struct vmap_node *vn)
{
- int err;
-
lockdep_assert_held(&vn->lazy.lock);
- try_init_lazy_mt_locked(vn);
- if (WARN_ON_ONCE(!vn->lazy.mt_enabled))
- return false;
-
- if (!validate_vmap_area_range_insert_mt_locked(&vn->lazy.mt,
- va->va_start,
- va->va_end))
+ /*
+ * The maple-tree lazy index is bypassed in the hot path: a simple
+ * list saves one mas_store per vfree and one mas_for_each + N
+ * mas_store(NULL) during purge. lazy.mt is left untouched here so
+ * the non-perf_mode publish_vmap_area_lazy() rollback can still
+ * unlink the VA via unlink_vmap_area_lazy_locked() if it inserted
+ * one — that path is unreachable in steady state with perf_mode on.
+ */
+ if (WARN_ON_ONCE(!list_empty(&va->list)))
return false;
- INIT_LIST_HEAD(&va->list);
-
- MA_STATE(mas, &vn->lazy.mt, va->va_start, va->va_end - 1);
-
- err = mas_preallocate(&mas, va, GFP_NOWAIT | __GFP_NOWARN);
- if (!err) {
- mas_store_prealloc(&mas, va);
- mas_destroy(&mas);
- return true;
- }
-
- err = mas_store_gfp(&mas, va, GFP_ATOMIC | __GFP_NOWARN);
- return !WARN_ON_ONCE(err);
+ list_add_tail(&va->list, &vn->lazy_list);
+ return true;
}
static __always_inline bool
unlink_vmap_area_lazy_locked(struct vmap_area *va, struct vmap_node *vn)
{
- int err;
-
lockdep_assert_held(&vn->lazy.lock);
- try_init_lazy_mt_locked(vn);
- if (WARN_ON_ONCE(!vn->lazy.mt_enabled))
- return false;
-
- MA_STATE(mas, &vn->lazy.mt, va->va_start, va->va_end - 1);
-
- err = mas_store_gfp(&mas, NULL, GFP_ATOMIC | __GFP_NOWARN);
- if (WARN_ON_ONCE(err))
+ /*
+ * Match insert_vmap_area_lazy_locked()'s list-based fast path. Used
+ * only by publish_vmap_area_lazy() rollback, which is unreachable in
+ * steady state but kept for the non-perf_mode early-boot window.
+ */
+ if (list_empty(&va->list))
return false;
- INIT_LIST_HEAD(&va->list);
+ list_del_init(&va->list);
return true;
}
@@ -1610,48 +1605,22 @@ lazy_vmap_areas_empty_locked(struct vmap_node *vn)
{
lockdep_assert_held(&vn->lazy.lock);
- try_init_lazy_mt_locked(vn);
- if (WARN_ON_ONCE(!vn->lazy.mt_enabled))
- return true;
-
- return mtree_empty(&vn->lazy.mt);
+ return list_empty(&vn->lazy_list);
}
static __always_inline void
move_lazy_vmap_areas_to_purge_locked(struct vmap_node *vn)
{
- LIST_HEAD(move_list);
- struct vmap_area *va, *n_va;
- int err;
-
lockdep_assert_held(&vn->lazy.lock);
- try_init_lazy_mt_locked(vn);
- if (WARN_ON_ONCE(!vn->lazy.mt_enabled))
- return;
-
- MA_STATE(mas, &vn->lazy.mt, 0, 0);
-
- mas_for_each(&mas, va, ULONG_MAX)
- list_add_tail(&va->list, &move_list);
-
/*
- * Erase ranges one-by-one and move only successfully erased entries to
- * purge_list. This avoids destroy/reinit churn and keeps lazy index
- * coherence if an erase operation fails under pressure.
+ * Move every queued VA to purge_list with a single splice. The
+ * sort-by-address property that the maple-tree lazy index used to
+ * provide is no longer used by purge_vmap_node(); kasan_release
+ * computes its own min/max over the resulting purge_list when
+ * needed.
*/
- list_for_each_entry_safe(va, n_va, &move_list, list) {
- MA_STATE(mas_erase, &vn->lazy.mt, va->va_start, va->va_end - 1);
-
- err = mas_store_gfp(&mas_erase, NULL, GFP_ATOMIC | __GFP_NOWARN);
- if (unlikely(err)) {
- WARN_ON_ONCE(err);
- list_del_init(&va->list);
- continue;
- }
-
- list_move_tail(&va->list, &vn->purge_list);
- }
+ list_splice_tail_init(&vn->lazy_list, &vn->purge_list);
}
static __always_inline bool
@@ -2806,13 +2775,18 @@ static void
kasan_release_vmalloc_node(struct vmap_node *vn)
{
struct vmap_area *va;
- unsigned long start, end;
+ unsigned long start = ULONG_MAX, end = 0;
unsigned int batch_count = 0;
- start = list_first_entry(&vn->purge_list, struct vmap_area, list)->va_start;
- end = list_last_entry(&vn->purge_list, struct vmap_area, list)->va_end;
-
+ /*
+ * purge_list is no longer sorted by address (lazy_list is built in
+ * insertion order via list_add_tail). Compute the bounding range
+ * inline with the per-VA shadow-release loop to avoid a second walk.
+ */
list_for_each_entry(va, &vn->purge_list, list) {
+ start = min(start, va->va_start);
+ end = max(end, va->va_end);
+
if (is_vmalloc_or_module_addr((void *) va->va_start))
kasan_release_vmalloc(va->va_start, va->va_end,
va->va_start, va->va_end,
@@ -2824,7 +2798,9 @@ kasan_release_vmalloc_node(struct vmap_node *vn)
}
}
- kasan_release_vmalloc(start, end, start, end, KASAN_VMALLOC_TLB_FLUSH);
+ if (start < end)
+ kasan_release_vmalloc(start, end, start, end,
+ KASAN_VMALLOC_TLB_FLUSH);
}
static void purge_vmap_node(struct work_struct *work)
@@ -2938,6 +2914,7 @@ static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end,
static cpumask_t purge_nodes;
unsigned int nr_purge_nodes;
struct vmap_node *vn;
+ struct vmap_area *va;
int i;
lockdep_assert_held(&vmap_purge_lock);
@@ -2964,11 +2941,14 @@ static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end,
move_lazy_vmap_areas_to_purge_locked(vn);
spin_unlock(&vn->lazy.lock);
- start = min(start, list_first_entry(&vn->purge_list,
- struct vmap_area, list)->va_start);
-
- end = max(end, list_last_entry(&vn->purge_list,
- struct vmap_area, list)->va_end);
+ /*
+ * lazy_list (and therefore purge_list) is no longer sorted by
+ * address. Compute the bounding range by walking purge_list.
+ */
+ list_for_each_entry(va, &vn->purge_list, list) {
+ start = min(start, va->va_start);
+ end = max(end, va->va_end);
+ }
cpumask_set_cpu(node_to_id(vn), &purge_nodes);
}
@@ -6153,6 +6133,7 @@ static void vmap_init_nodes(void)
mt_init_flags(&vn->lazy.mt, MT_FLAGS_LOCK_EXTERN);
mt_set_external_lock(&vn->lazy.mt, &vn->lazy.lock);
vn->lazy.mt_enabled = true;
+ INIT_LIST_HEAD(&vn->lazy_list);
for (i = 0; i < MAX_VA_SIZE_PAGES; i++) {
INIT_LIST_HEAD(&vn->pool[i].head);
--
2.34.1
next prev parent reply other threads:[~2026-06-13 17:21 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-13 17:19 [PATCH RFC 00/12] mm/vmalloc: migrate vmap_area indexing from rb-tree to maple-tree Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 01/12] mm/vmalloc: introduce maple_tree-based indexing for vmap_area Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 02/12] mm/vmalloc: convert allocation-side gap finding and insertion to maple_tree Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 03/12] mm/vmalloc: convert free, purge, and pcpu paths " Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 04/12] mm/vmalloc: finalize maple-only indexing and shrink struct vmap_area Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 05/12] mm/vmalloc: tighten failure handling under memory pressure Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 06/12] mm/vmalloc: tighten alloc/free hot paths Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 07/12] mm/vmalloc: consolidate occupied tree as authoritative index on hot path Pranjal Arya
2026-06-13 17:19 ` Pranjal Arya [this message]
2026-06-13 17:19 ` [PATCH RFC 09/12] mm/vmalloc: collapse busy-tree find-then-unlink into a single mas_erase Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 10/12] mm/vmalloc: per-CPU caching of free ranges from the maple_tree allocator Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 11/12] mm/vmalloc: O(1) lookup of cached vmap_areas with bounded fast-reject Pranjal Arya
2026-06-13 17:19 ` [PATCH RFC 12/12] mm/vmalloc: harden bump-allocator alloc/free against UBSAN array bounds Pranjal Arya
2026-06-13 23:15 ` [PATCH RFC 00/12] mm/vmalloc: migrate vmap_area indexing from rb-tree to maple-tree Matthew Wilcox
2026-06-14 6:35 ` [syzbot ci] " syzbot ci
2026-06-14 6:58 ` [PATCH RFC 00/12] " Uladzislau Rezki
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260613-vmalloc_maple-v1-8-0aa740bb944b@oss.qualcomm.com \
--to=pranjal.arya@oss.qualcomm.com \
--cc=Suzuki.Poulose@arm.com \
--cc=akpm@linux-foundation.org \
--cc=aliceryhl@google.com \
--cc=andrewjballance@gmail.com \
--cc=balbirs@nvidia.com \
--cc=dev.jain@arm.com \
--cc=dvyukov@google.com \
--cc=elver@google.com \
--cc=glider@google.com \
--cc=jackmanb@google.com \
--cc=liam@infradead.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=maple-tree@lists.infradead.org \
--cc=neil.armstrong@linaro.org \
--cc=praan@google.com \
--cc=puranjay@kernel.org \
--cc=santosh.shukla@amd.com \
--cc=shuah@kernel.org \
--cc=smostafa@google.com \
--cc=sudeep.holla@kernel.org \
--cc=surenb@google.com \
--cc=urezki@gmail.com \
--cc=will@kernel.org \
--cc=wkarny@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.