From: Kees Cook <kees@kernel.org>
To: Vlastimil Babka <vbabka@kernel.org>
Cc: "Kees Cook" <kees@kernel.org>, "Harry Yoo" <harry@kernel.org>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Hao Li" <hao.li@linux.dev>, "Christoph Lameter" <cl@gentwo.org>,
"David Rientjes" <rientjes@google.com>,
"Roman Gushchin" <roman.gushchin@linux.dev>,
linux-mm@kvack.org, "Pedro Falcato" <pfalcato@suse.de>,
"Kuniyuki Iwashima" <kuniyu@google.com>,
linux-hardening@vger.kernel.org,
"Jakub Kicinski" <kuba@kernel.org>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Paolo Abeni" <pabeni@redhat.com>,
"Simon Horman" <horms@kernel.org>,
"Jason Xing" <kerneljasonxing@gmail.com>,
"Björn Töpel" <bjorn@kernel.org>,
"Jiayuan Chen" <jiayuan.chen@linux.dev>,
"Willem de Bruijn" <willemb@google.com>,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org
Subject: [PATCH v4 5/7] mm/slab: Provide kmalloc type fallback for bucket allocations
Date: Mon, 21 Sep 2026 00:58:16 -0700 [thread overview]
Message-ID: <20260921075820.1718334-5-kees@kernel.org> (raw)
In-Reply-To: <20260921075811.too.775-kees@kernel.org>
kmem_buckets_create() clones kmalloc_caches[KMALLOC_NORMAL].
kmalloc_slab() figures out the kmalloc type the caller asks for, but
then ignored it whenever a bucket set was in use, returning a normal
cache regardless. This would be a problem if a caller asked for GFP_DMA,
__GFP_ACCOUNT, etc. None of the current users do this, so there is
problem, but it makes adding new users fragile. For example, skb data[1]
needs to handle GFP_DMA (rarely) and __GFP_ACCOUNT (often).
Send those allocations to the general caches instead so nothing breaks and
regular allocations remain isolated with the bucket. The kmem_bucket_type
enum contains only a single item here, but will be expanded in the next
patch.
Built and tests pass with ARCH=x86_64 defconfig with GCC 16.2.0, with
CONFIG_SLAB_BUCKETS as y and n.
Assisted-by: LLM
Link: https://lore.kernel.org/all/04debe19-bbe8-4b5f-9668-753d1f97832d@redhat.com/ [1]
Signed-off-by: Kees Cook <kees@kernel.org>
---
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Harry Yoo <harry@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Hao Li <hao.li@linux.dev>
Cc: Christoph Lameter <cl@gentwo.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: <linux-mm@kvack.org>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Kuniyuki Iwashima <kuniyu@google.com>
Cc: <linux-hardening@vger.kernel.org>
---
include/linux/slab.h | 13 +++++++++++
mm/slab.h | 23 ++++++++++++++++--
lib/tests/slub_kunit.c | 53 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 87 insertions(+), 2 deletions(-)
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 18a2351f9084..ab9ab3d34847 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -742,6 +742,19 @@ typedef struct kmem_cache * kmem_buckets[KMALLOC_SHIFT_HIGH + 1];
extern kmem_buckets kmalloc_caches[NR_KMALLOC_TYPES];
+/*
+ * The kmalloc types a bucket set can hold a copy of. This is deliberately not
+ * enum kmalloc_cache_type: the KMALLOC_PARTITION copies are all "normal" to a
+ * bucket set, which already separates what they were there to separate, so
+ * indexing by those would mean up to KMALLOC_PARTITION_CACHES_NR unusable
+ * rows per set. Allocations of any type not listed here are served by the
+ * general caches.
+ */
+enum kmem_bucket_type {
+ KMEM_BUCKET_NORMAL = 0,
+ NR_KMEM_BUCKET_TYPES
+};
+
/*
* Define gfp bits that should not be set for KMALLOC_NORMAL.
*/
diff --git a/mm/slab.h b/mm/slab.h
index 8fd6835e4235..7f1bfee83b92 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -421,6 +421,26 @@ static inline unsigned int size_index_elem(unsigned int bytes)
return (bytes - 1) / 8;
}
+/*
+ * Which set of buckets to use for the given kmalloc_cache_type. If not
+ * handled by the kmem_buckets, fall back to general caches.
+ */
+static inline kmem_buckets *
+kmalloc_choose_bucket(kmem_buckets *bucket, enum kmalloc_cache_type type)
+{
+ enum kmem_bucket_type btype;
+
+ if (!bucket)
+ return &kmalloc_caches[type];
+
+ if (type <= KMALLOC_PARTITION_END)
+ btype = KMEM_BUCKET_NORMAL;
+ else
+ return &kmalloc_caches[type]; /* No set holds a row for it. */
+
+ return &bucket[btype];
+}
+
/*
* Find the kmem_cache structure that serves a given size of
* allocation
@@ -438,8 +458,7 @@ kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, kmalloc_token_t token,
if (alloc_flags & SLAB_ALLOC_NO_OBJ_EXT)
type = KMALLOC_NO_OBJ_EXT;
- if (!b)
- b = &kmalloc_caches[type];
+ b = kmalloc_choose_bucket(b, type);
if (size <= 192)
index = kmalloc_size_index[size_index_elem(size)];
else
diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c
index d6467dd5cf9b..823607e06248 100644
--- a/lib/tests/slub_kunit.c
+++ b/lib/tests/slub_kunit.c
@@ -683,6 +683,58 @@ static void test_kmem_buckets_destroy(struct kunit *test)
KUNIT_EXPECT_EQ(test, 2, slab_errors);
}
+/*
+ * A bucket set holds only the kmalloc types it was created with, so an
+ * allocation that asks for a different one has to come from the general
+ * caches. Check that it does, rather than being served a normal cache that
+ * does not satisfy what the flags asked for.
+ */
+static void test_kmem_buckets_type_fallback(struct kunit *test)
+{
+ struct kmem_cache *c;
+ kmem_buckets *b;
+ void *p;
+
+ if (!IS_ENABLED(CONFIG_SLAB_BUCKETS))
+ kunit_skip(test, "needs CONFIG_SLAB_BUCKETS");
+
+ b = kmem_buckets_create("test_buckets", 0, 0, INT_MAX, NULL);
+ KUNIT_ASSERT_BUCKETS_CREATED(test, b);
+
+ /* A plain allocation stays isolated in the bucket set. */
+ p = kmem_buckets_alloc(b, 128, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, p);
+ c = cache_of(p);
+ kfree(p);
+ KUNIT_ASSERT_NOT_NULL(test, c);
+
+ KUNIT_EXPECT_TRUE_MSG(test, strstarts(c->name, "test_buckets-"),
+ "expected a bucket cache, got %s", c->name);
+
+ /* One that needs ZONE_DMA cannot, so it falls back. */
+ if (IS_ENABLED(CONFIG_ZONE_DMA)) {
+ p = kmem_buckets_alloc(b, 128, GFP_KERNEL | GFP_DMA);
+ KUNIT_ASSERT_NOT_NULL(test, p);
+ c = cache_of(p);
+ kfree(p);
+ KUNIT_ASSERT_NOT_NULL(test, c);
+
+ KUNIT_EXPECT_TRUE_MSG(test, strstarts(c->name, "dma-kmalloc-"),
+ "expected a DMA cache, got %s", c->name);
+ }
+
+ /* Nor can one that has to be accounted. */
+ if (IS_ENABLED(CONFIG_MEMCG) && !mem_cgroup_kmem_disabled()) {
+ p = kmem_buckets_alloc(b, 128, GFP_KERNEL | __GFP_ACCOUNT);
+ KUNIT_ASSERT_NOT_NULL(test, p);
+ c = virt_to_slab(p)->slab_cache;
+ kfree(p);
+
+ KUNIT_EXPECT_TRUE_MSG(test, strstarts(c->name, "kmalloc-cg-"),
+ "expected an accounted cache, got %s", c->name);
+ }
+}
+
static struct kunit_case test_cases[] = {
KUNIT_CASE(test_clobber_zone),
@@ -709,6 +761,7 @@ static struct kunit_case test_cases[] = {
KUNIT_CASE(test_kmem_buckets_alignment),
KUNIT_CASE(test_kmem_buckets_disabled),
KUNIT_CASE(test_kmem_buckets_destroy),
+ KUNIT_CASE(test_kmem_buckets_type_fallback),
{}
};
--
2.34.1
next prev parent reply other threads:[~2026-09-21 7:58 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-21 7:58 [PATCH v4 0/7] net: skb: isolate skb data area allocations into a separate bucket Kees Cook
2026-09-21 7:58 ` [PATCH v4 1/7] mm/slab: Mark the kmem_buckets_create() context as a Context: section Kees Cook
2026-09-21 12:03 ` Harry Yoo
2026-09-22 9:58 ` Pedro Falcato
2026-09-25 8:38 ` Hao Li
2026-09-21 7:58 ` [PATCH v4 2/7] mm/slab: Give bucket caches the alignment of the caches they mirror Kees Cook
2026-09-21 13:17 ` Harry Yoo
2026-09-21 23:25 ` Kees Cook
2026-09-24 18:22 ` Harry Yoo
2026-09-25 1:00 ` Kees Cook
2026-09-21 7:58 ` [PATCH v4 3/7] mm/slab: Add kmem_buckets_destroy() Kees Cook
2026-09-21 7:58 ` [PATCH v4 4/7] mm/slab: Add tests for the existing kmem_buckets behaviour Kees Cook
2026-09-21 7:58 ` Kees Cook [this message]
2026-09-22 10:11 ` [PATCH v4 5/7] mm/slab: Provide kmalloc type fallback for bucket allocations Pedro Falcato
2026-10-02 22:27 ` Kees Cook
2026-09-21 7:58 ` [PATCH v4 6/7] mm/slab: Let a bucket set handle __GFP_ACCOUNT Kees Cook
2026-09-21 13:25 ` Harry Yoo
2026-09-21 23:26 ` Kees Cook
2026-09-22 10:20 ` Pedro Falcato
2026-10-02 22:29 ` Kees Cook
2026-09-21 7:58 ` [PATCH v4 7/7] net: skb: isolate skb data area allocations into a separate bucket Kees Cook
2026-09-27 8:24 ` Paolo Abeni
2026-10-02 22:59 ` Kees Cook
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=20260921075820.1718334-5-kees@kernel.org \
--to=kees@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=bjorn@kernel.org \
--cc=cl@gentwo.org \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=hao.li@linux.dev \
--cc=harry@kernel.org \
--cc=horms@kernel.org \
--cc=jiayuan.chen@linux.dev \
--cc=kerneljasonxing@gmail.com \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pfalcato@suse.de \
--cc=rientjes@google.com \
--cc=roman.gushchin@linux.dev \
--cc=vbabka@kernel.org \
--cc=willemb@google.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.