From: Luis Chamberlain <mcgrof@kernel.org>
To: brauner@kernel.org, jack@suse.cz, tytso@mit.edu,
adilger.kernel@dilger.ca, linux-ext4@vger.kernel.org,
riel@surriel.com
Cc: dave@stgolabs.net, willy@infradead.org, hannes@cmpxchg.org,
oliver.sang@intel.com, david@redhat.com, axboe@kernel.dk,
hare@suse.de, david@fromorbit.com, djwong@kernel.org,
ritesh.list@gmail.com, linux-fsdevel@vger.kernel.org,
linux-block@vger.kernel.org, linux-mm@kvack.org,
gost.dev@samsung.com, p.raghav@samsung.com, da.gomez@samsung.com,
mcgrof@kernel.org
Subject: [PATCH v2 8/8] mm: add migration buffer-head debugfs interface
Date: Wed, 9 Apr 2025 18:49:45 -0700 [thread overview]
Message-ID: <20250410014945.2140781-9-mcgrof@kernel.org> (raw)
In-Reply-To: <20250410014945.2140781-1-mcgrof@kernel.org>
If you are working on enhancing folio migration it is easy to not
be certain on improvements. This debugfs interface enables you to
evaluate gains on improvements on buffer-head folio migration.
This can easily tell you *why* folio migration might fail, for example,
here is the output of a generic/750 run for 18 hours:
root@e3-ext4-2k ~ # cat /sys/kernel/debug/mm/migrate/bh/stats
[buffer_migrate_folio]
calls 50160811
success 50047572
fails 113239
[buffer_migrate_folio_norefs]
calls 23577082468
success 2939858
fails 23574142610
jbd-meta 23425956714
no-head-success 102
no-head-fails 0
invalid 147919982
valid 2939881
valid-success 2939756
valid-fails 125
Success ratios:
buffer_migrate_folio: 99% success (50047572/50160811)
buffer_migrate_folio_norefs: 0% success (2939858/23577082468)
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
mm/migrate.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 178 insertions(+), 6 deletions(-)
diff --git a/mm/migrate.c b/mm/migrate.c
index 8fed2655f2e8..c478e8218cb0 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -44,6 +44,7 @@
#include <linux/sched/sysctl.h>
#include <linux/memory-tiers.h>
#include <linux/pagewalk.h>
+#include <linux/debugfs.h>
#include <asm/tlbflush.h>
@@ -791,6 +792,126 @@ int migrate_folio(struct address_space *mapping, struct folio *dst,
EXPORT_SYMBOL(migrate_folio);
#ifdef CONFIG_BUFFER_HEAD
+
+static const char * const bh_routine_names[] = {
+ "buffer_migrate_folio",
+ "buffer_migrate_folio_norefs",
+};
+
+#define BH_STATS(X) \
+ X(bh_migrate_folio, 0, "calls") \
+ X(bh_migrate_folio_success, 0, "success") \
+ X(bh_migrate_folio_fails, 0, "fails") \
+ X(bh_migrate_folio_norefs, 1, "calls") \
+ X(bh_migrate_folio_norefs_success, 1, "success") \
+ X(bh_migrate_folio_norefs_fails, 1, "fails") \
+ X(bh_migrate_folio_norefs_meta, 1, "jbd-meta") \
+ X(bh_migrate_folio_norefs_nohead_success, 1, "no-head-success") \
+ X(bh_migrate_folio_norefs_nohead_fails, 1, "no-head-fails") \
+ X(bh_migrate_folio_norefs_invalid, 1, "invalid") \
+ X(bh_migrate_folio_norefs_valid, 1, "valid") \
+ X(bh_migrate_folio_norefs_valid_success, 1, "valid-success") \
+ X(bh_migrate_folio_norefs_valid_fails, 1, "valid-fails")
+
+
+#define DECLARE_STAT(name, routine_idx, meaning) static atomic_long_t name;
+BH_STATS(DECLARE_STAT)
+
+#define BH_STAT_PTR(name, routine_idx, meaning) &name,
+static atomic_long_t * const bh_stat_array[] = {
+ BH_STATS(BH_STAT_PTR)
+};
+
+#define BH_STAT_ROUTINE_IDX(name, routine_idx, meaning) routine_idx,
+static const int bh_stat_routine_index[] = {
+ BH_STATS(BH_STAT_ROUTINE_IDX)
+};
+
+#define BH_STAT_MEANING(name, routine_idx, meaning) meaning,
+static const char * const bh_stat_meanings[] = {
+ BH_STATS(BH_STAT_MEANING)
+};
+
+#define NUM_BH_STATS ARRAY_SIZE(bh_stat_array)
+
+static ssize_t read_file_bh_migrate_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ unsigned int i, len = 0, size = NUM_BH_STATS * 128;
+ int ret, last_routine = -1;
+ unsigned long total, success, rate;
+
+ BUILD_BUG_ON(ARRAY_SIZE(bh_stat_array) != ARRAY_SIZE(bh_stat_meanings));
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < NUM_BH_STATS; i++) {
+ int routine_idx = bh_stat_routine_index[i];
+
+ if (routine_idx != last_routine) {
+ len += scnprintf(buf + len, size - len, "\n[%s]\n",
+ bh_routine_names[routine_idx]);
+ last_routine = routine_idx;
+ }
+
+ len += scnprintf(buf + len, size - len, "%25s\t%lu\n",
+ bh_stat_meanings[i],
+ atomic_long_read(bh_stat_array[i]));
+
+ }
+
+ len += scnprintf(buf + len, size - len, "\nSuccess ratios:\n");
+
+ total = atomic_long_read(&bh_migrate_folio);
+ success = atomic_long_read(&bh_migrate_folio_success);
+ rate = total ? (success * 100) / total : 0;
+ len += scnprintf(buf + len, size - len,
+ "%s: %lu%% success (%lu/%lu)\n",
+ "buffer_migrate_folio", rate, success, total);
+
+ total = atomic_long_read(&bh_migrate_folio_norefs);
+ success = atomic_long_read(&bh_migrate_folio_norefs_success);
+ rate = total ? (success * 100) / total : 0;
+ len += scnprintf(buf + len, size - len,
+ "%s: %lu%% success (%lu/%lu)\n",
+ "buffer_migrate_folio_norefs", rate, success, total);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations fops_bh_migrate_stats = {
+ .read = read_file_bh_migrate_stats,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static void mm_migrate_bh_init(struct dentry *migrate_debug_root)
+{
+ struct dentry *parent_dirs[ARRAY_SIZE(bh_routine_names)] = { NULL };
+ struct dentry *root = debugfs_create_dir("bh", migrate_debug_root);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bh_routine_names); i++)
+ parent_dirs[i] = debugfs_create_dir(bh_routine_names[i], root);
+
+ for (i = 0; i < NUM_BH_STATS; i++) {
+ int routine = bh_stat_routine_index[i];
+ debugfs_create_ulong(bh_stat_meanings[i], 0400,
+ parent_dirs[routine],
+ (unsigned long *)
+ &bh_stat_array[i]->counter);
+ }
+
+ debugfs_create_file("stats", 0400, root, root, &fops_bh_migrate_stats);
+}
+
/* Returns true if all buffers are successfully locked */
static bool buffer_migrate_lock_buffers(struct buffer_head *head,
enum migrate_mode mode)
@@ -833,16 +954,26 @@ static int __buffer_migrate_folio(struct address_space *mapping,
int expected_count;
head = folio_buffers(src);
- if (!head)
- return migrate_folio(mapping, dst, src, mode);
+ if (!head) {
+ rc = migrate_folio(mapping, dst, src, mode);
+ if (check_refs) {
+ if (rc == 0)
+ atomic_long_inc(&bh_migrate_folio_norefs_nohead_success);
+ else
+ atomic_long_inc(&bh_migrate_folio_norefs_nohead_fails);
+ }
+ return rc;
+ }
/* Check whether page does not have extra refs before we do more work */
expected_count = folio_expected_refs(mapping, src);
if (folio_ref_count(src) != expected_count)
return -EAGAIN;
- if (buffer_meta(head))
+ if (buffer_meta(head)) {
+ atomic_long_inc(&bh_migrate_folio_norefs_meta);
return -EAGAIN;
+ }
if (!buffer_migrate_lock_buffers(head, mode))
return -EAGAIN;
@@ -868,17 +999,23 @@ static int __buffer_migrate_folio(struct address_space *mapping,
if (busy) {
if (invalidated) {
rc = -EAGAIN;
+ atomic_long_inc(&bh_migrate_folio_norefs_invalid);
goto unlock_buffers;
}
invalidate_bh_lrus();
invalidated = true;
goto recheck_buffers;
}
+ atomic_long_inc(&bh_migrate_folio_norefs_valid);
}
rc = filemap_migrate_folio(mapping, dst, src, mode);
- if (rc != MIGRATEPAGE_SUCCESS)
+ if (rc != MIGRATEPAGE_SUCCESS) {
+ if (check_refs)
+ atomic_long_inc(&bh_migrate_folio_norefs_valid_fails);
goto unlock_buffers;
+ } else if (check_refs)
+ atomic_long_inc(&bh_migrate_folio_norefs_valid_success);
bh = head;
do {
@@ -915,7 +1052,16 @@ static int __buffer_migrate_folio(struct address_space *mapping,
int buffer_migrate_folio(struct address_space *mapping,
struct folio *dst, struct folio *src, enum migrate_mode mode)
{
- return __buffer_migrate_folio(mapping, dst, src, mode, false);
+ int ret;
+ atomic_long_inc(&bh_migrate_folio);
+
+ ret = __buffer_migrate_folio(mapping, dst, src, mode, false);
+ if (ret == 0)
+ atomic_long_inc(&bh_migrate_folio_success);
+ else
+ atomic_long_inc(&bh_migrate_folio_fails);
+
+ return ret;
}
EXPORT_SYMBOL(buffer_migrate_folio);
@@ -936,9 +1082,21 @@ EXPORT_SYMBOL(buffer_migrate_folio);
int buffer_migrate_folio_norefs(struct address_space *mapping,
struct folio *dst, struct folio *src, enum migrate_mode mode)
{
- return __buffer_migrate_folio(mapping, dst, src, mode, true);
+ int ret;
+
+ atomic_long_inc(&bh_migrate_folio_norefs);
+
+ ret = __buffer_migrate_folio(mapping, dst, src, mode, true);
+ if (ret == 0)
+ atomic_long_inc(&bh_migrate_folio_norefs_success);
+ else
+ atomic_long_inc(&bh_migrate_folio_norefs_fails);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(buffer_migrate_folio_norefs);
+#else
+static inline void mm_migrate_bh_init(struct dentry *migrate_debug_root) { }
#endif /* CONFIG_BUFFER_HEAD */
int filemap_migrate_folio(struct address_space *mapping,
@@ -2737,3 +2895,17 @@ int migrate_misplaced_folio(struct folio *folio, int node)
}
#endif /* CONFIG_NUMA_BALANCING */
#endif /* CONFIG_NUMA */
+
+static __init int mm_migrate_debugfs_init(void)
+{
+ struct dentry *mm_debug_root;
+ struct dentry *migrate_debug_root;
+
+ mm_debug_root = debugfs_create_dir("mm", NULL);
+ migrate_debug_root = debugfs_create_dir("migrate", mm_debug_root);
+
+ mm_migrate_bh_init(migrate_debug_root);
+
+ return 0;
+}
+fs_initcall(mm_migrate_debugfs_init);
--
2.47.2
prev parent reply other threads:[~2025-04-10 1:50 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-10 1:49 [PATCH v2 0/8] mm: enhance migration work around on noref buffer-heads Luis Chamberlain
2025-04-10 1:49 ` [PATCH v2 1/8] migrate: fix skipping metadata buffer heads on migration Luis Chamberlain
2025-04-10 3:18 ` Matthew Wilcox
2025-04-10 12:05 ` Jan Kara
2025-04-14 21:09 ` Luis Chamberlain
2025-04-14 22:19 ` Luis Chamberlain
2025-04-15 9:05 ` Christian Brauner
2025-04-15 15:47 ` Luis Chamberlain
2025-04-15 16:23 ` Jan Kara
2025-04-15 21:06 ` Luis Chamberlain
2025-04-16 2:02 ` Davidlohr Bueso
2025-04-15 11:17 ` Jan Kara
2025-04-15 11:23 ` Jan Kara
2025-04-15 16:18 ` Luis Chamberlain
2025-04-15 16:28 ` Jan Kara
2025-04-16 16:58 ` Luis Chamberlain
2025-04-23 17:09 ` Jan Kara
2025-04-23 20:30 ` Luis Chamberlain
2025-04-25 22:51 ` Luis Chamberlain
2025-04-28 23:08 ` Luis Chamberlain
2025-04-29 9:32 ` Jan Kara
2025-04-15 1:36 ` Davidlohr Bueso
2025-04-15 11:25 ` Jan Kara
2025-04-15 18:14 ` Davidlohr Bueso
2025-04-10 1:49 ` [PATCH v2 2/8] fs/buffer: try to use folio lock for pagecache lookups Luis Chamberlain
2025-04-10 14:38 ` Jan Kara
2025-04-10 17:38 ` Davidlohr Bueso
2025-04-10 1:49 ` [PATCH v2 3/8] fs/buffer: introduce __find_get_block_nonatomic() Luis Chamberlain
2025-04-10 1:49 ` [PATCH v2 4/8] fs/ocfs2: use sleeping version of __find_get_block() Luis Chamberlain
2025-04-10 1:49 ` [PATCH v2 5/8] fs/jbd2: " Luis Chamberlain
2025-04-10 1:49 ` [PATCH v2 6/8] fs/ext4: " Luis Chamberlain
2025-04-10 13:36 ` Jan Kara
2025-04-10 17:32 ` Davidlohr Bueso
2025-04-10 1:49 ` [PATCH v2 7/8] mm/migrate: enable noref migration for jbd2 Luis Chamberlain
2025-04-10 13:40 ` Jan Kara
2025-04-10 17:30 ` Davidlohr Bueso
2025-04-14 12:12 ` Jan Kara
2025-04-10 1:49 ` Luis Chamberlain [this message]
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=20250410014945.2140781-9-mcgrof@kernel.org \
--to=mcgrof@kernel.org \
--cc=adilger.kernel@dilger.ca \
--cc=axboe@kernel.dk \
--cc=brauner@kernel.org \
--cc=da.gomez@samsung.com \
--cc=dave@stgolabs.net \
--cc=david@fromorbit.com \
--cc=david@redhat.com \
--cc=djwong@kernel.org \
--cc=gost.dev@samsung.com \
--cc=hannes@cmpxchg.org \
--cc=hare@suse.de \
--cc=jack@suse.cz \
--cc=linux-block@vger.kernel.org \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=oliver.sang@intel.com \
--cc=p.raghav@samsung.com \
--cc=riel@surriel.com \
--cc=ritesh.list@gmail.com \
--cc=tytso@mit.edu \
--cc=willy@infradead.org \
/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