This patch provides interface to check all the stack enteries saved in stackdepot so far as well as memory consumed by stackdepot. 1) Take current depot_index and offset to calculate end address for one iteration of (/sys/kernel/debug/depot_stack/depot_entries). 2) Fill end marker in every slab to point its end, and then use it while traversing all the slabs of stackdepot. "debugfs code inspired from page_onwer's way of printing BT" checked on ARM and x86_64. $cat /sys/kernel/debug/depot_stack/depot_size Memory consumed by Stackdepot:208 KB $ cat /sys/kernel/debug/depot_stack/depot_entries stack count 1 backtrace init_page_owner+0x1e/0x210 start_kernel+0x310/0x3cd secondary_startup_64+0xa5/0xb0 0xffffffffffffffff Signed-off-by: Vaneet Narang <v.narang@xxxxxxxxxxx> Signed-off-by: Maninder Singh <maninder1.s@xxxxxxxxxxx> --- include/linux/stackdepot.h | 13 +++ include/linux/stacktrace.h | 6 ++ lib/stackdepot.c | 183 ++++++++++++++++++++++++++++++++++++++++++++ mm/page_owner.c | 6 -- 4 files changed, 202 insertions(+), 6 deletions(-) diff --git a/include/linux/stackdepot.h b/include/linux/stackdepot.h index 7978b3e..dd95b11 100644 --- a/include/linux/stackdepot.h +++ b/include/linux/stackdepot.h @@ -23,6 +23,19 @@ typedef u32 depot_stack_handle_t; +/* + * structure to store markers which + * will be used while printing entries + * stored in stackdepot. + */ +struct depot_stack_data { + int print_offset; + int print_counter; + int print_index; + unsigned long end_marker; + void *end_address; +}; + struct stack_trace; depot_stack_handle_t depot_save_stack(struct stack_trace *trace, gfp_t flags); diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h index ba29a06..1cfd27d 100644 --- a/include/linux/stacktrace.h +++ b/include/linux/stacktrace.h @@ -4,6 +4,12 @@ #include <linux/types.h> +/* + * TODO: teach PAGE_OWNER_STACK_DEPTH (__dump_page_owner and save_stack) + * to use off stack temporal storage + */ +#define PAGE_OWNER_STACK_DEPTH (16) + struct task_struct; struct pt_regs; diff --git a/lib/stackdepot.c b/lib/stackdepot.c index f87d138..3067fcb 100644 --- a/lib/stackdepot.c +++ b/lib/stackdepot.c @@ -39,6 +39,8 @@ #include <linux/stackdepot.h> #include <linux/string.h> #include <linux/types.h> +#include <linux/debugfs.h> +#include <linux/uaccess.h> #define DEPOT_STACK_BITS (sizeof(depot_stack_handle_t) * 8) @@ -111,6 +113,7 @@ static bool init_stack_slab(void **prealloc) int required_size = offsetof(struct stack_record, entries) + sizeof(unsigned long) * size; struct stack_record *stack; + void *address; required_size = ALIGN(required_size, 1 << STACK_ALLOC_ALIGN); @@ -119,6 +122,17 @@ static bool init_stack_slab(void **prealloc) WARN_ONCE(1, "Stack depot reached limit capacity"); return NULL; } + + /* + * write POSION_END if any space left in + * current slab to represent its end. + * later used while printing all the stacks. + */ + if (depot_offset < STACK_ALLOC_SIZE) { + address = stack_slabs[depot_index] + depot_offset; + memset(address, POISON_END, sizeof(unsigned long)); + } + depot_index++; depot_offset = 0; /* @@ -285,3 +299,172 @@ depot_stack_handle_t depot_save_stack(struct stack_trace *trace, return retval; } EXPORT_SYMBOL_GPL(depot_save_stack); + +#define DEPOT_SIZE 64 + +static ssize_t read_depot_stack_size(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + char kbuf[DEPOT_SIZE]; + ssize_t ret = 0; + unsigned long size = depot_index * (1 << STACK_ALLOC_ORDER) * PAGE_SIZE; + + ret = snprintf(kbuf, count, "Memory consumed by Stackdepot:%lu KB\n", size >> 10); + if (ret >= count) + return -ENOMEM; + + return simple_read_from_buffer(buf, count, ppos, kbuf, ret); +} + +static ssize_t print_depot_stack(char __user *buf, size_t count, struct stack_trace *trace, loff_t *ppos) +{ + char *kbuf; + int ret = 0; + + kbuf = kvmalloc(count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + ret = snprintf(kbuf, count, "stack count %d backtrace\n", (int)*ppos); + ret += snprint_stack_trace(kbuf + ret, count - ret, trace, 0); + ret += snprintf(kbuf + ret, count - ret, "\n"); + + if (ret >= count) { + ret = -ENOMEM; + goto err; + } + + if (copy_to_user(buf, kbuf, ret)) + ret = -EFAULT; + +err: + kvfree(kbuf); + return ret; +} + +/* + * read_depot_stack() + * + * function to print all the entries present + * in depot_stack database currently in system. + */ +static ssize_t read_depot_stack(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct stack_record *stack; + void *address; + struct depot_stack_data *debugfs_data; + + debugfs_data = (struct depot_stack_data *)file->private_data; + + if (!debugfs_data) + return -EINVAL; + + while (debugfs_data->print_counter <= debugfs_data->print_index) { + unsigned long entries[PAGE_OWNER_STACK_DEPTH]; + struct stack_trace trace = { + .nr_entries = 0, + .entries = entries, + .max_entries = PAGE_OWNER_STACK_DEPTH, + .skip = 0 + }; + + address = stack_slabs[debugfs_data->print_counter] + debugfs_data->print_offset; + if (address == debugfs_data->end_address) + break; + + if (*((unsigned long *)address) == debugfs_data->end_marker) { + debugfs_data->print_counter++; + debugfs_data->print_offset = 0; + continue; + } + + stack = address; + trace.nr_entries = trace.max_entries = stack->size; + trace.entries = stack->entries; + + debugfs_data->print_offset += offsetof(struct stack_record, entries) + + (stack->size * sizeof(unsigned long)); + debugfs_data->print_offset = ALIGN(debugfs_data->print_offset, 1 << STACK_ALLOC_ALIGN); + if (debugfs_data->print_offset >= STACK_ALLOC_SIZE) { + debugfs_data->print_counter++; + debugfs_data->print_offset = 0; + } + + *ppos = *ppos + 1; /* one stack found, print it */ + return print_depot_stack(buf, count, &trace, ppos); + } + + return 0; +} + +int read_depot_open(struct inode *inode, struct file *file) +{ + struct depot_stack_data *debugfs_data; + unsigned long flags; + + debugfs_data = kzalloc(sizeof(struct depot_stack_data), GFP_KERNEL); + if (!debugfs_data) + return -ENOMEM; + /* + * First time depot_stack/depot_entries is called. + * (/sys/kernel/debug/depot_stack/depot_entries) + * initialise print depot_index and stopping address. + */ + memset(&(debugfs_data->end_marker), POISON_END, sizeof(unsigned long)); + + spin_lock_irqsave(&depot_lock, flags); + debugfs_data->print_index = depot_index; + debugfs_data->end_address = stack_slabs[depot_index] + depot_offset; + spin_unlock_irqrestore(&depot_lock, flags); + + file->private_data = debugfs_data; + return 0; +} + +int read_depot_release(struct inode *inode, struct file *file) +{ + void *debugfs_data = file->private_data; + + kfree(debugfs_data); + return 0; +} + +static const struct file_operations proc_depot_stack_operations = { + .open = read_depot_open, + .read = read_depot_stack, + .release = read_depot_release, +}; + +static const struct file_operations proc_depot_stack_size_operations = { + .read = read_depot_stack_size, +}; + +static int __init depot_stack_init(void) +{ + struct dentry *dentry, *dentry_root; + + dentry_root = debugfs_create_dir("depot_stack", NULL); + + if (!dentry_root) { + pr_warn("debugfs 'depot_stack' dir creation failed\n"); + return -ENOMEM; + } + + dentry = debugfs_create_file("depot_entries", 0400, dentry_root, + NULL, &proc_depot_stack_operations); + + if (IS_ERR(dentry)) + goto err; + + dentry = debugfs_create_file("depot_size", 0400, dentry_root, + NULL, &proc_depot_stack_size_operations); + + if (IS_ERR(dentry)) + goto err; + + return 0; + +err: + debugfs_remove_recursive(dentry_root); + return PTR_ERR(dentry); +} +late_initcall(depot_stack_init) diff --git a/mm/page_owner.c b/mm/page_owner.c index 4f44b95..341b326 100644 --- a/mm/page_owner.c +++ b/mm/page_owner.c @@ -13,12 +13,6 @@ #include "internal.h" -/* - * TODO: teach PAGE_OWNER_STACK_DEPTH (__dump_page_owner and save_stack) - * to use off stack temporal storage - */ -#define PAGE_OWNER_STACK_DEPTH (16) - struct page_owner { unsigned int order; gfp_t gfp_mask; -- 1.7.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxx. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>