From: Yongpeng Yang <[email protected]>

Add F2FS_IOC_GET_READ_CACHE_EXTENTS ioctl that allows userspace to
retrieve all cached read extents for a given file. This uses a two-call
pattern similar to fiemap: the first call with ext_count=0 queries the
node_count, and the second call fetches the actual extent entries.

Signed-off-by: Yongpeng Yang <[email protected]>
---
 fs/f2fs/extent_cache.c    | 70 +++++++++++++++++++++++++++++++++++++++
 fs/f2fs/f2fs.h            |  3 ++
 fs/f2fs/file.c            | 11 ++++++
 include/uapi/linux/f2fs.h | 21 ++++++++++++
 4 files changed, 105 insertions(+)

diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index e141ffb64e5f..0c10d5639d68 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -14,6 +14,7 @@
 
 #include <linux/fs.h>
 #include <linux/f2fs_fs.h>
+#include <uapi/linux/f2fs.h>
 
 #include "f2fs.h"
 #include "node.h"
@@ -1267,6 +1268,75 @@ static void __init_extent_tree_info(struct 
extent_tree_info *eti)
        atomic_set(&eti->total_ext_node, 0);
 }
 
+int f2fs_get_read_cache_extents(struct inode *inode,
+                       struct f2fs_read_cache_extent __user *uarg)
+{
+       struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
+       struct f2fs_cache_extent_info *kbuf = NULL;
+       struct f2fs_cache_extent_info largest = {};
+       struct rb_node *node;
+       struct extent_node *en;
+       unsigned int capacity, count = 0;
+       __u32 flags = 0;
+       int ret = 0;
+
+       if (get_user(capacity, &uarg->ext_count))
+               return -EFAULT;
+
+       if (is_inode_flag_set(inode, FI_NO_EXTENT))
+               flags |= F2FS_EXT_FL_NO_EXTENT;
+
+       if (!et || (flags & F2FS_EXT_FL_NO_EXTENT)) {
+               if (put_user(0U, &uarg->ext_count) ||
+                   put_user(flags, &uarg->flags) ||
+                   put_user(0U, &uarg->node_count))
+                       return -EFAULT;
+               return 0;
+       }
+
+       if (capacity) {
+               kbuf = f2fs_kvmalloc(F2FS_I_SB(inode), capacity * 
sizeof(*kbuf), GFP_KERNEL);
+               if (!kbuf)
+                       return -ENOMEM;
+       }
+
+       read_lock(&et->lock);
+
+       largest.fofs = et->largest.fofs;
+       largest.blk = et->largest.blk;
+       largest.len = et->largest.len;
+       largest.last_access_mode = et->largest.last_access_mode;
+
+       for (node = rb_first_cached(&et->root); node; node = rb_next(node)) {
+               if (count >= capacity)
+                       break;
+               en = rb_entry(node, struct extent_node, rb_node);
+
+               kbuf[count].fofs = en->ei.fofs;
+               kbuf[count].blk = en->ei.blk;
+               kbuf[count].len = en->ei.len;
+               kbuf[count].last_access_mode = en->ei.last_access_mode;
+               count++;
+       }
+
+       read_unlock(&et->lock);
+
+       if (count && copy_to_user(uarg->extents, kbuf,
+                                 count * sizeof(*kbuf))) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       if (put_user(count, &uarg->ext_count) ||
+           put_user(flags, &uarg->flags) ||
+           put_user((u32)atomic_read(&et->node_cnt), &uarg->node_count) ||
+           copy_to_user(&uarg->largest, &largest, sizeof(largest)))
+               ret = -EFAULT;
+out:
+       kvfree(kbuf);
+       return ret;
+}
+
 void f2fs_init_extent_cache_info(struct f2fs_sb_info *sbi)
 {
        __init_extent_tree_info(&sbi->extent_tree[EX_READ]);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1588b64d04a3..69641fc31c51 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -26,6 +26,7 @@
 #include <linux/part_stat.h>
 #include <linux/rw_hint.h>
 
+#include <uapi/linux/f2fs.h>
 #include <linux/fscrypt.h>
 #include <linux/fsverity.h>
 
@@ -4590,6 +4591,8 @@ void f2fs_update_read_extent_cache(struct dnode_of_data 
*dn);
 void f2fs_update_read_extent_cache_range(struct dnode_of_data *dn,
                                pgoff_t fofs, block_t blkaddr, unsigned int len,
                                enum extent_access_mode access_mode);
+int f2fs_get_read_cache_extents(struct inode *inode,
+                       struct f2fs_read_cache_extent __user *uarg);
 unsigned int f2fs_shrink_read_extent_tree(struct f2fs_sb_info *sbi,
                        int nr_shrink);
 
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index a3a5d499eadf..66ec9927d667 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -3672,6 +3672,14 @@ static int f2fs_ioc_precache_extents(struct file *filp)
        return f2fs_precache_extents(file_inode(filp));
 }
 
+static int f2fs_ioc_get_read_cache_extents(struct file *filp, unsigned long 
arg)
+{
+       struct inode *inode = file_inode(filp);
+
+       return f2fs_get_read_cache_extents(inode,
+                       (struct f2fs_read_cache_extent __user *)arg);
+}
+
 static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp));
@@ -4744,6 +4752,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int 
cmd, unsigned long arg)
                return f2fs_ioc_get_dev_alias_file(filp, arg);
        case F2FS_IOC_IO_PRIO:
                return f2fs_ioc_io_prio(filp, arg);
+       case F2FS_IOC_GET_READ_CACHE_EXTENTS:
+               return f2fs_ioc_get_read_cache_extents(filp, arg);
        default:
                return -ENOTTY;
        }
@@ -5506,6 +5516,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int 
cmd, unsigned long arg)
        case F2FS_IOC_COMPRESS_FILE:
        case F2FS_IOC_GET_DEV_ALIAS_FILE:
        case F2FS_IOC_IO_PRIO:
+       case F2FS_IOC_GET_READ_CACHE_EXTENTS:
                break;
        default:
                return -ENOIOCTLCMD;
diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
index 795e26258355..6ff9003bc030 100644
--- a/include/uapi/linux/f2fs.h
+++ b/include/uapi/linux/f2fs.h
@@ -45,6 +45,8 @@
 #define F2FS_IOC_START_ATOMIC_REPLACE  _IO(F2FS_IOCTL_MAGIC, 25)
 #define F2FS_IOC_GET_DEV_ALIAS_FILE    _IOR(F2FS_IOCTL_MAGIC, 26, __u32)
 #define F2FS_IOC_IO_PRIO               _IOW(F2FS_IOCTL_MAGIC, 27, __u32)
+#define F2FS_IOC_GET_READ_CACHE_EXTENTS        _IOWR(F2FS_IOCTL_MAGIC, 28,     
\
+                                               struct f2fs_read_cache_extent)
 
 /*
  * should be same as XFS_IOC_GOINGDOWN.
@@ -104,4 +106,23 @@ struct f2fs_comp_option {
        __u8 log_cluster_size;
 };
 
+struct f2fs_cache_extent_info {
+       __u32 fofs;             /* start file offset in blocks */
+       __u32 blk;              /* start block address */
+       __u32 len;              /* length in blocks */
+       __u32 last_access_mode; /* last access mode of extent_node */
+};
+
+/* flags for f2fs_read_cache_extent */
+#define F2FS_EXT_FL_NO_EXTENT  0x1     /* extent cache disabled for this inode 
*/
+
+struct f2fs_read_cache_extent {
+       __u32 ext_count;        /* in: array capacity; out: mapped extent count 
*/
+       __u32 flags;            /* out: status flags */
+       __u32 node_count;       /* out: total extent nodes in tree */
+       __u32 reserved;
+       struct f2fs_cache_extent_info largest;          /* out: largest extent 
*/
+       struct f2fs_cache_extent_info extents[];        /* out: extent array */
+};
+
 #endif /* _UAPI_LINUX_F2FS_H */
-- 
2.43.0



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

Reply via email to