From: "Vlastimil Babka (SUSE)" <vbabka@kernel.org>
To: "Harry Yoo" <harry@kernel.org>,
"Alexei Starovoitov" <ast@kernel.org>,
"Alexander Potapenko" <glider@google.com>,
"Marco Elver" <elver@google.com>,
"Sumit Semwal" <sumit.semwal@linaro.org>,
"Christian König" <christian.koenig@amd.com>,
"Catalin Marinas" <catalin.marinas@arm.com>,
"Ingo Molnar" <mingo@redhat.com>,
"Peter Zijlstra" <peterz@infradead.org>,
"Juri Lelli" <juri.lelli@redhat.com>,
"Vincent Guittot" <vincent.guittot@linaro.org>,
"Sebastian Andrzej Siewior" <bigeasy@linutronix.de>,
"Clark Williams" <clrkwllms@kernel.org>,
"Steven Rostedt" <rostedt@goodmis.org>
Cc: 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>,
bpf@vger.kernel.org, linux-mm@kvack.org,
linux-kernel@vger.kernel.org, Dmitry Vyukov <dvyukov@google.com>,
linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org,
linaro-mm-sig@lists.linaro.org, kasan-dev@googlegroups.com,
Dietmar Eggemann <dietmar.eggemann@arm.com>,
Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
Valentin Schneider <vschneid@redhat.com>,
K Prateek Nayak <kprateek.nayak@amd.com>,
linux-rt-devel@lists.linux.dev,
"Vlastimil Babka (SUSE)" <vbabka@kernel.org>
Subject: [PATCH RFC 2/5] mm/slab, kfence: support kfence objects in kfree_nolock()
Date: Fri, 07 Aug 2026 15:50:28 +0200 [thread overview]
Message-ID: <20260807-kfree_nolock_kmalloc-v1-2-ba993cbf7a60@kernel.org> (raw)
In-Reply-To: <20260807-kfree_nolock_kmalloc-v1-0-ba993cbf7a60@kernel.org>
KFENCE objects are one of the reasons why kfree_nolock() cannot
currently handle kmalloc() objects. They are however rare so we can
simply defer their freeing to irq_work.
The only complication is where to put the llist node. We cannot use the
freepointer location like in defer_free() because for some caches it may
be outside the object area and KFENCE would detect writes there.
Since KFENCE already solves a similar situation when freeing objects
from SLAB_TYPESAFE_BY_RCU caches with an rcu_head in its internal
metadata, reuse that rcu_head also for the llist node. Introduce
kfence_obj_to_llnode() and kfence_llnode_to_obj() so SLAB can work with
this llist node without being exposed to KFENCE internals.
Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
---
include/linux/kfence.h | 5 +++++
mm/kfence/core.c | 14 ++++++++++++++
mm/kfence/kfence.h | 5 ++++-
mm/slub.c | 39 ++++++++++++++++++++++++++++++++++++---
4 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/include/linux/kfence.h b/include/linux/kfence.h
index e5822f6e7f27..00721c85258d 100644
--- a/include/linux/kfence.h
+++ b/include/linux/kfence.h
@@ -188,6 +188,9 @@ static __always_inline __must_check bool kfence_free(void *addr)
return true;
}
+struct llist_node *kfence_obj_to_llnode(void *addr);
+void *kfence_llnode_to_obj(struct llist_node *llnode);
+
/**
* kfence_handle_page_fault() - perform page fault handling for KFENCE pages
* @addr: faulting address
@@ -235,6 +238,8 @@ static inline size_t kfence_ksize(const void *addr) { return 0; }
static inline void *kfence_object_start(const void *addr) { return NULL; }
static inline void __kfence_free(void *addr) { }
static inline bool __must_check kfence_free(void *addr) { return false; }
+static inline struct llist_node *kfence_obj_to_llnode(void *addr) { return NULL; }
+static inline void *kfence_llnode_to_obj(struct llist_node *llnode) { return NULL; }
static inline bool __must_check kfence_handle_page_fault(unsigned long addr, bool is_write,
struct pt_regs *regs)
{
diff --git a/mm/kfence/core.c b/mm/kfence/core.c
index 6577bd76954e..42519d24687f 100644
--- a/mm/kfence/core.c
+++ b/mm/kfence/core.c
@@ -1271,6 +1271,20 @@ void __kfence_free(void *addr)
}
}
+struct llist_node *kfence_obj_to_llnode(void *addr)
+{
+ struct kfence_metadata *meta = addr_to_metadata((unsigned long)addr);
+
+ return &meta->llnode;
+}
+
+void *kfence_llnode_to_obj(struct llist_node *llnode)
+{
+ struct kfence_metadata *meta = container_of(llnode, struct kfence_metadata, llnode);
+
+ return (void *)meta->addr;
+}
+
bool kfence_handle_page_fault(unsigned long addr, bool is_write, struct pt_regs *regs)
{
const int page_index = (addr - (unsigned long)__kfence_pool) / PAGE_SIZE;
diff --git a/mm/kfence/kfence.h b/mm/kfence/kfence.h
index 1f618f9b0d12..0fca1dc2c794 100644
--- a/mm/kfence/kfence.h
+++ b/mm/kfence/kfence.h
@@ -58,7 +58,10 @@ struct kfence_track {
/* KFENCE metadata per guarded allocation. */
struct kfence_metadata {
struct list_head list __guarded_by(&kfence_freelist_lock); /* Freelist node. */
- struct rcu_head rcu_head; /* For delayed freeing. */
+ union {
+ struct rcu_head rcu_head; /* For delayed freeing. */
+ struct llist_node llnode; /* For kfree_nolock(). */
+ };
/*
* Lock protecting below data; to ensure consistency of the below data,
diff --git a/mm/slub.c b/mm/slub.c
index 044db93d64a0..2d7648b96bfa 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -4050,6 +4050,7 @@ static void flush_all(struct kmem_cache *s)
struct deferred_percpu_work {
struct llist_head objects;
+ struct llist_head objects_kfence;
struct llist_head objects_by_rcu;
struct llist_head rcu_sheaves;
struct irq_work work;
@@ -4059,6 +4060,7 @@ static void deferred_percpu_work_fn(struct irq_work *work);
static DEFINE_PER_CPU(struct deferred_percpu_work, deferred_percpu_work) = {
.objects = LLIST_HEAD_INIT(objects),
+ .objects_kfence = LLIST_HEAD_INIT(objects_kfence),
.objects_by_rcu = LLIST_HEAD_INIT(objects_by_rcu),
.rcu_sheaves = LLIST_HEAD_INIT(rcu_sheaves),
.work = IRQ_WORK_INIT(deferred_percpu_work_fn),
@@ -6399,6 +6401,13 @@ static void deferred_percpu_work_fn(struct irq_work *work)
stat(s, FREE_SLOWPATH);
}
+ llnode = llist_del_all(&dpw->objects_kfence);
+ llist_for_each_safe(pos, t, llnode) {
+ void *obj = kfence_llnode_to_obj(pos);
+
+ __kfence_free(obj);
+ }
+
llnode = llist_del_all(&dpw->objects_by_rcu);
llist_for_each_safe(pos, t, llnode) {
void *head = pos;
@@ -6431,6 +6440,21 @@ static void defer_free(struct kmem_cache *s, void *obj)
irq_work_queue(&dpw->work);
}
+static void defer_free_kfence(void *obj)
+{
+ struct deferred_percpu_work *dpw;
+ struct llist_node *llnode;
+
+ /* kasan_reset_tag() is not necessary, kfence objects are not tagged */
+ llnode = kfence_obj_to_llnode(obj);
+
+ guard(preempt)();
+
+ dpw = this_cpu_ptr(&deferred_percpu_work);
+ if (llist_add(llnode, &dpw->objects_kfence))
+ irq_work_queue(&dpw->work);
+}
+
void defer_kfree_rcu(struct kvfree_rcu_head *head)
{
struct deferred_percpu_work *dpw;
@@ -6758,10 +6782,13 @@ EXPORT_SYMBOL(kfree);
/*
* Can be called while holding raw_spinlock_t or from IRQ and NMI,
* but ONLY for objects allocated by kmalloc_nolock().
- * Debug checks (like kmemleak and kfence) were skipped on allocation,
- * hence
+ *
+ * In case kmemleak is enabled,
+ *
* obj = kmalloc(); kfree_nolock(obj);
- * will miss kmemleak/kfence book keeping and will cause false positives.
+ *
+ * will miss kmemleak book keeping and will cause false positives.
+ *
* large_kmalloc is not supported either.
*/
void kfree_nolock(const void *object)
@@ -6793,6 +6820,12 @@ void kfree_nolock(const void *object)
* since they take spinlocks or not safe from any context.
*/
kmsan_slab_free(s, x);
+
+ if (is_kfence_address(x)) {
+ defer_free_kfence(x);
+ return;
+ }
+
/*
* If KASAN finds a kernel bug it will do kasan_report_invalid_free()
* which will call raw_spin_lock_irqsave() which is technically
--
2.55.0
next prev parent reply other threads:[~2026-08-07 13:50 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 13:50 [PATCH RFC 0/5] allow kfree_nolock() handle kmalloc() objects Vlastimil Babka (SUSE)
2026-08-07 13:50 ` [PATCH RFC 1/5] mm/slab: cleanup deferred free handling Vlastimil Babka (SUSE)
2026-08-07 13:50 ` Vlastimil Babka (SUSE) [this message]
2026-08-07 13:50 ` [PATCH RFC 3/5] mm/slab, kmemleak: handle kmemleak freeing in kfree_nolock() Vlastimil Babka (SUSE)
2026-08-07 13:50 ` [PATCH RFC 4/5] mm/slab: handle large_kmalloc objects " Vlastimil Babka (SUSE)
2026-08-07 13:50 ` [PATCH RFC 5/5] sched: use kfree_nolock() instead of kfree_rcu() Vlastimil Babka (SUSE)
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=20260807-kfree_nolock_kmalloc-v1-2-ba993cbf7a60@kernel.org \
--to=vbabka@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=ast@kernel.org \
--cc=bigeasy@linutronix.de \
--cc=bpf@vger.kernel.org \
--cc=bsegall@google.com \
--cc=catalin.marinas@arm.com \
--cc=christian.koenig@amd.com \
--cc=cl@gentwo.org \
--cc=clrkwllms@kernel.org \
--cc=dietmar.eggemann@arm.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=dvyukov@google.com \
--cc=elver@google.com \
--cc=glider@google.com \
--cc=hao.li@linux.dev \
--cc=harry@kernel.org \
--cc=juri.lelli@redhat.com \
--cc=kasan-dev@googlegroups.com \
--cc=kprateek.nayak@amd.com \
--cc=linaro-mm-sig@lists.linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=rientjes@google.com \
--cc=roman.gushchin@linux.dev \
--cc=rostedt@goodmis.org \
--cc=sumit.semwal@linaro.org \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox