* [PATCH v2 0/4] mm/slub: preserve previous object lifetime
@ 2026-08-13 16:08 Pengpeng Hou
2026-08-13 16:10 ` [PATCH v2 1/4] mm/slub: use a track count for user metadata sizing Pengpeng Hou
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Pengpeng Hou @ 2026-08-13 16:08 UTC (permalink / raw)
To: Vlastimil Babka, Andrew Morton, linux-mm
Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
Roman Gushchin, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
Pengpeng Hou
SLAB_STORE_USER records one allocation and one free track per object. A
new allocation overwrites the allocation track, and a later stale free can
overwrite the free track, so the completed lifetime that produced the
stale reference is no longer available in the report.
Extend the existing U option directly, as discussed on the RFC, to retain
one previous completed allocation/free pair. The records remain address
history only; they do not infer semantic ownership. The series also adds
KUnit coverage and documents the additional report section.
Changes since v1:
https://lore.kernel.org/all/20260616141410.52117-1-pengpeng@iscas.ac.cn/
- extend U directly instead of adding a separate H option
- use TRACK_NR in every user-metadata layout calculation
- copy the completed pair with explicit track assignments
- keep the current free track until a later free replaces it
- pin the KUnit task across immediate free and reuse
Testing used Linux 3d6d817622b0, GCC 13.3 and QEMU 8.2.2 for x86_64.
The focused SLUB KUnit test passed, including immediate free/reuse and
verification that the completed allocation/free pair moved to the
previous-lifetime slots.
A temporary KUnit probe, not part of this series, recorded the internal
cache slot size and objects per order-0 slab before and after the series:
object bytes U slot/objects patched slot/objects
32 96/42 160/25
64 128/32 192/21
128 192/21 256/16
256 320/12 384/10
All four caches retained slab order 0. The table quantifies the expected
per-object metadata cost only for caches using SLAB_STORE_USER; caches
without U do not allocate these track records.
Pengpeng Hou (4):
mm/slub: use a track count for user metadata sizing
mm/slub: preserve one previous object lifetime
mm/slub: test previous lifetime tracking
Documentation/mm: describe SLUB previous lifetime tracking
Documentation/admin-guide/mm/slab.rst | 13 ++++-
lib/tests/slub_kunit.c | 35 ++++++++++++++
mm/slab.h | 4 ++
mm/slub.c | 69 ++++++++++++++++++++-------
4 files changed, 104 insertions(+), 17 deletions(-)
base-commit: 3d6d817622b0a9721e3cc404df3469171582be13
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/4] mm/slub: use a track count for user metadata sizing
2026-08-13 16:08 [PATCH v2 0/4] mm/slub: preserve previous object lifetime Pengpeng Hou
@ 2026-08-13 16:10 ` Pengpeng Hou
2026-08-17 10:04 ` Hao Li
2026-08-13 16:12 ` [PATCH v2 2/4] mm/slub: preserve one previous object lifetime Pengpeng Hou
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Pengpeng Hou @ 2026-08-13 16:10 UTC (permalink / raw)
To: Vlastimil Babka, Andrew Morton, linux-mm
Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
Roman Gushchin, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
Pengpeng Hou
SLAB_STORE_USER metadata consists of allocation and free tracks, but the
object-layout calculations repeat the literal count at each offset site.
Add TRACK_NR to the existing track-item enum and use it for metadata
initialization, original-size placement, object-extension offsets, padding
checks and cache sizing. This is a behavior-preserving preparation for
extending the tracked history.
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
mm/slub.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/mm/slub.c b/mm/slub.c
index 0337e60db5ac..0653def0fe36 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -329,7 +329,7 @@ struct track {
unsigned long when; /* When did the operation occur */
};
-enum track_item { TRACK_ALLOC, TRACK_FREE };
+enum track_item { TRACK_ALLOC, TRACK_FREE, TRACK_NR };
#ifdef SLAB_SUPPORTS_SYSFS
static int sysfs_slab_add(struct kmem_cache *);
@@ -753,7 +753,7 @@ static inline void set_orig_size(struct kmem_cache *s,
return;
p += get_info_end(s);
- p += sizeof(struct track) * 2;
+ p += sizeof(struct track) * TRACK_NR;
*(unsigned long *)p = orig_size;
}
@@ -769,7 +769,7 @@ static inline unsigned long get_orig_size(struct kmem_cache *s, void *object)
return s->object_size;
p += get_info_end(s);
- p += sizeof(struct track) * 2;
+ p += sizeof(struct track) * TRACK_NR;
return *(unsigned long *)p;
}
@@ -887,7 +887,7 @@ static unsigned int obj_exts_offset_in_object(struct kmem_cache *s)
unsigned int offset = get_info_end(s);
if (kmem_cache_debug_flags(s, SLAB_STORE_USER))
- offset += sizeof(struct track) * 2;
+ offset += sizeof(struct track) * TRACK_NR;
if (slub_debug_orig_size(s))
offset += sizeof(unsigned long);
@@ -1090,7 +1090,7 @@ static void init_tracking(struct kmem_cache *s, void *object)
return;
p = get_track(s, object, TRACK_ALLOC);
- memset(p, 0, 2*sizeof(struct track));
+ memset(p, 0, sizeof(struct track) * TRACK_NR);
}
static void print_track(const char *s, struct track *t, unsigned long pr_time)
@@ -1199,7 +1199,7 @@ static void print_trailer(struct kmem_cache *s, struct slab *slab, u8 *p)
off = get_info_end(s);
if (s->flags & SLAB_STORE_USER)
- off += 2 * sizeof(struct track);
+ off += sizeof(struct track) * TRACK_NR;
if (slub_debug_orig_size(s))
off += sizeof(unsigned long);
@@ -1403,7 +1403,7 @@ static int check_pad_bytes(struct kmem_cache *s, struct slab *slab, u8 *p)
if (s->flags & SLAB_STORE_USER) {
/* We also have user information there */
- off += 2 * sizeof(struct track);
+ off += sizeof(struct track) * TRACK_NR;
if (s->flags & SLAB_KMALLOC)
off += sizeof(unsigned long);
@@ -7895,7 +7895,7 @@ static int calculate_sizes(struct kmem_cache_args *args, struct kmem_cache *s)
* Need to store information about allocs and frees after
* the object.
*/
- size += 2 * sizeof(struct track);
+ size += sizeof(struct track) * TRACK_NR;
/* Save the original kmalloc request size */
if (flags & SLAB_KMALLOC)
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/4] mm/slub: preserve one previous object lifetime
2026-08-13 16:08 [PATCH v2 0/4] mm/slub: preserve previous object lifetime Pengpeng Hou
2026-08-13 16:10 ` [PATCH v2 1/4] mm/slub: use a track count for user metadata sizing Pengpeng Hou
@ 2026-08-13 16:12 ` Pengpeng Hou
2026-08-17 10:30 ` Hao Li
2026-08-13 16:14 ` [PATCH v2 3/4] mm/slub: test previous lifetime tracking Pengpeng Hou
2026-08-13 16:17 ` [PATCH v2 4/4] Documentation/mm: describe SLUB " Pengpeng Hou
3 siblings, 1 reply; 7+ messages in thread
From: Pengpeng Hou @ 2026-08-13 16:12 UTC (permalink / raw)
To: Vlastimil Babka, Andrew Morton, linux-mm
Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
Roman Gushchin, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
Pengpeng Hou
SLAB_STORE_USER replaces the allocation track when an object is reused. A
later stale free can then replace the free track as well, leaving the
report without the completed lifetime that created the stale reference.
Store one additional alloc/free pair. Before recording a new allocation,
copy the current pair to the previous slots only when both records exist.
Keep the current free track intact to preserve existing SLAB_STORE_USER
behavior during the reuse window.
Print the previous pair when available. These records are diagnostic
history and do not infer semantic ownership.
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
mm/slub.c | 45 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 36 insertions(+), 9 deletions(-)
diff --git a/mm/slub.c b/mm/slub.c
index 0653def0fe36..355fbffb981f 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -329,7 +329,13 @@ 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,
+};
#ifdef SLAB_SUPPORTS_SYSFS
static int sysfs_slab_add(struct kmem_cache *);
@@ -1074,12 +1080,23 @@ static void set_track_update(struct kmem_cache *s, void *object,
p->when = jiffies;
}
-static __always_inline void set_track(struct kmem_cache *s, void *object,
- enum track_item alloc, unsigned long addr, gfp_t gfp_flags)
+static __always_inline void set_alloc_track(struct kmem_cache *s, void *object,
+ unsigned long addr, gfp_t gfp_flags)
{
depot_stack_handle_t handle = set_track_prepare(gfp_flags);
+ struct track *alloc = get_track(s, object, TRACK_ALLOC);
+ struct track *free = get_track(s, object, TRACK_FREE);
+ struct track *prev_alloc;
+ struct track *prev_free;
+
+ if (alloc->addr && free->addr) {
+ prev_alloc = get_track(s, object, TRACK_PREV_ALLOC);
+ prev_free = get_track(s, object, TRACK_PREV_FREE);
+ *prev_alloc = *alloc;
+ *prev_free = *free;
+ }
- set_track_update(s, object, alloc, addr, handle);
+ set_track_update(s, object, TRACK_ALLOC, addr, handle);
}
static void init_tracking(struct kmem_cache *s, void *object)
@@ -1113,12 +1130,22 @@ static void print_track(const char *s, struct track *t, unsigned long pr_time)
void print_tracking(struct kmem_cache *s, void *object)
{
+ struct track *prev_alloc;
unsigned long pr_time = jiffies;
+
if (!(s->flags & SLAB_STORE_USER))
return;
print_track("Allocated", get_track(s, object, TRACK_ALLOC), pr_time);
print_track("Freed", get_track(s, object, TRACK_FREE), pr_time);
+
+ prev_alloc = get_track(s, object, TRACK_PREV_ALLOC);
+ if (!prev_alloc->addr)
+ return;
+
+ pr_err("Previous object lifetime:\n");
+ print_track("Allocated", prev_alloc, pr_time);
+ print_track("Freed", get_track(s, object, TRACK_PREV_FREE), pr_time);
}
static void print_slab_info(const struct slab *slab)
@@ -1366,8 +1393,8 @@ check_bytes_and_report(struct kmem_cache *s, struct slab *slab,
*
* [Metadata starts at object + s->inuse]
* - A. freelist pointer (if freeptr_outside_object)
- * - B. alloc tracking (SLAB_STORE_USER)
- * - C. free tracking (SLAB_STORE_USER)
+ * - B. current alloc/free tracking (SLAB_STORE_USER)
+ * - C. previous alloc/free tracking (SLAB_STORE_USER)
* - D. original request size (SLAB_KMALLOC && SLAB_STORE_USER)
* - E. KASAN metadata (if enabled)
*
@@ -2024,8 +2051,8 @@ static inline void slab_pad_check(struct kmem_cache *s, struct slab *slab) {}
static inline int check_object(struct kmem_cache *s, struct slab *slab,
void *object, u8 val) { return 1; }
static inline depot_stack_handle_t set_track_prepare(gfp_t gfp_flags) { return 0; }
-static inline void set_track(struct kmem_cache *s, void *object,
- enum track_item alloc, unsigned long addr, gfp_t gfp_flags) {}
+static inline void set_alloc_track(struct kmem_cache *s, void *object,
+ unsigned long addr, gfp_t gfp_flags) {}
static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n,
struct slab *slab) {}
static inline void remove_full(struct kmem_cache *s, struct kmem_cache_node *n,
@@ -4493,7 +4520,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
success:
if (kmem_cache_debug_flags(s, SLAB_STORE_USER))
- set_track(s, object, TRACK_ALLOC, ac->caller_addr, gfpflags);
+ set_alloc_track(s, object, ac->caller_addr, gfpflags);
return object;
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 3/4] mm/slub: test previous lifetime tracking
2026-08-13 16:08 [PATCH v2 0/4] mm/slub: preserve previous object lifetime Pengpeng Hou
2026-08-13 16:10 ` [PATCH v2 1/4] mm/slub: use a track count for user metadata sizing Pengpeng Hou
2026-08-13 16:12 ` [PATCH v2 2/4] mm/slub: preserve one previous object lifetime Pengpeng Hou
@ 2026-08-13 16:14 ` Pengpeng Hou
2026-08-13 16:17 ` [PATCH v2 4/4] Documentation/mm: describe SLUB " Pengpeng Hou
3 siblings, 0 replies; 7+ messages in thread
From: Pengpeng Hou @ 2026-08-13 16:14 UTC (permalink / raw)
To: Vlastimil Babka, Andrew Morton, linux-mm
Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
Roman Gushchin, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
Pengpeng Hou
Add KUnit coverage for the SLAB_STORE_USER reuse transition. The test
creates a non-mergeable cache, pins the task against migration, verifies
that a fresh object has no previous lifetime, then frees and immediately
reallocates the object and verifies that the completed pair was retained.
Exclude KASAN configurations because quarantine can intentionally prevent
immediate reuse.
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
lib/tests/slub_kunit.c | 35 +++++++++++++++++++++++++++++++++++
mm/slab.h | 4 ++++
mm/slub.c | 10 ++++++++++
3 files changed, 49 insertions(+)
diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c
index fa6d31dbca16..62d0f5e64268 100644
--- a/lib/tests/slub_kunit.c
+++ b/lib/tests/slub_kunit.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/rcupdate.h>
+#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/perf_event.h>
#include "../mm/slab.h"
@@ -106,6 +107,38 @@ static void test_first_word(struct kunit *test)
kmem_cache_destroy(s);
}
+static void test_store_user_previous_lifetime(struct kunit *test)
+{
+ struct kmem_cache *s;
+ void *p;
+ void *q;
+
+ s = test_kmem_cache_create("TestSlub_prev_lifetime", 64,
+ SLAB_STORE_USER | SLAB_NO_MERGE);
+ migrate_disable();
+ p = kmem_cache_alloc(s, GFP_KERNEL);
+ if (!p) {
+ KUNIT_FAIL(test, "failed to allocate the first object");
+ goto out_enable;
+ }
+ KUNIT_EXPECT_FALSE(test, slab_test_has_previous_lifetime(s, p));
+
+ kmem_cache_free(s, p);
+ q = kmem_cache_alloc(s, GFP_KERNEL);
+ if (q != p) {
+ KUNIT_FAIL(test, "freed object was not immediately reused");
+ if (q)
+ kmem_cache_free(s, q);
+ goto out_enable;
+ }
+ KUNIT_EXPECT_TRUE(test, slab_test_has_previous_lifetime(s, q));
+
+ kmem_cache_free(s, q);
+out_enable:
+ migrate_enable();
+ kmem_cache_destroy(s);
+}
+
static void test_clobber_50th_byte(struct kunit *test)
{
struct kmem_cache *s = test_kmem_cache_create("TestSlub_50th_word_free",
@@ -395,6 +428,7 @@ static struct kunit_case test_cases[] = {
#ifndef CONFIG_KASAN
KUNIT_CASE(test_next_pointer),
KUNIT_CASE(test_first_word),
+ KUNIT_CASE(test_store_user_previous_lifetime),
KUNIT_CASE(test_clobber_50th_byte),
#endif
@@ -419,3 +453,4 @@ kunit_test_suite(test_suite);
MODULE_DESCRIPTION("Kunit tests for slub allocator");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/mm/slab.h b/mm/slab.h
index f5e336b6b6b0..15634d9603f8 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -518,6 +518,10 @@ bool slab_in_kunit_test(void);
static inline bool slab_in_kunit_test(void) { return false; }
#endif
+#if IS_ENABLED(CONFIG_SLUB_KUNIT_TEST)
+bool slab_test_has_previous_lifetime(struct kmem_cache *s, void *object);
+#endif
+
/*
* slub is about to manipulate internal object metadata. This memory lies
* outside the range of the allocated object, so accessing it would normally
diff --git a/mm/slub.c b/mm/slub.c
index 355fbffb981f..d6b7487fb300 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -46,6 +46,7 @@
#include <linux/prandom.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
+#include <kunit/visibility.h>
#include <linux/sort.h>
#include <linux/irq_work.h>
#include <linux/kprobes.h>
@@ -1148,6 +1149,15 @@ void print_tracking(struct kmem_cache *s, void *object)
print_track("Freed", get_track(s, object, TRACK_PREV_FREE), pr_time);
}
+#if IS_ENABLED(CONFIG_SLUB_KUNIT_TEST)
+bool slab_test_has_previous_lifetime(struct kmem_cache *s, void *object)
+{
+ return get_track(s, object, TRACK_PREV_ALLOC)->addr &&
+ get_track(s, object, TRACK_PREV_FREE)->addr;
+}
+EXPORT_SYMBOL_IF_KUNIT(slab_test_has_previous_lifetime);
+#endif
+
static void print_slab_info(const struct slab *slab)
{
pr_err("Slab 0x%p objects=%u used=%u fp=0x%p flags=%pGp\n",
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 4/4] Documentation/mm: describe SLUB previous lifetime tracking
2026-08-13 16:08 [PATCH v2 0/4] mm/slub: preserve previous object lifetime Pengpeng Hou
` (2 preceding siblings ...)
2026-08-13 16:14 ` [PATCH v2 3/4] mm/slub: test previous lifetime tracking Pengpeng Hou
@ 2026-08-13 16:17 ` Pengpeng Hou
3 siblings, 0 replies; 7+ messages in thread
From: Pengpeng Hou @ 2026-08-13 16:17 UTC (permalink / raw)
To: Vlastimil Babka, Andrew Morton, linux-mm
Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
Roman Gushchin, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel,
Pengpeng Hou
Document that SLAB_STORE_USER now retains one previous completed
allocation/free pair, show the additional report section, and state that
address history is diagnostic evidence rather than semantic ownership
attribution.
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
Documentation/admin-guide/mm/slab.rst | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/mm/slab.rst b/Documentation/admin-guide/mm/slab.rst
index 14429ab90611..5f60904b1e0b 100644
--- a/Documentation/admin-guide/mm/slab.rst
+++ b/Documentation/admin-guide/mm/slab.rst
@@ -49,7 +49,8 @@ Possible debug options are::
Sorry SLAB legacy issues)
Z Red zoning
P Poisoning (object and padding)
- U User tracking (free and alloc)
+ U User tracking (free and alloc, plus one previous
+ completed object lifetime)
T Trace (please only use on single slabs)
A Enable failslab filter mark for the cache
O Switch debugging off for caches that would have
@@ -245,10 +246,20 @@ into the syslog:
cpu> pid=<pid of the process>
INFO: Freed in <kernel function> age=<jiffies since free> cpu=<freed by cpu>
pid=<pid of the process>
+ INFO: Previous object lifetime:
+ INFO: Allocated in <kernel function> age=<jiffies since alloc> cpu=<allocated by
+ cpu> pid=<pid of the process>
+ INFO: Freed in <kernel function> age=<jiffies since free> cpu=<freed by cpu>
+ pid=<pid of the process>
(Object allocation / free information is only available if SLAB_STORE_USER is
set for the slab. slab_debug sets that option)
+ If an object with a completed allocation/free pair is reused, user tracking
+ also retains that pair as the previous object lifetime. This address history
+ can help diagnose stale references after reuse, but it does not establish
+ semantic ownership or identify a use-after-free root cause by itself.
+
2. The object contents if an object was involved.
Various types of lines can follow the BUG SLUB line:
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/4] mm/slub: use a track count for user metadata sizing
2026-08-13 16:10 ` [PATCH v2 1/4] mm/slub: use a track count for user metadata sizing Pengpeng Hou
@ 2026-08-17 10:04 ` Hao Li
0 siblings, 0 replies; 7+ messages in thread
From: Hao Li @ 2026-08-17 10:04 UTC (permalink / raw)
To: Pengpeng Hou
Cc: Vlastimil Babka, Andrew Morton, linux-mm, Harry Yoo,
Christoph Lameter, David Rientjes, Roman Gushchin,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, linux-doc, linux-kernel
On Fri, Aug 14, 2026 at 12:10:59AM +0800, Pengpeng Hou wrote:
> SLAB_STORE_USER metadata consists of allocation and free tracks, but the
> object-layout calculations repeat the literal count at each offset site.
>
> Add TRACK_NR to the existing track-item enum and use it for metadata
> initialization, original-size placement, object-extension offsets, padding
> checks and cache sizing. This is a behavior-preserving preparation for
> extending the tracked history.
>
> Assisted-by: Codex:gpt-5
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> mm/slub.c | 16 ++++++++--------
> 1 file changed, 8 insertions(+), 8 deletions(-)
LGTM.
Reviewed-by: Hao Li <hao.li@linux.dev>
--
Thanks,
Hao
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/4] mm/slub: preserve one previous object lifetime
2026-08-13 16:12 ` [PATCH v2 2/4] mm/slub: preserve one previous object lifetime Pengpeng Hou
@ 2026-08-17 10:30 ` Hao Li
0 siblings, 0 replies; 7+ messages in thread
From: Hao Li @ 2026-08-17 10:30 UTC (permalink / raw)
To: Pengpeng Hou
Cc: Vlastimil Babka, Andrew Morton, linux-mm, Harry Yoo,
Christoph Lameter, David Rientjes, Roman Gushchin,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, linux-doc, linux-kernel
On Fri, Aug 14, 2026 at 12:12:44AM +0800, Pengpeng Hou wrote:
> SLAB_STORE_USER replaces the allocation track when an object is reused. A
> later stale free can then replace the free track as well, leaving the
> report without the completed lifetime that created the stale reference.
>
> Store one additional alloc/free pair. Before recording a new allocation,
> copy the current pair to the previous slots only when both records exist.
> Keep the current free track intact to preserve existing SLAB_STORE_USER
> behavior during the reuse window.
>
> Print the previous pair when available. These records are diagnostic
> history and do not infer semantic ownership.
>
> Assisted-by: Codex:gpt-5
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> mm/slub.c | 45 ++++++++++++++++++++++++++++++++++++---------
> 1 file changed, 36 insertions(+), 9 deletions(-)
>
> diff --git a/mm/slub.c b/mm/slub.c
> index 0653def0fe36..355fbffb981f 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -329,7 +329,13 @@ 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,
> +};
>
> #ifdef SLAB_SUPPORTS_SYSFS
> static int sysfs_slab_add(struct kmem_cache *);
> @@ -1074,12 +1080,23 @@ static void set_track_update(struct kmem_cache *s, void *object,
> p->when = jiffies;
> }
>
> -static __always_inline void set_track(struct kmem_cache *s, void *object,
> - enum track_item alloc, unsigned long addr, gfp_t gfp_flags)
> +static __always_inline void set_alloc_track(struct kmem_cache *s, void *object,
> + unsigned long addr, gfp_t gfp_flags)
> {
> depot_stack_handle_t handle = set_track_prepare(gfp_flags);
> + struct track *alloc = get_track(s, object, TRACK_ALLOC);
> + struct track *free = get_track(s, object, TRACK_FREE);
> + struct track *prev_alloc;
> + struct track *prev_free;
> +
> + if (alloc->addr && free->addr) {
> + prev_alloc = get_track(s, object, TRACK_PREV_ALLOC);
> + prev_free = get_track(s, object, TRACK_PREV_FREE);
> + *prev_alloc = *alloc;
> + *prev_free = *free;
> + }
>
> - set_track_update(s, object, alloc, addr, handle);
> + set_track_update(s, object, TRACK_ALLOC, addr, handle);
> }
>
> static void init_tracking(struct kmem_cache *s, void *object)
> @@ -1113,12 +1130,22 @@ static void print_track(const char *s, struct track *t, unsigned long pr_time)
>
> void print_tracking(struct kmem_cache *s, void *object)
> {
> + struct track *prev_alloc;
> unsigned long pr_time = jiffies;
> +
> if (!(s->flags & SLAB_STORE_USER))
> return;
>
> print_track("Allocated", get_track(s, object, TRACK_ALLOC), pr_time);
> print_track("Freed", get_track(s, object, TRACK_FREE), pr_time);
When object is in allocated state, under normal case, this "Freed" line
duplicates with the "Freed" line under "Previous object lifetime:"
Would it make sense to add a check here? something like:
if ("free track" isn't the same as "prev_free track")
print_track("Freed", get_track(s, object, TRACK_FREE), pr_time);
> +
> + prev_alloc = get_track(s, object, TRACK_PREV_ALLOC);
> + if (!prev_alloc->addr)
> + return;
> +
> + pr_err("Previous object lifetime:\n");
> + print_track("Allocated", prev_alloc, pr_time);
> + print_track("Freed", get_track(s, object, TRACK_PREV_FREE), pr_time);
> }
>
> static void print_slab_info(const struct slab *slab)
> @@ -1366,8 +1393,8 @@ check_bytes_and_report(struct kmem_cache *s, struct slab *slab,
> *
> * [Metadata starts at object + s->inuse]
> * - A. freelist pointer (if freeptr_outside_object)
> - * - B. alloc tracking (SLAB_STORE_USER)
> - * - C. free tracking (SLAB_STORE_USER)
> + * - B. current alloc/free tracking (SLAB_STORE_USER)
> + * - C. previous alloc/free tracking (SLAB_STORE_USER)
> * - D. original request size (SLAB_KMALLOC && SLAB_STORE_USER)
> * - E. KASAN metadata (if enabled)
> *
> @@ -2024,8 +2051,8 @@ static inline void slab_pad_check(struct kmem_cache *s, struct slab *slab) {}
> static inline int check_object(struct kmem_cache *s, struct slab *slab,
> void *object, u8 val) { return 1; }
> static inline depot_stack_handle_t set_track_prepare(gfp_t gfp_flags) { return 0; }
> -static inline void set_track(struct kmem_cache *s, void *object,
> - enum track_item alloc, unsigned long addr, gfp_t gfp_flags) {}
> +static inline void set_alloc_track(struct kmem_cache *s, void *object,
> + unsigned long addr, gfp_t gfp_flags) {}
> static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n,
> struct slab *slab) {}
> static inline void remove_full(struct kmem_cache *s, struct kmem_cache_node *n,
> @@ -4493,7 +4520,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
>
> success:
> if (kmem_cache_debug_flags(s, SLAB_STORE_USER))
> - set_track(s, object, TRACK_ALLOC, ac->caller_addr, gfpflags);
> + set_alloc_track(s, object, ac->caller_addr, gfpflags);
>
> return object;
> }
> --
> 2.50.1 (Apple Git-155)
>
--
Thanks,
Hao
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-08-17 10:31 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 16:08 [PATCH v2 0/4] mm/slub: preserve previous object lifetime Pengpeng Hou
2026-08-13 16:10 ` [PATCH v2 1/4] mm/slub: use a track count for user metadata sizing Pengpeng Hou
2026-08-17 10:04 ` Hao Li
2026-08-13 16:12 ` [PATCH v2 2/4] mm/slub: preserve one previous object lifetime Pengpeng Hou
2026-08-17 10:30 ` Hao Li
2026-08-13 16:14 ` [PATCH v2 3/4] mm/slub: test previous lifetime tracking Pengpeng Hou
2026-08-13 16:17 ` [PATCH v2 4/4] Documentation/mm: describe SLUB " Pengpeng Hou
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.