f2fs: zone: fix to calculate first_zoned_segno correctly
authorChao Yu <chao@kernel.org>
Thu, 10 Apr 2025 03:10:19 +0000 (11:10 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Mon, 28 Apr 2025 15:26:48 +0000 (15:26 +0000)
A zoned device can has both conventional zones and sequential zones,
so we should not treat first segment of zoned device as first_zoned_segno,
instead, we need to check zone type for each zone during traversing zoned
device to find first_zoned_segno.

Otherwise, for below case, first_zoned_segno will be 0, which could be
wrong.

create_null_blk 512 2 1024 1024
mkfs.f2fs -m /dev/nullb0

Testcase:

export SCRIPTS_PATH=/share/git/scripts

test multiple devices w/ zoned device
for ((i=0;i<8;i++)) do {
zonesize=$((2<<$i))
conzone=$((4096/$zonesize))
seqzone=$((4096/$zonesize))
$SCRIPTS_PATH/nullblk_create.sh 512 $zonesize $conzone $seqzone
mkfs.f2fs -f -m /dev/vdb -c /dev/nullb0
mount /dev/vdb /mnt/f2fs
touch /mnt/f2fs/file
f2fs_io pinfile set /mnt/f2fs/file $((8589934592*2))
stat /mnt/f2fs/file
df
cat /proc/fs/f2fs/vdb/segment_info
umount /mnt/f2fs
$SCRIPTS_PATH/nullblk_remove.sh 0
} done

test single zoned device
for ((i=0;i<8;i++)) do {
zonesize=$((2<<$i))
conzone=$((4096/$zonesize))
seqzone=$((4096/$zonesize))
$SCRIPTS_PATH/nullblk_create.sh 512 $zonesize $conzone $seqzone
mkfs.f2fs -f -m /dev/nullb0
mount /dev/nullb0 /mnt/f2fs
touch /mnt/f2fs/file
f2fs_io pinfile set /mnt/f2fs/file $((8589934592*2))
stat /mnt/f2fs/file
df
cat /proc/fs/f2fs/nullb0/segment_info
umount /mnt/f2fs
$SCRIPTS_PATH/nullblk_remove.sh 0
} done

Fixes: 9703d69d9d15 ("f2fs: support file pinning for zoned devices")
Cc: Daeho Jeong <daehojeong@google.com>
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/data.c
fs/f2fs/f2fs.h
fs/f2fs/segment.c
fs/f2fs/super.c

index 895b29f..08a8a10 100644 (file)
@@ -3967,7 +3967,7 @@ retry:
 
                if ((pblock - SM_I(sbi)->main_blkaddr) % blks_per_sec ||
                                nr_pblocks % blks_per_sec ||
-                               !f2fs_valid_pinned_area(sbi, pblock)) {
+                               f2fs_is_sequential_zone_area(sbi, pblock)) {
                        bool last_extent = false;
 
                        not_aligned++;
index 9f40193..3fa3179 100644 (file)
@@ -1787,7 +1787,7 @@ struct f2fs_sb_info {
        unsigned int dirty_device;              /* for checkpoint data flush */
        spinlock_t dev_lock;                    /* protect dirty_device */
        bool aligned_blksize;                   /* all devices has the same logical blksize */
-       unsigned int first_zoned_segno;         /* first zoned segno */
+       unsigned int first_seq_zone_segno;      /* first segno in sequential zone */
 
        /* For write statistics */
        u64 sectors_written_start;
@@ -4620,12 +4620,16 @@ F2FS_FEATURE_FUNCS(readonly, RO);
 F2FS_FEATURE_FUNCS(device_alias, DEVICE_ALIAS);
 
 #ifdef CONFIG_BLK_DEV_ZONED
-static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi,
-                                   block_t blkaddr)
+static inline bool f2fs_zone_is_seq(struct f2fs_sb_info *sbi, int devi,
+                                                       unsigned int zone)
 {
-       unsigned int zno = blkaddr / sbi->blocks_per_blkz;
+       return test_bit(zone, FDEV(devi).blkz_seq);
+}
 
-       return test_bit(zno, FDEV(devi).blkz_seq);
+static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi,
+                                                               block_t blkaddr)
+{
+       return f2fs_zone_is_seq(sbi, devi, blkaddr / sbi->blocks_per_blkz);
 }
 #endif
 
@@ -4697,15 +4701,31 @@ static inline bool f2fs_lfs_mode(struct f2fs_sb_info *sbi)
        return F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS;
 }
 
-static inline bool f2fs_valid_pinned_area(struct f2fs_sb_info *sbi,
+static inline bool f2fs_is_sequential_zone_area(struct f2fs_sb_info *sbi,
                                          block_t blkaddr)
 {
        if (f2fs_sb_has_blkzoned(sbi)) {
+#ifdef CONFIG_BLK_DEV_ZONED
                int devi = f2fs_target_device_index(sbi, blkaddr);
 
-               return !bdev_is_zoned(FDEV(devi).bdev);
+               if (!bdev_is_zoned(FDEV(devi).bdev))
+                       return false;
+
+               if (f2fs_is_multi_device(sbi)) {
+                       if (blkaddr < FDEV(devi).start_blk ||
+                               blkaddr > FDEV(devi).end_blk) {
+                               f2fs_err(sbi, "Invalid block %x", blkaddr);
+                               return false;
+                       }
+                       blkaddr -= FDEV(devi).start_blk;
+               }
+
+               return f2fs_blkz_is_seq(sbi, devi, blkaddr);
+#else
+               return false;
+#endif
        }
-       return true;
+       return false;
 }
 
 static inline bool f2fs_low_mem_mode(struct f2fs_sb_info *sbi)
index 400988f..1d454fc 100644 (file)
@@ -2783,7 +2783,7 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
                if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_PRIOR_CONV || pinning)
                        segno = 0;
                else
-                       segno = max(sbi->first_zoned_segno, *newseg);
+                       segno = max(sbi->first_seq_zone_segno, *newseg);
                hint = GET_SEC_FROM_SEG(sbi, segno);
        }
 #endif
@@ -2795,7 +2795,7 @@ find_other_zone:
        if (secno >= MAIN_SECS(sbi) && f2fs_sb_has_blkzoned(sbi)) {
                /* Write only to sequential zones */
                if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_ONLY_SEQ) {
-                       hint = GET_SEC_FROM_SEG(sbi, sbi->first_zoned_segno);
+                       hint = GET_SEC_FROM_SEG(sbi, sbi->first_seq_zone_segno);
                        secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint);
                } else
                        secno = find_first_zero_bit(free_i->free_secmap,
@@ -2844,9 +2844,9 @@ got_it:
        /* set it as dirty segment in free segmap */
        f2fs_bug_on(sbi, test_bit(segno, free_i->free_segmap));
 
-       /* no free section in conventional zone */
+       /* no free section in conventional device or conventional zone */
        if (new_sec && pinning &&
-               !f2fs_valid_pinned_area(sbi, START_BLOCK(sbi, segno))) {
+               f2fs_is_sequential_zone_area(sbi, START_BLOCK(sbi, segno))) {
                ret = -EAGAIN;
                goto out_unlock;
        }
@@ -3317,7 +3317,7 @@ retry:
 
        if (f2fs_sb_has_blkzoned(sbi) && err == -EAGAIN && gc_required) {
                f2fs_down_write(&sbi->gc_lock);
-               err = f2fs_gc_range(sbi, 0, GET_SEGNO(sbi, FDEV(0).end_blk),
+               err = f2fs_gc_range(sbi, 0, sbi->first_seq_zone_segno - 1,
                                true, ZONED_PIN_SEC_REQUIRED_COUNT);
                f2fs_up_write(&sbi->gc_lock);
 
index 232e2fc..8abfbee 100644 (file)
@@ -4311,14 +4311,35 @@ static void f2fs_record_error_work(struct work_struct *work)
        f2fs_record_stop_reason(sbi);
 }
 
-static inline unsigned int get_first_zoned_segno(struct f2fs_sb_info *sbi)
+static inline unsigned int get_first_seq_zone_segno(struct f2fs_sb_info *sbi)
 {
+#ifdef CONFIG_BLK_DEV_ZONED
+       unsigned int zoneno, total_zones;
        int devi;
 
-       for (devi = 0; devi < sbi->s_ndevs; devi++)
-               if (bdev_is_zoned(FDEV(devi).bdev))
-                       return GET_SEGNO(sbi, FDEV(devi).start_blk);
-       return 0;
+       if (!f2fs_sb_has_blkzoned(sbi))
+               return NULL_SEGNO;
+
+       for (devi = 0; devi < sbi->s_ndevs; devi++) {
+               if (!bdev_is_zoned(FDEV(devi).bdev))
+                       continue;
+
+               total_zones = GET_ZONE_FROM_SEG(sbi, FDEV(devi).total_segments);
+
+               for (zoneno = 0; zoneno < total_zones; zoneno++) {
+                       unsigned int segs, blks;
+
+                       if (!f2fs_zone_is_seq(sbi, devi, zoneno))
+                               continue;
+
+                       segs = GET_SEG_FROM_SEC(sbi,
+                                       zoneno * sbi->secs_per_zone);
+                       blks = SEGS_TO_BLKS(sbi, segs);
+                       return GET_SEGNO(sbi, FDEV(devi).start_blk + blks);
+               }
+       }
+#endif
+       return NULL_SEGNO;
 }
 
 static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
@@ -4355,6 +4376,14 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
 #endif
 
        for (i = 0; i < max_devices; i++) {
+               if (max_devices == 1) {
+                       FDEV(i).total_segments =
+                               le32_to_cpu(raw_super->segment_count_main);
+                       FDEV(i).start_blk = 0;
+                       FDEV(i).end_blk = FDEV(i).total_segments *
+                                               BLKS_PER_SEG(sbi);
+               }
+
                if (i == 0)
                        FDEV(0).bdev_file = sbi->sb->s_bdev_file;
                else if (!RDEV(i).path[0])
@@ -4725,7 +4754,7 @@ try_onemore:
        sbi->sectors_written_start = f2fs_get_sectors_written(sbi);
 
        /* get segno of first zoned block device */
-       sbi->first_zoned_segno = get_first_zoned_segno(sbi);
+       sbi->first_seq_zone_segno = get_first_seq_zone_segno(sbi);
 
        /* Read accumulated write IO statistics if exists */
        seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);