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 --- 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)