On 6/12/26 19:58, Yongpeng Yang wrote:
> 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.

It looks a little bit heavy to maintain a debug purpose ioctl interface.

Maybe set ino via sysfs and dump extent cache via procfs? only enabled
if F2FS_CHECK_FS=y?

Jaegeuk, do you have any suggestion?

Thanks,

> 
> 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 */



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

Reply via email to