From: liujinbao1 <[email protected]>

Add the undiscard_blks_bysize node to classify undiscard_blks
into three categories: [0,15], [15,511], and 512+, and count
the number of blocks in each category as undiscard_small_blks,
undiscard_middle_blks, and undiscard_large_blks, respectively,
in order to better understand the size distribution of undiscard_blks.
e.g. when the undiscard_blks value is 794,
the undiscard_blks_bysize node shows the number of undiscard_blks
in each range as: small: 424, middle: 370, large: 0.

Signed-off-by: Sheng Yong <[email protected]>
Signed-off-by: liujinbao1 <[email protected]>
---
 Documentation/ABI/testing/sysfs-fs-f2fs |  8 ++++++
 fs/f2fs/f2fs.h                          |  3 +++
 fs/f2fs/segment.c                       | 34 +++++++++++++++++++++++++
 fs/f2fs/sysfs.c                         | 13 ++++++++++
 4 files changed, 58 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs 
b/Documentation/ABI/testing/sysfs-fs-f2fs
index 770470e0598b..23247030e27c 100644
--- a/Documentation/ABI/testing/sysfs-fs-f2fs
+++ b/Documentation/ABI/testing/sysfs-fs-f2fs
@@ -516,6 +516,14 @@ Date:              December 2023
 Contact:       "Zhiguo Niu" <[email protected]>
 Description:   Shows the total number of undiscard blocks.
 
+What:          /sys/fs/f2fs/<disk>/stat/undiscard_blks_bysize
+Date:          Feb 2026
+Contact:       "Jinbao Liu" <[email protected]>
+Description:   Show undiscard block counts by size category.
+               Three block count ranges: small (0-15 blocks),
+               middle (16-511 blocks), large (512+ blocks).
+               Format: "small: %u, middle: %u, large: %u\n"
+
 What:          /sys/fs/f2fs/<disk>/ckpt_thread_ioprio
 Date:          January 2021
 Contact:       "Daeho Jeong" <[email protected]>
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index a90a62cfe617..033666f2f368 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -501,6 +501,9 @@ struct discard_cmd_control {
        unsigned int max_ordered_discard;       /* maximum discard granularity 
issued by lba order */
        unsigned int discard_io_aware;          /* io_aware policy */
        unsigned int undiscard_blks;            /* # of undiscard blocks */
+       unsigned int undiscard_small_blks;      /* # of undiscard blocks range 
in [0, 15] */
+       unsigned int undiscard_middle_blks;     /* # of undiscard blocks range 
in [16, 511]*/
+       unsigned int undiscard_large_blks;      /* # of undiscard blocks range 
> 511*/
        unsigned int next_pos;                  /* next discard position */
        atomic_t issued_discard;                /* # of issued discard */
        atomic_t queued_discard;                /* # of queued discard */
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index c26424f47686..c354a0f49802 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -962,6 +962,26 @@ static unsigned int get_free_segment(struct f2fs_sb_info 
*sbi)
        return NULL_SEGNO;
 }
 
+static void __stat_undiscard_blks(struct discard_cmd_control *dcc,
+                               block_t len, bool inc)
+{
+       if (inc) {
+               if (len < DEFAULT_DISCARD_GRANULARITY)
+                       dcc->undiscard_small_blks += len;
+               else if (len < MAX_PLIST_NUM)
+                       dcc->undiscard_middle_blks += len;
+               else
+                       dcc->undiscard_large_blks += len;
+       } else {
+               if (len < DEFAULT_DISCARD_GRANULARITY)
+                       dcc->undiscard_small_blks -= len;
+               else if (len < MAX_PLIST_NUM)
+                       dcc->undiscard_middle_blks -= len;
+               else
+                       dcc->undiscard_large_blks -= len;
+       }
+}
+
 static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi,
                struct block_device *bdev, block_t lstart,
                block_t start, block_t len)
@@ -990,6 +1010,7 @@ static struct discard_cmd *__create_discard_cmd(struct 
f2fs_sb_info *sbi,
        dc->bio_ref = 0;
        atomic_inc(&dcc->discard_cmd_cnt);
        dcc->undiscard_blks += len;
+       __stat_undiscard_blks(dcc, len, true);
 
        return dc;
 }
@@ -1108,6 +1129,7 @@ static void __detach_discard_cmd(struct 
discard_cmd_control *dcc,
        list_del(&dc->list);
        rb_erase_cached(&dc->rb_node, &dcc->root);
        dcc->undiscard_blks -= dc->di.len;
+       __stat_undiscard_blks(dcc, dc->di.len, false);
 
        kmem_cache_free(discard_cmd_slab, dc);
 
@@ -1393,6 +1415,8 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi,
        }
 
        if (!err && len) {
+               __stat_undiscard_blks(dcc, (dc->di.len + len), false);
+               __stat_undiscard_blks(dcc, dc->di.len, true);
                dcc->undiscard_blks -= len;
                __update_discard_tree_range(sbi, bdev, lstart, start, len);
        }
@@ -1450,10 +1474,12 @@ static void __punch_discard_cmd(struct f2fs_sb_info 
*sbi,
        }
 
        dcc->undiscard_blks -= di.len;
+       __stat_undiscard_blks(dcc, di.len, false);
 
        if (blkaddr > di.lstart) {
                dc->di.len = blkaddr - dc->di.lstart;
                dcc->undiscard_blks += dc->di.len;
+               __stat_undiscard_blks(dcc, dc->di.len, true);
                __relocate_discard_cmd(dcc, dc);
                modified = true;
        }
@@ -1468,6 +1494,7 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi,
                        dc->di.len--;
                        dc->di.start++;
                        dcc->undiscard_blks += dc->di.len;
+                       __stat_undiscard_blks(dcc, dc->di.len, true);
                        __relocate_discard_cmd(dcc, dc);
                }
        }
@@ -1524,8 +1551,10 @@ static void __update_discard_tree_range(struct 
f2fs_sb_info *sbi,
                        prev_dc->bdev == bdev &&
                        __is_discard_back_mergeable(&di, &prev_dc->di,
                                                        max_discard_blocks)) {
+                       __stat_undiscard_blks(dcc, prev_dc->di.len, false);
                        prev_dc->di.len += di.len;
                        dcc->undiscard_blks += di.len;
+                       __stat_undiscard_blks(dcc, prev_dc->di.len, true);
                        __relocate_discard_cmd(dcc, prev_dc);
                        di = prev_dc->di;
                        tdc = prev_dc;
@@ -1536,10 +1565,12 @@ static void __update_discard_tree_range(struct 
f2fs_sb_info *sbi,
                        next_dc->bdev == bdev &&
                        __is_discard_front_mergeable(&di, &next_dc->di,
                                                        max_discard_blocks)) {
+                       __stat_undiscard_blks(dcc, next_dc->di.len, false);
                        next_dc->di.lstart = di.lstart;
                        next_dc->di.len += di.len;
                        next_dc->di.start = di.start;
                        dcc->undiscard_blks += di.len;
+                       __stat_undiscard_blks(dcc, next_dc->di.len, true);
                        __relocate_discard_cmd(dcc, next_dc);
                        if (tdc)
                                __remove_discard_cmd(sbi, tdc);
@@ -2349,6 +2380,9 @@ static int create_discard_cmd_control(struct f2fs_sb_info 
*sbi)
        dcc->max_discard_issue_time = DEF_MAX_DISCARD_ISSUE_TIME;
        dcc->discard_urgent_util = DEF_DISCARD_URGENT_UTIL;
        dcc->undiscard_blks = 0;
+       dcc->undiscard_small_blks = 0;
+       dcc->undiscard_middle_blks = 0;
+       dcc->undiscard_large_blks = 0;
        dcc->next_pos = 0;
        dcc->root = RB_ROOT_CACHED;
        dcc->rbtree_check = false;
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index c42f4f979d13..9b11a490c8d0 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -176,6 +176,17 @@ static ssize_t undiscard_blks_show(struct f2fs_attr *a,
                                SM_I(sbi)->dcc_info->undiscard_blks);
 }
 
+static ssize_t undiscard_blks_bysize_show(struct f2fs_attr *a,
+               struct f2fs_sb_info *sbi, char *buf)
+{
+       if (!SM_I(sbi)->dcc_info)
+               return -EINVAL;
+       return sysfs_emit(buf, "small: %u, middle: %u, large: %u\n",
+                               SM_I(sbi)->dcc_info->undiscard_small_blks,
+                               SM_I(sbi)->dcc_info->undiscard_middle_blks,
+                               SM_I(sbi)->dcc_info->undiscard_large_blks);
+}
+
 static ssize_t atgc_enabled_show(struct f2fs_attr *a,
                struct f2fs_sb_info *sbi, char *buf)
 {
@@ -1471,6 +1482,7 @@ F2FS_GENERAL_RO_ATTR(cp_status);
 F2FS_GENERAL_RO_ATTR(issued_discard);
 F2FS_GENERAL_RO_ATTR(queued_discard);
 F2FS_GENERAL_RO_ATTR(undiscard_blks);
+F2FS_GENERAL_RO_ATTR(undiscard_blks_bysize);
 
 static struct attribute *f2fs_stat_attrs[] = {
        ATTR_LIST(sb_status),
@@ -1478,6 +1490,7 @@ static struct attribute *f2fs_stat_attrs[] = {
        ATTR_LIST(issued_discard),
        ATTR_LIST(queued_discard),
        ATTR_LIST(undiscard_blks),
+       ATTR_LIST(undiscard_blks_bysize),
        NULL,
 };
 ATTRIBUTE_GROUPS(f2fs_stat);
-- 
2.25.1



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

Reply via email to