f2fs: zone: fix to calculate first_zoned_segno correctly

[ Upstream commit dc6d9ef57fcf42fac1b3be4bff5ac5b3f1e8f9f3 ]

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: 9703d69d9d ("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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Chao Yu
2025-04-10 11:10:19 +08:00
committed by Greg Kroah-Hartman
parent ffbbe11577
commit 8912b139a8
4 changed files with 69 additions and 20 deletions

View File

@@ -3986,7 +3986,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++;

View File

@@ -1762,7 +1762,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;
@@ -4557,12 +4557,16 @@ F2FS_FEATURE_FUNCS(compression, COMPRESSION);
F2FS_FEATURE_FUNCS(readonly, RO);
#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
@@ -4634,15 +4638,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)

View File

@@ -2719,7 +2719,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
@@ -2731,7 +2731,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,
@@ -2784,9 +2784,9 @@ got_it:
goto out_unlock;
}
/* 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;
}
@@ -3250,7 +3250,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);

View File

@@ -4260,14 +4260,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)
@@ -4304,6 +4325,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])
@@ -4671,7 +4700,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);