[RFC PATCH 2/5] mm/slub: add optional previous lifetime user tracking
From: Pengpeng Hou
Date: Thu Jun 11 2026 - 02:41:06 EST
SLAB_STORE_USER currently reserves two per-object tracks, one for the
allocation and one for the free. Add an opt-in SLAB_STORE_HISTORY flag
that extends the user tracking area to hold one previous completed
lifetime.
Expose the option as slab_debug=H, but require it to be used together
with U. This avoids silently enabling user tracking and its stack depot
cost when a user only requested H. The option is not part of the default
debug flags.
No history is recorded or printed yet; this only adds the flag, the
object metadata layout, and the sysfs state file.
Signed-off-by: Pengpeng Hou <pengpeng@xxxxxxxxxxx>
---
include/linux/slab.h | 3 +++
mm/slab.h | 3 ++-
mm/slub.c | 36 +++++++++++++++++++++++++++++++-----
3 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 2b5ab488e96b..78b9ec5bc17a 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -32,6 +32,7 @@ enum _slab_flag_bits {
_SLAB_CACHE_DMA,
_SLAB_CACHE_DMA32,
_SLAB_STORE_USER,
+ _SLAB_STORE_HISTORY,
_SLAB_PANIC,
_SLAB_TYPESAFE_BY_RCU,
_SLAB_TRACE,
@@ -98,6 +99,8 @@ enum _slab_flag_bits {
#define SLAB_CACHE_DMA32 __SLAB_FLAG_BIT(_SLAB_CACHE_DMA32)
/* DEBUG: Store the last owner for bug hunting */
#define SLAB_STORE_USER __SLAB_FLAG_BIT(_SLAB_STORE_USER)
+/* DEBUG: Store the previous object lifetime for bug hunting */
+#define SLAB_STORE_HISTORY __SLAB_FLAG_BIT(_SLAB_STORE_HISTORY)
/* Panic if kmem_cache_create() fails */
#define SLAB_PANIC __SLAB_FLAG_BIT(_SLAB_PANIC)
/**
diff --git a/mm/slab.h b/mm/slab.h
index bf2f87acf5e3..a6af35829f79 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -417,7 +417,8 @@ void flush_rcu_sheaves_on_cache(struct kmem_cache *s);
SLAB_NO_USER_FLAGS | SLAB_KMALLOC | SLAB_NO_MERGE)
#define SLAB_DEBUG_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
- SLAB_TRACE | SLAB_CONSISTENCY_CHECKS)
+ SLAB_STORE_HISTORY | SLAB_TRACE | \
+ SLAB_CONSISTENCY_CHECKS)
#define SLAB_FLAGS_PERMITTED (SLAB_CORE_FLAGS | SLAB_DEBUG_FLAGS)
diff --git a/mm/slub.c b/mm/slub.c
index a9114dddc976..803c597351ce 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -277,7 +277,7 @@ void *fixup_red_left(struct kmem_cache *s, void *p)
* issues when checking or reading debug information
*/
#define SLAB_NO_CMPXCHG (SLAB_CONSISTENCY_CHECKS | SLAB_STORE_USER | \
- SLAB_TRACE)
+ SLAB_STORE_HISTORY | SLAB_TRACE)
/*
@@ -285,7 +285,8 @@ void *fixup_red_left(struct kmem_cache *s, void *p)
* disabled when slab_debug=O is used and a cache's min order increases with
* metadata.
*/
-#define DEBUG_METADATA_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER)
+#define DEBUG_METADATA_FLAGS (SLAB_RED_ZONE | SLAB_POISON | \
+ SLAB_STORE_USER | SLAB_STORE_HISTORY)
#define OO_SHIFT 16
#define OO_MASK ((1 << OO_SHIFT) - 1)
@@ -316,14 +317,23 @@ struct track {
unsigned long when; /* When did the operation occur */
};
-enum track_item { TRACK_ALLOC, TRACK_FREE, TRACK_NR };
+enum track_item {
+ TRACK_ALLOC,
+ TRACK_FREE,
+ TRACK_PREV_ALLOC,
+ TRACK_PREV_FREE,
+ TRACK_NR,
+};
static inline unsigned int nr_user_tracks(struct kmem_cache *s)
{
if (!(s->flags & SLAB_STORE_USER))
return 0;
- return TRACK_NR;
+ if (s->flags & SLAB_STORE_HISTORY)
+ return TRACK_NR;
+
+ return TRACK_PREV_ALLOC;
}
static inline unsigned int user_tracking_size(struct kmem_cache *s)
@@ -1837,6 +1847,9 @@ parse_slub_debug_flags(const char *str, slab_flags_t *flags, const char **slabs,
case 'u':
*flags |= SLAB_STORE_USER;
break;
+ case 'h':
+ *flags |= SLAB_STORE_HISTORY;
+ break;
case 't':
*flags |= SLAB_TRACE;
break;
@@ -1855,6 +1868,11 @@ parse_slub_debug_flags(const char *str, slab_flags_t *flags, const char **slabs,
pr_err("slab_debug option '%c' unknown. skipped\n", *str);
}
}
+ if ((*flags & SLAB_STORE_HISTORY) && !(*flags & SLAB_STORE_USER)) {
+ if (init)
+ pr_err("slab_debug option 'H' requires 'U'. skipped\n");
+ *flags &= ~SLAB_STORE_HISTORY;
+ }
check_slabs:
if (*str == ',')
*slabs = ++str;
@@ -1969,7 +1987,7 @@ slab_flags_t kmem_cache_flags(slab_flags_t flags, const char *name)
* but let the user enable it via the command line below.
*/
if (flags & SLAB_NOLEAKTRACE)
- slub_debug_local &= ~SLAB_STORE_USER;
+ slub_debug_local &= ~(SLAB_STORE_USER | SLAB_STORE_HISTORY);
len = strlen(name);
next_block = slub_debug_string;
@@ -9223,6 +9241,13 @@ static ssize_t store_user_show(struct kmem_cache *s, char *buf)
SLAB_ATTR_RO(store_user);
+static ssize_t store_history_show(struct kmem_cache *s, char *buf)
+{
+ return sysfs_emit(buf, "%d\n", !!(s->flags & SLAB_STORE_HISTORY));
+}
+
+SLAB_ATTR_RO(store_history);
+
static ssize_t validate_show(struct kmem_cache *s, char *buf)
{
return 0;
@@ -9442,6 +9467,7 @@ static const struct attribute *const slab_attrs[] = {
&red_zone_attr.attr,
&poison_attr.attr,
&store_user_attr.attr,
+ &store_history_attr.attr,
&validate_attr.attr,
#endif
#ifdef CONFIG_ZONE_DMA
--
2.50.1 (Apple Git-155)