forked from mirrors/linux
		
	Merge branches 'slab/for-5.19/stackdepot' and 'slab/for-5.19/refactor' into slab/for-linus
This commit is contained in:
		
						commit
						e001897da6
					
				
					 9 changed files with 238 additions and 91 deletions
				
			
		| 
						 | 
					@ -384,5 +384,69 @@ c) Execute ``slabinfo-gnuplot.sh`` in '-t' mode, passing all of the
 | 
				
			||||||
      40,60`` range will plot only samples collected between 40th and
 | 
					      40,60`` range will plot only samples collected between 40th and
 | 
				
			||||||
      60th seconds).
 | 
					      60th seconds).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DebugFS files for SLUB
 | 
				
			||||||
 | 
					======================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information about current state of SLUB caches with the user tracking
 | 
				
			||||||
 | 
					debug option enabled, debugfs files are available, typically under
 | 
				
			||||||
 | 
					/sys/kernel/debug/slab/<cache>/ (created only for caches with enabled user
 | 
				
			||||||
 | 
					tracking). There are 2 types of these files with the following debug
 | 
				
			||||||
 | 
					information:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. alloc_traces::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Prints information about unique allocation traces of the currently
 | 
				
			||||||
 | 
					    allocated objects. The output is sorted by frequency of each trace.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Information in the output:
 | 
				
			||||||
 | 
					    Number of objects, allocating function, minimal/average/maximal jiffies since alloc,
 | 
				
			||||||
 | 
					    pid range of the allocating processes, cpu mask of allocating cpus, and stack trace.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Example:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    1085 populate_error_injection_list+0x97/0x110 age=166678/166680/166682 pid=1 cpus=1::
 | 
				
			||||||
 | 
						__slab_alloc+0x6d/0x90
 | 
				
			||||||
 | 
						kmem_cache_alloc_trace+0x2eb/0x300
 | 
				
			||||||
 | 
						populate_error_injection_list+0x97/0x110
 | 
				
			||||||
 | 
						init_error_injection+0x1b/0x71
 | 
				
			||||||
 | 
						do_one_initcall+0x5f/0x2d0
 | 
				
			||||||
 | 
						kernel_init_freeable+0x26f/0x2d7
 | 
				
			||||||
 | 
						kernel_init+0xe/0x118
 | 
				
			||||||
 | 
						ret_from_fork+0x22/0x30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. free_traces::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Prints information about unique freeing traces of the currently allocated
 | 
				
			||||||
 | 
					    objects. The freeing traces thus come from the previous life-cycle of the
 | 
				
			||||||
 | 
					    objects and are reported as not available for objects allocated for the first
 | 
				
			||||||
 | 
					    time. The output is sorted by frequency of each trace.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Information in the output:
 | 
				
			||||||
 | 
					    Number of objects, freeing function, minimal/average/maximal jiffies since free,
 | 
				
			||||||
 | 
					    pid range of the freeing processes, cpu mask of freeing cpus, and stack trace.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Example:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    1980 <not-available> age=4294912290 pid=0 cpus=0
 | 
				
			||||||
 | 
					    51 acpi_ut_update_ref_count+0x6a6/0x782 age=236886/237027/237772 pid=1 cpus=1
 | 
				
			||||||
 | 
						kfree+0x2db/0x420
 | 
				
			||||||
 | 
						acpi_ut_update_ref_count+0x6a6/0x782
 | 
				
			||||||
 | 
						acpi_ut_update_object_reference+0x1ad/0x234
 | 
				
			||||||
 | 
						acpi_ut_remove_reference+0x7d/0x84
 | 
				
			||||||
 | 
						acpi_rs_get_prt_method_data+0x97/0xd6
 | 
				
			||||||
 | 
						acpi_get_irq_routing_table+0x82/0xc4
 | 
				
			||||||
 | 
						acpi_pci_irq_find_prt_entry+0x8e/0x2e0
 | 
				
			||||||
 | 
						acpi_pci_irq_lookup+0x3a/0x1e0
 | 
				
			||||||
 | 
						acpi_pci_irq_enable+0x77/0x240
 | 
				
			||||||
 | 
						pcibios_enable_device+0x39/0x40
 | 
				
			||||||
 | 
						do_pci_enable_device.part.0+0x5d/0xe0
 | 
				
			||||||
 | 
						pci_enable_device_flags+0xfc/0x120
 | 
				
			||||||
 | 
						pci_enable_device+0x13/0x20
 | 
				
			||||||
 | 
						virtio_pci_probe+0x9e/0x170
 | 
				
			||||||
 | 
						local_pci_probe+0x48/0x80
 | 
				
			||||||
 | 
						pci_device_probe+0x105/0x1c0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Christoph Lameter, May 30, 2007
 | 
					Christoph Lameter, May 30, 2007
 | 
				
			||||||
Sergey Senozhatsky, October 23, 2015
 | 
					Sergey Senozhatsky, October 23, 2015
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,7 +105,6 @@ struct kmem_cache {
 | 
				
			||||||
	struct kmem_cache_order_objects oo;
 | 
						struct kmem_cache_order_objects oo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Allocation and freeing of slabs */
 | 
						/* Allocation and freeing of slabs */
 | 
				
			||||||
	struct kmem_cache_order_objects max;
 | 
					 | 
				
			||||||
	struct kmem_cache_order_objects min;
 | 
						struct kmem_cache_order_objects min;
 | 
				
			||||||
	gfp_t allocflags;	/* gfp flags to use on each alloc */
 | 
						gfp_t allocflags;	/* gfp flags to use on each alloc */
 | 
				
			||||||
	int refcount;		/* Refcount for slab cache destroy */
 | 
						int refcount;		/* Refcount for slab cache destroy */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,18 +20,36 @@ depot_stack_handle_t __stack_depot_save(unsigned long *entries,
 | 
				
			||||||
					gfp_t gfp_flags, bool can_alloc);
 | 
										gfp_t gfp_flags, bool can_alloc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Every user of stack depot has to call this during its own init when it's
 | 
					 * Every user of stack depot has to call stack_depot_init() during its own init
 | 
				
			||||||
 * decided that it will be calling stack_depot_save() later.
 | 
					 * when it's decided that it will be calling stack_depot_save() later. This is
 | 
				
			||||||
 | 
					 * recommended for e.g. modules initialized later in the boot process, when
 | 
				
			||||||
 | 
					 * slab_is_available() is true.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * The alternative is to select STACKDEPOT_ALWAYS_INIT to have stack depot
 | 
					 * The alternative is to select STACKDEPOT_ALWAYS_INIT to have stack depot
 | 
				
			||||||
 * enabled as part of mm_init(), for subsystems where it's known at compile time
 | 
					 * enabled as part of mm_init(), for subsystems where it's known at compile time
 | 
				
			||||||
 * that stack depot will be used.
 | 
					 * that stack depot will be used.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Another alternative is to call stack_depot_want_early_init(), when the
 | 
				
			||||||
 | 
					 * decision to use stack depot is taken e.g. when evaluating kernel boot
 | 
				
			||||||
 | 
					 * parameters, which precedes the enablement point in mm_init().
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * stack_depot_init() and stack_depot_want_early_init() can be called regardless
 | 
				
			||||||
 | 
					 * of CONFIG_STACKDEPOT and are no-op when disabled. The actual save/fetch/print
 | 
				
			||||||
 | 
					 * functions should only be called from code that makes sure CONFIG_STACKDEPOT
 | 
				
			||||||
 | 
					 * is enabled.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					#ifdef CONFIG_STACKDEPOT
 | 
				
			||||||
int stack_depot_init(void);
 | 
					int stack_depot_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_STACKDEPOT_ALWAYS_INIT
 | 
					void __init stack_depot_want_early_init(void);
 | 
				
			||||||
static inline int stack_depot_early_init(void)	{ return stack_depot_init(); }
 | 
					
 | 
				
			||||||
 | 
					/* This is supposed to be called only from mm_init() */
 | 
				
			||||||
 | 
					int __init stack_depot_early_init(void);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
 | 
					static inline int stack_depot_init(void) { return 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void stack_depot_want_early_init(void) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline int stack_depot_early_init(void)	{ return 0; }
 | 
					static inline int stack_depot_early_init(void)	{ return 0; }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1875,6 +1875,7 @@ config SLUB_DEBUG
 | 
				
			||||||
	default y
 | 
						default y
 | 
				
			||||||
	bool "Enable SLUB debugging support" if EXPERT
 | 
						bool "Enable SLUB debugging support" if EXPERT
 | 
				
			||||||
	depends on SLUB && SYSFS
 | 
						depends on SLUB && SYSFS
 | 
				
			||||||
 | 
						select STACKDEPOT if STACKTRACE_SUPPORT
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  SLUB has extensive debug support features. Disabling these can
 | 
						  SLUB has extensive debug support features. Disabling these can
 | 
				
			||||||
	  result in significant savings in code size. This also disables
 | 
						  result in significant savings in code size. This also disables
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -709,6 +709,7 @@ config DEBUG_SLAB
 | 
				
			||||||
config SLUB_DEBUG_ON
 | 
					config SLUB_DEBUG_ON
 | 
				
			||||||
	bool "SLUB debugging on by default"
 | 
						bool "SLUB debugging on by default"
 | 
				
			||||||
	depends on SLUB && SLUB_DEBUG
 | 
						depends on SLUB && SLUB_DEBUG
 | 
				
			||||||
 | 
						select STACKDEPOT_ALWAYS_INIT if STACKTRACE_SUPPORT
 | 
				
			||||||
	default n
 | 
						default n
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  Boot with debugging on by default. SLUB boots by default with
 | 
						  Boot with debugging on by default. SLUB boots by default with
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,6 +66,9 @@ struct stack_record {
 | 
				
			||||||
	unsigned long entries[];	/* Variable-sized array of entries. */
 | 
						unsigned long entries[];	/* Variable-sized array of entries. */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool __stack_depot_want_early_init __initdata = IS_ENABLED(CONFIG_STACKDEPOT_ALWAYS_INIT);
 | 
				
			||||||
 | 
					static bool __stack_depot_early_init_passed __initdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void *stack_slabs[STACK_ALLOC_MAX_SLABS];
 | 
					static void *stack_slabs[STACK_ALLOC_MAX_SLABS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int depot_index;
 | 
					static int depot_index;
 | 
				
			||||||
| 
						 | 
					@ -162,38 +165,58 @@ static int __init is_stack_depot_disabled(char *str)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
early_param("stack_depot_disable", is_stack_depot_disabled);
 | 
					early_param("stack_depot_disable", is_stack_depot_disabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					void __init stack_depot_want_early_init(void)
 | 
				
			||||||
 * __ref because of memblock_alloc(), which will not be actually called after
 | 
					{
 | 
				
			||||||
 * the __init code is gone, because at that point slab_is_available() is true
 | 
						/* Too late to request early init now */
 | 
				
			||||||
 */
 | 
						WARN_ON(__stack_depot_early_init_passed);
 | 
				
			||||||
__ref int stack_depot_init(void)
 | 
					
 | 
				
			||||||
 | 
						__stack_depot_want_early_init = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int __init stack_depot_early_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* This is supposed to be called only once, from mm_init() */
 | 
				
			||||||
 | 
						if (WARN_ON(__stack_depot_early_init_passed))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__stack_depot_early_init_passed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!__stack_depot_want_early_init || stack_depot_disable)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size = (STACK_HASH_SIZE * sizeof(struct stack_record *));
 | 
				
			||||||
 | 
						pr_info("Stack Depot early init allocating hash table with memblock_alloc, %zu bytes\n",
 | 
				
			||||||
 | 
							size);
 | 
				
			||||||
 | 
						stack_table = memblock_alloc(size, SMP_CACHE_BYTES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!stack_table) {
 | 
				
			||||||
 | 
							pr_err("Stack Depot hash table allocation failed, disabling\n");
 | 
				
			||||||
 | 
							stack_depot_disable = true;
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int stack_depot_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	static DEFINE_MUTEX(stack_depot_init_mutex);
 | 
						static DEFINE_MUTEX(stack_depot_init_mutex);
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&stack_depot_init_mutex);
 | 
						mutex_lock(&stack_depot_init_mutex);
 | 
				
			||||||
	if (!stack_depot_disable && !stack_table) {
 | 
						if (!stack_depot_disable && !stack_table) {
 | 
				
			||||||
		size_t size = (STACK_HASH_SIZE * sizeof(struct stack_record *));
 | 
							pr_info("Stack Depot allocating hash table with kvcalloc\n");
 | 
				
			||||||
		int i;
 | 
							stack_table = kvcalloc(STACK_HASH_SIZE, sizeof(struct stack_record *), GFP_KERNEL);
 | 
				
			||||||
 | 
							if (!stack_table) {
 | 
				
			||||||
		if (slab_is_available()) {
 | 
					 | 
				
			||||||
			pr_info("Stack Depot allocating hash table with kvmalloc\n");
 | 
					 | 
				
			||||||
			stack_table = kvmalloc(size, GFP_KERNEL);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			pr_info("Stack Depot allocating hash table with memblock_alloc\n");
 | 
					 | 
				
			||||||
			stack_table = memblock_alloc(size, SMP_CACHE_BYTES);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (stack_table) {
 | 
					 | 
				
			||||||
			for (i = 0; i < STACK_HASH_SIZE;  i++)
 | 
					 | 
				
			||||||
				stack_table[i] = NULL;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			pr_err("Stack Depot hash table allocation failed, disabling\n");
 | 
								pr_err("Stack Depot hash table allocation failed, disabling\n");
 | 
				
			||||||
			stack_depot_disable = true;
 | 
								stack_depot_disable = true;
 | 
				
			||||||
			mutex_unlock(&stack_depot_init_mutex);
 | 
								ret = -ENOMEM;
 | 
				
			||||||
			return -ENOMEM;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mutex_unlock(&stack_depot_init_mutex);
 | 
						mutex_unlock(&stack_depot_init_mutex);
 | 
				
			||||||
	return 0;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(stack_depot_init);
 | 
					EXPORT_SYMBOL_GPL(stack_depot_init);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,12 @@ static void init_early_allocated_pages(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __init early_page_owner_param(char *buf)
 | 
					static int __init early_page_owner_param(char *buf)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return kstrtobool(buf, &page_owner_enabled);
 | 
						int ret = kstrtobool(buf, &page_owner_enabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (page_owner_enabled)
 | 
				
			||||||
 | 
							stack_depot_want_early_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
early_param("page_owner", early_page_owner_param);
 | 
					early_param("page_owner", early_page_owner_param);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,8 +88,6 @@ static __init void init_page_owner(void)
 | 
				
			||||||
	if (!page_owner_enabled)
 | 
						if (!page_owner_enabled)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stack_depot_init();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	register_dummy_stack();
 | 
						register_dummy_stack();
 | 
				
			||||||
	register_failure_stack();
 | 
						register_failure_stack();
 | 
				
			||||||
	register_early_stack();
 | 
						register_early_stack();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,6 +24,7 @@
 | 
				
			||||||
#include <asm/tlbflush.h>
 | 
					#include <asm/tlbflush.h>
 | 
				
			||||||
#include <asm/page.h>
 | 
					#include <asm/page.h>
 | 
				
			||||||
#include <linux/memcontrol.h>
 | 
					#include <linux/memcontrol.h>
 | 
				
			||||||
 | 
					#include <linux/stackdepot.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CREATE_TRACE_POINTS
 | 
					#define CREATE_TRACE_POINTS
 | 
				
			||||||
#include <trace/events/kmem.h>
 | 
					#include <trace/events/kmem.h>
 | 
				
			||||||
| 
						 | 
					@ -314,9 +315,13 @@ kmem_cache_create_usercopy(const char *name,
 | 
				
			||||||
	 * If no slub_debug was enabled globally, the static key is not yet
 | 
						 * If no slub_debug was enabled globally, the static key is not yet
 | 
				
			||||||
	 * enabled by setup_slub_debug(). Enable it if the cache is being
 | 
						 * enabled by setup_slub_debug(). Enable it if the cache is being
 | 
				
			||||||
	 * created with any of the debugging flags passed explicitly.
 | 
						 * created with any of the debugging flags passed explicitly.
 | 
				
			||||||
 | 
						 * It's also possible that this is the first cache created with
 | 
				
			||||||
 | 
						 * SLAB_STORE_USER and we should init stack_depot for it.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (flags & SLAB_DEBUG_FLAGS)
 | 
						if (flags & SLAB_DEBUG_FLAGS)
 | 
				
			||||||
		static_branch_enable(&slub_debug_enabled);
 | 
							static_branch_enable(&slub_debug_enabled);
 | 
				
			||||||
 | 
						if (flags & SLAB_STORE_USER)
 | 
				
			||||||
 | 
							stack_depot_init();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&slab_mutex);
 | 
						mutex_lock(&slab_mutex);
 | 
				
			||||||
| 
						 | 
					@ -849,6 +854,8 @@ new_kmalloc_cache(int idx, enum kmalloc_cache_type type, slab_flags_t flags)
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		flags |= SLAB_ACCOUNT;
 | 
							flags |= SLAB_ACCOUNT;
 | 
				
			||||||
 | 
						} else if (IS_ENABLED(CONFIG_ZONE_DMA) && (type == KMALLOC_DMA)) {
 | 
				
			||||||
 | 
							flags |= SLAB_CACHE_DMA;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kmalloc_caches[type][idx] = create_kmalloc_cache(
 | 
						kmalloc_caches[type][idx] = create_kmalloc_cache(
 | 
				
			||||||
| 
						 | 
					@ -877,7 +884,7 @@ void __init create_kmalloc_caches(slab_flags_t flags)
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Including KMALLOC_CGROUP if CONFIG_MEMCG_KMEM defined
 | 
						 * Including KMALLOC_CGROUP if CONFIG_MEMCG_KMEM defined
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	for (type = KMALLOC_NORMAL; type <= KMALLOC_RECLAIM; type++) {
 | 
						for (type = KMALLOC_NORMAL; type < NR_KMALLOC_TYPES; type++) {
 | 
				
			||||||
		for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
 | 
							for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
 | 
				
			||||||
			if (!kmalloc_caches[type][i])
 | 
								if (!kmalloc_caches[type][i])
 | 
				
			||||||
				new_kmalloc_cache(i, type, flags);
 | 
									new_kmalloc_cache(i, type, flags);
 | 
				
			||||||
| 
						 | 
					@ -898,20 +905,6 @@ void __init create_kmalloc_caches(slab_flags_t flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Kmalloc array is now usable */
 | 
						/* Kmalloc array is now usable */
 | 
				
			||||||
	slab_state = UP;
 | 
						slab_state = UP;
 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef CONFIG_ZONE_DMA
 | 
					 | 
				
			||||||
	for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
 | 
					 | 
				
			||||||
		struct kmem_cache *s = kmalloc_caches[KMALLOC_NORMAL][i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (s) {
 | 
					 | 
				
			||||||
			kmalloc_caches[KMALLOC_DMA][i] = create_kmalloc_cache(
 | 
					 | 
				
			||||||
				kmalloc_info[i].name[KMALLOC_DMA],
 | 
					 | 
				
			||||||
				kmalloc_info[i].size,
 | 
					 | 
				
			||||||
				SLAB_CACHE_DMA | flags, 0,
 | 
					 | 
				
			||||||
				kmalloc_info[i].size);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif /* !CONFIG_SLOB */
 | 
					#endif /* !CONFIG_SLOB */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										137
									
								
								mm/slub.c
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								mm/slub.c
									
									
									
									
									
								
							| 
						 | 
					@ -26,6 +26,7 @@
 | 
				
			||||||
#include <linux/cpuset.h>
 | 
					#include <linux/cpuset.h>
 | 
				
			||||||
#include <linux/mempolicy.h>
 | 
					#include <linux/mempolicy.h>
 | 
				
			||||||
#include <linux/ctype.h>
 | 
					#include <linux/ctype.h>
 | 
				
			||||||
 | 
					#include <linux/stackdepot.h>
 | 
				
			||||||
#include <linux/debugobjects.h>
 | 
					#include <linux/debugobjects.h>
 | 
				
			||||||
#include <linux/kallsyms.h>
 | 
					#include <linux/kallsyms.h>
 | 
				
			||||||
#include <linux/kfence.h>
 | 
					#include <linux/kfence.h>
 | 
				
			||||||
| 
						 | 
					@ -37,6 +38,7 @@
 | 
				
			||||||
#include <linux/memcontrol.h>
 | 
					#include <linux/memcontrol.h>
 | 
				
			||||||
#include <linux/random.h>
 | 
					#include <linux/random.h>
 | 
				
			||||||
#include <kunit/test.h>
 | 
					#include <kunit/test.h>
 | 
				
			||||||
 | 
					#include <linux/sort.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/debugfs.h>
 | 
					#include <linux/debugfs.h>
 | 
				
			||||||
#include <trace/events/kmem.h>
 | 
					#include <trace/events/kmem.h>
 | 
				
			||||||
| 
						 | 
					@ -264,8 +266,8 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s)
 | 
				
			||||||
#define TRACK_ADDRS_COUNT 16
 | 
					#define TRACK_ADDRS_COUNT 16
 | 
				
			||||||
struct track {
 | 
					struct track {
 | 
				
			||||||
	unsigned long addr;	/* Called from address */
 | 
						unsigned long addr;	/* Called from address */
 | 
				
			||||||
#ifdef CONFIG_STACKTRACE
 | 
					#ifdef CONFIG_STACKDEPOT
 | 
				
			||||||
	unsigned long addrs[TRACK_ADDRS_COUNT];	/* Called from address */
 | 
						depot_stack_handle_t handle;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	int cpu;		/* Was running on cpu */
 | 
						int cpu;		/* Was running on cpu */
 | 
				
			||||||
	int pid;		/* Pid context */
 | 
						int pid;		/* Pid context */
 | 
				
			||||||
| 
						 | 
					@ -724,57 +726,51 @@ static struct track *get_track(struct kmem_cache *s, void *object,
 | 
				
			||||||
	return kasan_reset_tag(p + alloc);
 | 
						return kasan_reset_tag(p + alloc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void set_track(struct kmem_cache *s, void *object,
 | 
					static void noinline set_track(struct kmem_cache *s, void *object,
 | 
				
			||||||
			enum track_item alloc, unsigned long addr)
 | 
								enum track_item alloc, unsigned long addr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct track *p = get_track(s, object, alloc);
 | 
						struct track *p = get_track(s, object, alloc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (addr) {
 | 
					#ifdef CONFIG_STACKDEPOT
 | 
				
			||||||
#ifdef CONFIG_STACKTRACE
 | 
						unsigned long entries[TRACK_ADDRS_COUNT];
 | 
				
			||||||
		unsigned int nr_entries;
 | 
						unsigned int nr_entries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		metadata_access_enable();
 | 
						nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 3);
 | 
				
			||||||
		nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
 | 
						p->handle = stack_depot_save(entries, nr_entries, GFP_NOWAIT);
 | 
				
			||||||
					      TRACK_ADDRS_COUNT, 3);
 | 
					 | 
				
			||||||
		metadata_access_disable();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (nr_entries < TRACK_ADDRS_COUNT)
 | 
					 | 
				
			||||||
			p->addrs[nr_entries] = 0;
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
		p->addr = addr;
 | 
					
 | 
				
			||||||
		p->cpu = smp_processor_id();
 | 
						p->addr = addr;
 | 
				
			||||||
		p->pid = current->pid;
 | 
						p->cpu = smp_processor_id();
 | 
				
			||||||
		p->when = jiffies;
 | 
						p->pid = current->pid;
 | 
				
			||||||
	} else {
 | 
						p->when = jiffies;
 | 
				
			||||||
		memset(p, 0, sizeof(struct track));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void init_tracking(struct kmem_cache *s, void *object)
 | 
					static void init_tracking(struct kmem_cache *s, void *object)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct track *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(s->flags & SLAB_STORE_USER))
 | 
						if (!(s->flags & SLAB_STORE_USER))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set_track(s, object, TRACK_FREE, 0UL);
 | 
						p = get_track(s, object, TRACK_ALLOC);
 | 
				
			||||||
	set_track(s, object, TRACK_ALLOC, 0UL);
 | 
						memset(p, 0, 2*sizeof(struct track));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void print_track(const char *s, struct track *t, unsigned long pr_time)
 | 
					static void print_track(const char *s, struct track *t, unsigned long pr_time)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						depot_stack_handle_t handle __maybe_unused;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!t->addr)
 | 
						if (!t->addr)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr_err("%s in %pS age=%lu cpu=%u pid=%d\n",
 | 
						pr_err("%s in %pS age=%lu cpu=%u pid=%d\n",
 | 
				
			||||||
	       s, (void *)t->addr, pr_time - t->when, t->cpu, t->pid);
 | 
						       s, (void *)t->addr, pr_time - t->when, t->cpu, t->pid);
 | 
				
			||||||
#ifdef CONFIG_STACKTRACE
 | 
					#ifdef CONFIG_STACKDEPOT
 | 
				
			||||||
	{
 | 
						handle = READ_ONCE(t->handle);
 | 
				
			||||||
		int i;
 | 
						if (handle)
 | 
				
			||||||
		for (i = 0; i < TRACK_ADDRS_COUNT; i++)
 | 
							stack_depot_print(handle);
 | 
				
			||||||
			if (t->addrs[i])
 | 
						else
 | 
				
			||||||
				pr_err("\t%pS\n", (void *)t->addrs[i]);
 | 
							pr_err("object allocation/free stack trace missing\n");
 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1532,6 +1528,8 @@ static int __init setup_slub_debug(char *str)
 | 
				
			||||||
			global_slub_debug_changed = true;
 | 
								global_slub_debug_changed = true;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			slab_list_specified = true;
 | 
								slab_list_specified = true;
 | 
				
			||||||
 | 
								if (flags & SLAB_STORE_USER)
 | 
				
			||||||
 | 
									stack_depot_want_early_init();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1549,6 +1547,8 @@ static int __init setup_slub_debug(char *str)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	slub_debug = global_flags;
 | 
						slub_debug = global_flags;
 | 
				
			||||||
 | 
						if (slub_debug & SLAB_STORE_USER)
 | 
				
			||||||
 | 
							stack_depot_want_early_init();
 | 
				
			||||||
	if (slub_debug != 0 || slub_debug_string)
 | 
						if (slub_debug != 0 || slub_debug_string)
 | 
				
			||||||
		static_branch_enable(&slub_debug_enabled);
 | 
							static_branch_enable(&slub_debug_enabled);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
| 
						 | 
					@ -4162,8 +4162,6 @@ static int calculate_sizes(struct kmem_cache *s)
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	s->oo = oo_make(order, size);
 | 
						s->oo = oo_make(order, size);
 | 
				
			||||||
	s->min = oo_make(get_order(size), size);
 | 
						s->min = oo_make(get_order(size), size);
 | 
				
			||||||
	if (oo_objects(s->oo) > oo_objects(s->max))
 | 
					 | 
				
			||||||
		s->max = s->oo;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return !!oo_objects(s->oo);
 | 
						return !!oo_objects(s->oo);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4341,18 +4339,26 @@ void kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
 | 
				
			||||||
	objp = fixup_red_left(s, objp);
 | 
						objp = fixup_red_left(s, objp);
 | 
				
			||||||
	trackp = get_track(s, objp, TRACK_ALLOC);
 | 
						trackp = get_track(s, objp, TRACK_ALLOC);
 | 
				
			||||||
	kpp->kp_ret = (void *)trackp->addr;
 | 
						kpp->kp_ret = (void *)trackp->addr;
 | 
				
			||||||
#ifdef CONFIG_STACKTRACE
 | 
					#ifdef CONFIG_STACKDEPOT
 | 
				
			||||||
	for (i = 0; i < KS_ADDRS_COUNT && i < TRACK_ADDRS_COUNT; i++) {
 | 
						{
 | 
				
			||||||
		kpp->kp_stack[i] = (void *)trackp->addrs[i];
 | 
							depot_stack_handle_t handle;
 | 
				
			||||||
		if (!kpp->kp_stack[i])
 | 
							unsigned long *entries;
 | 
				
			||||||
			break;
 | 
							unsigned int nr_entries;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trackp = get_track(s, objp, TRACK_FREE);
 | 
							handle = READ_ONCE(trackp->handle);
 | 
				
			||||||
	for (i = 0; i < KS_ADDRS_COUNT && i < TRACK_ADDRS_COUNT; i++) {
 | 
							if (handle) {
 | 
				
			||||||
		kpp->kp_free_stack[i] = (void *)trackp->addrs[i];
 | 
								nr_entries = stack_depot_fetch(handle, &entries);
 | 
				
			||||||
		if (!kpp->kp_free_stack[i])
 | 
								for (i = 0; i < KS_ADDRS_COUNT && i < nr_entries; i++)
 | 
				
			||||||
			break;
 | 
									kpp->kp_stack[i] = (void *)entries[i];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							trackp = get_track(s, objp, TRACK_FREE);
 | 
				
			||||||
 | 
							handle = READ_ONCE(trackp->handle);
 | 
				
			||||||
 | 
							if (handle) {
 | 
				
			||||||
 | 
								nr_entries = stack_depot_fetch(handle, &entries);
 | 
				
			||||||
 | 
								for (i = 0; i < KS_ADDRS_COUNT && i < nr_entries; i++)
 | 
				
			||||||
 | 
									kpp->kp_free_stack[i] = (void *)entries[i];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -5054,6 +5060,7 @@ EXPORT_SYMBOL(validate_slab_cache);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct location {
 | 
					struct location {
 | 
				
			||||||
 | 
						depot_stack_handle_t handle;
 | 
				
			||||||
	unsigned long count;
 | 
						unsigned long count;
 | 
				
			||||||
	unsigned long addr;
 | 
						unsigned long addr;
 | 
				
			||||||
	long long sum_time;
 | 
						long long sum_time;
 | 
				
			||||||
| 
						 | 
					@ -5106,9 +5113,13 @@ static int add_location(struct loc_track *t, struct kmem_cache *s,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	long start, end, pos;
 | 
						long start, end, pos;
 | 
				
			||||||
	struct location *l;
 | 
						struct location *l;
 | 
				
			||||||
	unsigned long caddr;
 | 
						unsigned long caddr, chandle;
 | 
				
			||||||
	unsigned long age = jiffies - track->when;
 | 
						unsigned long age = jiffies - track->when;
 | 
				
			||||||
 | 
						depot_stack_handle_t handle = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_STACKDEPOT
 | 
				
			||||||
 | 
						handle = READ_ONCE(track->handle);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	start = -1;
 | 
						start = -1;
 | 
				
			||||||
	end = t->count;
 | 
						end = t->count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5123,7 +5134,8 @@ static int add_location(struct loc_track *t, struct kmem_cache *s,
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		caddr = t->loc[pos].addr;
 | 
							caddr = t->loc[pos].addr;
 | 
				
			||||||
		if (track->addr == caddr) {
 | 
							chandle = t->loc[pos].handle;
 | 
				
			||||||
 | 
							if ((track->addr == caddr) && (handle == chandle)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			l = &t->loc[pos];
 | 
								l = &t->loc[pos];
 | 
				
			||||||
			l->count++;
 | 
								l->count++;
 | 
				
			||||||
| 
						 | 
					@ -5148,6 +5160,8 @@ static int add_location(struct loc_track *t, struct kmem_cache *s,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (track->addr < caddr)
 | 
							if (track->addr < caddr)
 | 
				
			||||||
			end = pos;
 | 
								end = pos;
 | 
				
			||||||
 | 
							else if (track->addr == caddr && handle < chandle)
 | 
				
			||||||
 | 
								end = pos;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			start = pos;
 | 
								start = pos;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -5170,6 +5184,7 @@ static int add_location(struct loc_track *t, struct kmem_cache *s,
 | 
				
			||||||
	l->max_time = age;
 | 
						l->max_time = age;
 | 
				
			||||||
	l->min_pid = track->pid;
 | 
						l->min_pid = track->pid;
 | 
				
			||||||
	l->max_pid = track->pid;
 | 
						l->max_pid = track->pid;
 | 
				
			||||||
 | 
						l->handle = handle;
 | 
				
			||||||
	cpumask_clear(to_cpumask(l->cpus));
 | 
						cpumask_clear(to_cpumask(l->cpus));
 | 
				
			||||||
	cpumask_set_cpu(track->cpu, to_cpumask(l->cpus));
 | 
						cpumask_set_cpu(track->cpu, to_cpumask(l->cpus));
 | 
				
			||||||
	nodes_clear(l->nodes);
 | 
						nodes_clear(l->nodes);
 | 
				
			||||||
| 
						 | 
					@ -6079,6 +6094,21 @@ static int slab_debugfs_show(struct seq_file *seq, void *v)
 | 
				
			||||||
			seq_printf(seq, " nodes=%*pbl",
 | 
								seq_printf(seq, " nodes=%*pbl",
 | 
				
			||||||
				 nodemask_pr_args(&l->nodes));
 | 
									 nodemask_pr_args(&l->nodes));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_STACKDEPOT
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								depot_stack_handle_t handle;
 | 
				
			||||||
 | 
								unsigned long *entries;
 | 
				
			||||||
 | 
								unsigned int nr_entries, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								handle = READ_ONCE(l->handle);
 | 
				
			||||||
 | 
								if (handle) {
 | 
				
			||||||
 | 
									nr_entries = stack_depot_fetch(handle, &entries);
 | 
				
			||||||
 | 
									seq_puts(seq, "\n");
 | 
				
			||||||
 | 
									for (j = 0; j < nr_entries; j++)
 | 
				
			||||||
 | 
										seq_printf(seq, "        %pS\n", (void *)entries[j]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		seq_puts(seq, "\n");
 | 
							seq_puts(seq, "\n");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6103,6 +6133,17 @@ static void *slab_debugfs_next(struct seq_file *seq, void *v, loff_t *ppos)
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int cmp_loc_by_count(const void *a, const void *b, const void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct location *loc1 = (struct location *)a;
 | 
				
			||||||
 | 
						struct location *loc2 = (struct location *)b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (loc1->count > loc2->count)
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void *slab_debugfs_start(struct seq_file *seq, loff_t *ppos)
 | 
					static void *slab_debugfs_start(struct seq_file *seq, loff_t *ppos)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct loc_track *t = seq->private;
 | 
						struct loc_track *t = seq->private;
 | 
				
			||||||
| 
						 | 
					@ -6164,6 +6205,10 @@ static int slab_debug_trace_open(struct inode *inode, struct file *filep)
 | 
				
			||||||
		spin_unlock_irqrestore(&n->list_lock, flags);
 | 
							spin_unlock_irqrestore(&n->list_lock, flags);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Sort locations by count */
 | 
				
			||||||
 | 
						sort_r(t->loc, t->count, sizeof(struct location),
 | 
				
			||||||
 | 
							cmp_loc_by_count, NULL, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bitmap_free(obj_map);
 | 
						bitmap_free(obj_map);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue