summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorHarry Yoo (Oracle) <harry@kernel.org>2026-07-29 17:20:15 +0900
committerVlastimil Babka (SUSE) <vbabka@kernel.org>2026-08-04 12:18:29 +0200
commit3bc999d944b35dead1755b2bde1911cd5892225e (patch)
treed1e5725e49eaacc728f6b269dd0dcc770b198742 /include/linux
parentacc6fdade62c11d822f7b73f16092ebd365fc1c2 (diff)
mm/slab: introduce kfree_rcu_nolock()
Currently, k[v]free_rcu() cannot be called in unknown context since it could lead to a deadlock when called in the middle of k[v]free_rcu(). Make users' lives easier by introducing kfree_rcu_nolock() variant, now that kfree_rcu_sheaf() is available on PREEMPT_RT and __kfree_rcu_sheaf() handles unknown context. When sheaves path fails, kfree_rcu_nolock() falls back to defer_kfree_rcu() that uses an irq work to free the object via kvfree_call_rcu(). In most cases, the sheaves path is expected to succeed and therefore it's unnecessary to introduce additional complexity to the existing kvfree_rcu batching by teaching it how to handle unknown context. Since defer_kfree_rcu() can be called on caches without sheaves, move deferred_work_barrier() and rcu_barrier() outside the branch in kvfree_rcu_barrier_on_cache(). Now that deferred kvfree_rcu objects are submitted to kvfree_call_rcu() after deferred_work_barrier() and may end up in RCU sheaves, deferred_work_barrier() must be invoked before flush_rcu_sheaves_on_cache(). Since the RCU sheaf path has not been used on !KVFREE_RCU_BATCHED kernels, always fall back when kvfree_rcu() is not batched, for consistency. kvfree_rcu_barrier{,_on_cache()}() on !KVFREE_RCU_BATCHED are moved to mm/slab_common.c to invoke deferred_work_barrier() before rcu_barrier(). Signed-off-by: Harry Yoo (Oracle) <harry@kernel.org> Link: https://patch.msgid.link/20260729-kfree_rcu_nolock-v5-7-a28cdcda9673@kernel.org Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/rcupdate.h22
-rw-r--r--include/linux/slab.h16
2 files changed, 25 insertions, 13 deletions
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index ef5bb6981133..aede77bd0387 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -1099,6 +1099,7 @@ static inline void rcu_read_unlock_migrate(void)
* In mm/slab_common.c, no suitable header to include here.
*/
void kvfree_call_rcu(struct kvfree_rcu_head *head, void *ptr);
+void kfree_call_rcu_nolock(struct kvfree_rcu_head *head, void *ptr);
/*
* The BUILD_BUG_ON() makes sure the rcu_head offset can be handled. See the
@@ -1124,6 +1125,27 @@ do { \
kvfree_call_rcu(NULL, (void *) (___p)); \
} while (0)
+/**
+ * kfree_rcu_nolock() - a version of kfree_rcu() that can be called in any context.
+ * @ptr: pointer to kfree for double-argument invocations.
+ * @kvrhf: the name of the struct kvfree_rcu_head within the type of @ptr.
+ *
+ * With KVFREE_RCU_BATCHED, kfree_rcu_nolock() tries hard to free objects
+ * without any deferred processing, but may still defer freeing.
+ * Large kmalloc and vmalloc objects are always deferred.
+ *
+ * kfree_rcu_nolock() supports 2-arg variant only.
+ */
+#define kfree_rcu_nolock(ptr, kvrhf) \
+do { \
+ typeof (ptr) ___p = (ptr); \
+ \
+ if (___p) { \
+ BUILD_BUG_ON(offsetof(typeof(*(ptr)), kvrhf) >= 4096); \
+ kfree_call_rcu_nolock(&((___p)->kvrhf), (void *) (___p)); \
+ } \
+} while (0)
+
/*
* Place this after a lock-acquisition primitive to guarantee that
* an UNLOCK+LOCK pair acts as a full barrier. This guarantee applies
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 32c9f8ed7ae2..066a25cc6966 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -1427,25 +1427,15 @@ extern void kvfree_sensitive(const void *addr, size_t len);
unsigned int kmem_cache_size(struct kmem_cache *s);
#ifndef CONFIG_KVFREE_RCU_BATCHED
-static inline void kvfree_rcu_barrier(void)
-{
- rcu_barrier();
-}
-
-static inline void kvfree_rcu_barrier_on_cache(struct kmem_cache *s)
-{
- rcu_barrier();
-}
-
static inline void kfree_rcu_scheduler_running(void) { }
#else
+void kfree_rcu_scheduler_running(void);
+#endif
+
void kvfree_rcu_barrier(void);
void kvfree_rcu_barrier_on_cache(struct kmem_cache *s);
-void kfree_rcu_scheduler_running(void);
-#endif
-
/**
* kmalloc_size_roundup - Report allocation bucket size for the given size
*