Thread (4 messages) flat view 4 messages, 1 author, 6h ago
DORMANTno replies

[RFC PATCH 2/3] mm/slub: count kvmalloc() allocations forced to vmalloc()

From: Daniil Tatianin <hidden>
Date: 2026-08-05 09:50:31
Also in: linux-mm, lkml
Subsystem: documentation, memory management, memory management - core, slab allocator, the rest · Maintainers: Jonathan Corbet, Andrew Morton, David Hildenbrand, Vlastimil Babka, Harry Yoo, Linus Torvalds

vm.kvmalloc_max_contig_order changes allocation behavior silently.  There
is no way to tell whether a given value is doing anything at all, or how
much of the workload it affects, short of tracing kvmalloc() by hand,
which makes it hard to pick a value and hard to notice when a workload
starts running into it.

Add a kvmalloc_forced_vmalloc counter to /proc/vmstat, incremented for
every allocation that skipped kmalloc() because of the limit.  Requests
above KMALLOC_MAX_SIZE are skipped by the limit like any other, since the
attempt would fail regardless, but they are not counted, as they would
have reached vmalloc() either way.  What is left is the number of
allocations the limit actually diverted, which is what is needed to tell
whether it is set sensibly.

Note that the counter stays at zero while the sysctl is left at its
default, since no allocation is denied in that case.  Determining what to
set the limit to in the first place still requires tracing kvmalloc()
directly, or setting the limit on a canary first and reading the counter
there.

Signed-off-by: Daniil Tatianin <redacted>
---
 Documentation/admin-guide/sysctl/vm.rst |  5 +++++
 include/linux/vm_event_item.h           |  3 +++
 mm/Kconfig                              |  2 ++
 mm/slub.c                               | 15 ++++++++++++++-
 mm/vmstat.c                             |  3 +++
 5 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst
index bc669052c50b..4581fb68c974 100644
--- a/Documentation/admin-guide/sysctl/vm.rst
+++ b/Documentation/admin-guide/sysctl/vm.rst
@@ -394,6 +394,11 @@ the order of KMALLOC_MAX_CACHE_SIZE up to MAX_PAGE_ORDER.
 Requests above KMALLOC_MAX_SIZE end up in vmalloc() whatever this is set to,
 since kmalloc() cannot serve them at all.
 
+The kvmalloc_forced_vmalloc counter in /proc/vmstat counts the allocations
+that were routed to vmalloc() because of this limit.  Requests that
+kmalloc() could not have served anyway are not counted, so the counter only
+reflects allocations the limit itself diverted.
+
 
 legacy_va_layout
 ================
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
index 03fe95f5a020..c060c06ca2d2 100644
--- a/include/linux/vm_event_item.h
+++ b/include/linux/vm_event_item.h
@@ -145,6 +145,9 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
 		DIRECT_MAP_LEVEL2_COLLAPSE,
 		DIRECT_MAP_LEVEL3_COLLAPSE,
 #endif
+#ifdef CONFIG_KVMALLOC_ORDER_LIMIT
+		KVMALLOC_FORCED_VMALLOC,
+#endif
 #ifdef CONFIG_PER_VMA_LOCK_STATS
 		VMA_LOCK_SUCCESS,
 		VMA_LOCK_ABORT,
diff --git a/mm/Kconfig b/mm/Kconfig
index 9b1fcabb6d8f..0ca7fc5321cf 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -262,6 +262,8 @@ config KVMALLOC_ORDER_LIMIT
 	  This enables the vm.kvmalloc_max_contig_order sysctl, which caps
 	  the allocation order kvmalloc() is willing to ask the page
 	  allocator for.  Larger requests are routed to vmalloc() directly.
+	  It also adds the kvmalloc_forced_vmalloc counter to /proc/vmstat,
+	  which tells how many allocations were routed this way.
 
 	  The default value of the sysctl preserves the existing behavior,
 	  so saying Y here only makes the tunable available.
diff --git a/mm/slub.c b/mm/slub.c
index 0a9910602cec..7287949ea247 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -51,6 +51,7 @@
 #include <linux/kprobes.h>
 #include <linux/debugfs.h>
 #include <linux/sysctl.h>
+#include <linux/vmstat.h>
 #include <trace/events/kmem.h>
 
 #include "internal.h"
@@ -6916,7 +6917,19 @@ static bool kvmalloc_order_denied(size_t size)
 	if (likely(sysctl_kvmalloc_max_contig_order >= MAX_PAGE_ORDER))
 		return false;
 
-	return get_order(size) > sysctl_kvmalloc_max_contig_order;
+	if (get_order(size) <= sysctl_kvmalloc_max_contig_order)
+		return false;
+
+	/*
+	 * kmalloc() cannot serve anything above KMALLOC_MAX_SIZE, so such a
+	 * request would have reached vmalloc() with or without the limit.
+	 * Skipping the doomed attempt is still worthwhile, but the limit did
+	 * not divert anything, so do not account it.
+	 */
+	if (size <= KMALLOC_MAX_SIZE)
+		count_vm_event(KVMALLOC_FORCED_VMALLOC);
+
+	return true;
 }
 
 static const struct ctl_table kvmalloc_sysctl_table[] = {
diff --git a/mm/vmstat.c b/mm/vmstat.c
index f534972f517d..e7139c0947ce 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1459,6 +1459,9 @@ const char * const vmstat_text[] = {
 	[I(DIRECT_MAP_LEVEL2_COLLAPSE)]		= "direct_map_level2_collapses",
 	[I(DIRECT_MAP_LEVEL3_COLLAPSE)]		= "direct_map_level3_collapses",
 #endif
+#ifdef CONFIG_KVMALLOC_ORDER_LIMIT
+	[I(KVMALLOC_FORCED_VMALLOC)]		= "kvmalloc_forced_vmalloc",
+#endif
 #ifdef CONFIG_PER_VMA_LOCK_STATS
 	[I(VMA_LOCK_SUCCESS)]			= "vma_lock_success",
 	[I(VMA_LOCK_ABORT)]			= "vma_lock_abort",
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help