Re: [PATCH 1/1] stackdepot: interface to check entries and size of stackdepot.
From: Michal Hocko
Date: Thu Nov 23 2017 - 11:28:44 EST
On Wed 22-11-17 16:17:41, Maninder Singh wrote:
> 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
Why do we need this? Who is goging to use this information and what for?
I haven't looked at the code but just the diffstat looks like this
should better have a _very_ good justification to be considered for
merging. To be honest with you I have hard time imagine how this can be
useful other than debugging stack depot...
> 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
>
--
Michal Hocko
SUSE Labs