linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: Oscar Salvador <osalvador@suse.de>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-kernel@vger.kernel.org, linux-mm@kvack.org,
	Michal Hocko <mhocko@suse.com>, Vlastimil Babka <vbabka@suse.cz>,
	Marco Elver <elver@google.com>,
	Andrey Konovalov <andreyknvl@gmail.com>,
	Alexander Potapenko <glider@google.com>,
	Oscar Salvador <osalvador@suse.de>
Subject: [PATCH v7 3/4] mm,page_owner: Display all stacks and their count
Date: Fri,  9 Feb 2024 00:45:38 +0100	[thread overview]
Message-ID: <20240208234539.19113-4-osalvador@suse.de> (raw)
In-Reply-To: <20240208234539.19113-1-osalvador@suse.de>

This patch adds a new file called 'page_owner_stacks', which
will show all stacks that were added by page_owner followed by
their counting, giving us a clear overview of stack <-> count
relationship.

E.g:

  prep_new_page+0xa9/0x120
  get_page_from_freelist+0x801/0x2210
  __alloc_pages+0x18b/0x350
  alloc_pages_mpol+0x91/0x1f0
  folio_alloc+0x14/0x50
  filemap_alloc_folio+0xb2/0x100
  __filemap_get_folio+0x14a/0x490
  ext4_write_begin+0xbd/0x4b0 [ext4]
  generic_perform_write+0xc1/0x1e0
  ext4_buffered_write_iter+0x68/0xe0 [ext4]
  ext4_file_write_iter+0x70/0x740 [ext4]
  vfs_write+0x33d/0x420
  ksys_write+0xa5/0xe0
  do_syscall_64+0x80/0x160
  entry_SYSCALL_64_after_hwframe+0x6e/0x76
 stack_count: 4578

In order to show all the stacks, we implement stack_depot_get_next_stack(),
which walks all buckets while retrieving the stacks stored in them.
stack_depot_get_next_stack() will return all stacks, one at a time,
by first finding a non-empty bucket, and then retrieving all the stacks
stored in that bucket.
Once we have completely gone through it, we get the next non-empty bucket
and repeat the same steps, and so on until we have completely checked all
buckets.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 include/linux/stackdepot.h | 20 +++++++++
 lib/stackdepot.c           | 46 +++++++++++++++++++++
 mm/page_owner.c            | 85 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+)

diff --git a/include/linux/stackdepot.h b/include/linux/stackdepot.h
index ac62de4d4999..d851ec821e6f 100644
--- a/include/linux/stackdepot.h
+++ b/include/linux/stackdepot.h
@@ -183,6 +183,26 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,
  */
 struct stack_record *stack_depot_get_stack(depot_stack_handle_t handle);
 
+/**
+ * stack_depot_get_next_stack - Returns all stacks, one at a time
+ *
+ * @table:	Current table we are checking
+ * @bucket:	Current bucket we are checking
+ * @last_found:	Last stack that was found
+ *
+ * This function finds first a non-empty bucket and returns the first stack
+ * stored in it. On consequent calls, it walks the bucket to see whether
+ * it contains more stacks.
+ * Once we have walked all the stacks in a bucket, we check
+ * the next one, and we repeat the same steps until we have checked all of them
+ *
+ * Return: A pointer a to stack_record struct, or NULL when we have walked all
+ * buckets.
+ */
+struct stack_record *stack_depot_get_next_stack(unsigned long *table,
+						struct list_head **bucket,
+						struct stack_record **last_found);
+
 /**
  * stack_depot_fetch - Fetch a stack trace from stack depot
  *
diff --git a/lib/stackdepot.c b/lib/stackdepot.c
index 197c355601f9..107bd0174cd6 100644
--- a/lib/stackdepot.c
+++ b/lib/stackdepot.c
@@ -782,6 +782,52 @@ unsigned int stack_depot_get_extra_bits(depot_stack_handle_t handle)
 }
 EXPORT_SYMBOL(stack_depot_get_extra_bits);
 
+struct stack_record *stack_depot_get_next_stack(unsigned long *table,
+						struct list_head **curr_bucket,
+						struct stack_record **last_found)
+{
+	struct list_head *bucket = *curr_bucket;
+	unsigned long nr_table = *table;
+	struct stack_record *found = NULL;
+	unsigned long stack_table_entries = stack_hash_mask + 1;
+
+	rcu_read_lock_sched_notrace();
+	if (!bucket) {
+		/*
+		 * Find a non-empty bucket. Once we have found it,
+		 * we will use list_for_each_entry_continue_rcu() on the next
+		 * call to keep walking the bucket.
+		 */
+new_table:
+		bucket = &stack_table[nr_table];
+		list_for_each_entry_rcu(found, bucket, hash_list) {
+			goto out;
+		}
+	} else {
+		 /* Check whether we have more stacks in this bucket */
+		found = *last_found;
+		list_for_each_entry_continue_rcu(found, bucket, hash_list) {
+			goto out;
+		}
+	}
+
+	/* No more stacks in this bucket, check the next one */
+	nr_table++;
+	if (nr_table < stack_table_entries)
+		goto new_table;
+
+	/* We are done walking all buckets */
+	found = NULL;
+
+out:
+	*table = nr_table;
+	*curr_bucket = bucket;
+	*last_found = found;
+	rcu_read_unlock_sched_notrace();
+
+	return found;
+}
+
 static int stats_show(struct seq_file *seq, void *v)
 {
 	/*
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 0adf41702b9d..aea212734557 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -749,6 +749,89 @@ static const struct file_operations proc_page_owner_operations = {
 	.llseek		= lseek_page_owner,
 };
 
+struct stack_iterator {
+	unsigned long nr_table;
+	struct list_head *bucket;
+	struct stack_record *last_stack;
+};
+
+static void *stack_start(struct seq_file *m, loff_t *ppos)
+{
+	struct stack_iterator *iter = m->private;
+
+	if (*ppos == -1UL)
+		return NULL;
+
+	return stack_depot_get_next_stack(&iter->nr_table,
+					  &iter->bucket,
+					  &iter->last_stack);
+}
+
+static void *stack_next(struct seq_file *m, void *v, loff_t *ppos)
+{
+	struct stack_iterator *iter = m->private;
+	struct stack_record *stack;
+
+	stack = stack_depot_get_next_stack(&iter->nr_table,
+					   &iter->bucket,
+					   &iter->last_stack);
+	*ppos = stack ? *ppos + 1 : -1UL;
+
+	return stack;
+}
+
+static int stack_print(struct seq_file *m, void *v)
+{
+	char *buf;
+	int ret = 0;
+	struct stack_iterator *iter = m->private;
+	struct stack_record *stack = iter->last_stack;
+
+	if (!stack->size || stack->size < 0 || refcount_read(&stack->count) < 2)
+		return 0;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+
+	ret += stack_trace_snprint(buf, PAGE_SIZE, stack->entries, stack->size,
+				   0);
+	if (!ret)
+		goto out;
+
+	scnprintf(buf + ret, PAGE_SIZE - ret, "stack_count: %d\n\n",
+		  refcount_read(&stack->count));
+
+	seq_printf(m, buf);
+	seq_puts(m, "\n\n");
+out:
+	kfree(buf);
+
+	return 0;
+}
+
+static void stack_stop(struct seq_file *m, void *v)
+{
+}
+
+static const struct seq_operations page_owner_stack_op = {
+	.start	= stack_start,
+	.next	= stack_next,
+	.stop	= stack_stop,
+	.show	= stack_print
+};
+
+static int page_owner_stack_open(struct inode *inode, struct file *file)
+{
+	return seq_open_private(file, &page_owner_stack_op,
+				sizeof(struct stack_iterator));
+}
+
+const struct file_operations page_owner_stack_operations = {
+	.open		= page_owner_stack_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
 static int __init pageowner_init(void)
 {
 	if (!static_branch_unlikely(&page_owner_inited)) {
@@ -758,6 +841,8 @@ static int __init pageowner_init(void)
 
 	debugfs_create_file("page_owner", 0400, NULL, NULL,
 			    &proc_page_owner_operations);
+	debugfs_create_file("page_owner_stacks", 0400, NULL, NULL,
+			    &page_owner_stack_operations);
 
 	return 0;
 }
-- 
2.43.0



  parent reply	other threads:[~2024-02-08 23:45 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-08 23:45 [PATCH v7 0/4] page_owner: print stacks and their outstanding allocations Oscar Salvador
2024-02-08 23:45 ` [PATCH v7 1/4] lib/stackdepot: Move stack_record struct definition into the header Oscar Salvador
2024-02-09  7:45   ` Marco Elver
2024-02-09 21:33     ` Oscar Salvador
2024-02-09 17:39   ` kernel test robot
2024-02-10  9:59   ` kernel test robot
2024-02-08 23:45 ` [PATCH v7 2/4] mm,page_owner: Implement the tracking of the stacks count Oscar Salvador
2024-02-09  7:37   ` Marco Elver
2024-02-09  7:45   ` Marco Elver
2024-02-09 21:39     ` Oscar Salvador
2024-02-09 21:42       ` Marco Elver
2024-02-09 21:44         ` Marco Elver
2024-02-11 20:42           ` Oscar Salvador
2024-02-08 23:45 ` Oscar Salvador [this message]
2024-02-09  8:00   ` [PATCH v7 3/4] mm,page_owner: Display all stacks and their count Marco Elver
2024-02-09 21:52     ` Oscar Salvador
2024-02-09 23:14       ` Oscar Salvador
2024-02-10  7:52         ` Marco Elver
2024-02-11 20:39           ` Oscar Salvador
2024-02-12 10:47             ` Vlastimil Babka
2024-02-09 23:14   ` kernel test robot
2024-02-08 23:45 ` [PATCH v7 4/4] mm,page_owner: Filter out stacks by a threshold Oscar Salvador
2024-02-09  0:28 ` [PATCH v7 0/4] page_owner: print stacks and their outstanding allocations Andrew Morton
2024-02-09 21:31   ` Oscar Salvador
2024-02-09  8:03 ` Marco Elver
2024-02-09 21:32   ` Oscar Salvador

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240208234539.19113-4-osalvador@suse.de \
    --to=osalvador@suse.de \
    --cc=akpm@linux-foundation.org \
    --cc=andreyknvl@gmail.com \
    --cc=elver@google.com \
    --cc=glider@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=mhocko@suse.com \
    --cc=vbabka@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox