[f2fs-dev] [PATCH] f2fs_io: add extent_cache command to dump read extent cache

Yongpeng Yang <[email protected]> Sat, 15 Aug 2026 00:50:08 +0800
Newsgroups net.sourceforge.lists.linux-f2fs-devel
Message-ID <[email protected]>
From: Yongpeng Yang <[email protected]>

Add an "extent_cache" subcommand that dumps the in-memory read extent
cache of a given file to the kernel log, by passing the file's
descriptor to the /sys/fs/f2fs/<dev>/extent_cache_debug sysfs entry
introduced by the kernel counterpart patch.

The dump itself is emitted by the kernel via f2fs_info(),  inspect it
with "dmesg | grep ino=<ino>".

Signed-off-by: Yongpeng Yang <[email protected]>
---
 tools/f2fs_io/f2fs_io.c | 65 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/tools/f2fs_io/f2fs_io.c b/tools/f2fs_io/f2fs_io.c
index 06e0ea0..3025b7b 100644
--- a/tools/f2fs_io/f2fs_io.c
+++ b/tools/f2fs_io/f2fs_io.c
@@ -33,6 +33,7 @@
 #include <sys/mman.h>
 #include <sys/sendfile.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <termios.h>
 #include <time.h>
@@ -1965,6 +1966,69 @@ static void do_precache_extents(int argc, char **argv, const struct cmd_desc *cm
 	exit(0);
 }
 
+#define extent_cache_desc "dump the in-memory read extent cache of a file"
+#define extent_cache_help							\
+"f2fs_io extent_cache [file_path]\n\n"					\
+"Dump the in-memory read extent cache (largest extent plus every rbtree\n" \
+"node, as fofs/blk/len) of the given file to the kernel log by passing\n" \
+"its file descriptor to the per-sb sysfs entry\n"			\
+"/sys/fs/f2fs/<dev>/extent_cache_debug.\n\n"				\
+"Read the file beforehand so its extent tree is populated, then run\n" \
+"this command and inspect the dump with 'dmesg | grep ino=<ino>'.\n"
+
+static void do_extent_cache(int argc, char **argv, const struct cmd_desc *cmd)
+{
+	struct stat st;
+	char fd_str[32];
+	char devlink[PATH_MAX];
+	char devname[NAME_MAX + 1];
+	char sysfs_path[PATH_MAX];
+	char *base;
+	ssize_t rlen;
+	int fd, sfd, ret;
+
+	if (argc != 2) {
+		fputs("Excess arguments\n\n", stderr);
+		fputs(cmd->cmd_help, stderr);
+		exit(1);
+	}
+
+	/* Open the target file; its fd is handed to the kernel. */
+	fd = xopen(argv[1], O_RDONLY, 0);
+	if (fstat(fd, &st) < 0)
+		die_errno("fstat %s failed", argv[1]);
+	if (!S_ISREG(st.st_mode))
+		die("%s is not a regular file", argv[1]);
+
+	/* Resolve the kernel device name (sb->s_id) from st_dev. */
+	ret = snprintf(devlink, sizeof(devlink), "/sys/dev/block/%u:%u",
+		       major(st.st_dev), minor(st.st_dev));
+	if (ret < 0 || (size_t)ret >= sizeof(devlink))
+		die("device path too long");
+	rlen = readlink(devlink, sysfs_path, sizeof(sysfs_path) - 1);
+	if (rlen < 0)
+		die_errno("readlink %s failed", devlink);
+	sysfs_path[rlen] = '\0';
+	base = basename(sysfs_path);
+	snprintf(devname, sizeof(devname), "%s", base);
+
+	ret = snprintf(sysfs_path, sizeof(sysfs_path),
+		       "/sys/fs/f2fs/%s/extent_cache_debug", devname);
+	if (ret < 0 || (size_t)ret >= sizeof(sysfs_path))
+		die("sysfs path too long");
+
+	/* Pass the fd to the kernel; the dump is written to the kernel log. */
+	snprintf(fd_str, sizeof(fd_str), "%d", fd);
+	sfd = xopen(sysfs_path, O_WRONLY, 0);
+	full_write(sfd, fd_str, strlen(fd_str));
+	close(sfd);
+
+	printf("ino=%lu extent cache dump written to kernel log\n"
+	       "inspect with: dmesg | grep ino=%lu\n", st.st_ino, st.st_ino);
+	close(fd);
+	exit(0);
+}
+
 #define move_range_desc "moving a range of data blocks from source file to destination file"
 #define move_range_help						\
 "f2fs_io move_range [src_path] [dst_path] [src_start] [dst_start] "	\
@@ -2636,6 +2700,7 @@ const struct cmd_desc cmd_list[] = {
 	CMD(gc),
 	CMD(checkpoint),
 	CMD(precache_extents),
+	CMD(extent_cache),
 	CMD(move_range),
 	CMD(gc_range),
 	CMD(listxattr),
-- 
2.43.0



_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel