]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
f2fs: map data writes to FDP streams
authorWenjie Qi <qwjhust@gmail.com>
Fri, 17 Apr 2026 03:51:26 +0000 (11:51 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 22 May 2026 03:49:06 +0000 (03:49 +0000)
From: Wenjie Qi <qiwenjie@xiaomi.com>

F2FS already classifies DATA writes using its existing hot, warm and cold
temperature policy, but it only passes that intent down as a write hint.
That hint alone is not sufficient for NVMe FDP placement, because the
current NVMe command path consumes `bio->bi_write_stream` rather than
`bio->bi_write_hint` when selecting a placement ID.

When the target block device exposes write streams, map the existing F2FS
DATA temperature classes onto stream IDs and set `bio->bi_write_stream`
for both buffered and direct writes. If the device exposes no write
streams, keep the current behavior by leaving the stream unset.

The stream mapping is evaluated against the target block device of each
bio, so the existing per-device fallback behavior stays unchanged for
multi-device filesystems. Existing blkzoned restrictions also remain in
place.

The mapping is intentionally small and deterministic:

- 1 stream: hot, warm and cold all use stream 1
- 2 streams: hot/warm use 1, cold uses 2
- 3+ streams: hot uses 1, warm uses 2, cold uses 3

Signed-off-by: Wenjie Qi <qwjhust@gmail.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Documentation/filesystems/f2fs.rst
fs/f2fs/data.c
fs/f2fs/f2fs.h
fs/f2fs/file.c
fs/f2fs/segment.c

index 7e40316312867cf4586ad13b1d6c4c787bc9de43..8c4a14ae444f479ef05b8fd910d2017df1fe3375 100644 (file)
@@ -137,6 +137,15 @@ noacl                       Disable POSIX Access Control List. Note: acl is enabled
 active_logs=%u          Support configuring the number of active logs. In the
                         current design, f2fs supports only 2, 4, and 6 logs.
                         Default number is 6.
+                        When the underlying block device exposes write
+                        streams, the default active_logs=6 configuration
+                        maps hot, warm, and cold DATA writes to streams 1,
+                        2, and 3, respectively. If only one or two write
+                        streams are available, f2fs falls back to mapping
+                        all DATA writes to stream 1 or mapping hot/warm
+                        to stream 1 and cold to stream 2. If no write
+                        streams are exposed, f2fs leaves the stream
+                        unset.
 disable_ext_identify    Disable the extension list configured by mkfs, so f2fs
                         is not aware of cold files such as media files.
 inline_xattr            Enable the inline xattrs feature.
index 8d4f1e75dee3eb175aa0164cdf211ee6aebad00b..0f92a8805635ca2bef6988578e145569bd4a280b 100644 (file)
@@ -509,6 +509,8 @@ static struct bio *__bio_alloc(struct f2fs_io_info *fio, int npages)
                bio->bi_private = sbi;
                bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi,
                                                fio->type, fio->temp);
+               bio->bi_write_stream = f2fs_io_type_to_write_stream(bdev, fio->type,
+                                                                   fio->temp);
        }
        iostat_alloc_and_bind_ctx(sbi, bio, NULL);
 
index 91f506e7c9cfb5bfd9fef17f533ade2a8b37b652..dc8f3b55b5607331cb60ab696255eb7e4d7ff700 100644 (file)
@@ -4061,6 +4061,8 @@ void f2fs_destroy_segment_manager_caches(void);
 int f2fs_rw_hint_to_seg_type(struct f2fs_sb_info *sbi, enum rw_hint hint);
 enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi,
                        enum page_type type, enum temp_type temp);
+u8 f2fs_io_type_to_write_stream(struct block_device *bdev,
+                               enum page_type type, enum temp_type temp);
 unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi);
 unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi,
                        unsigned int segno);
index fb12c5c9affda075585c14edd7add47fede1fa42..2d8b383ecf52f615a9f03578f9ea619541e66383 100644 (file)
@@ -5076,6 +5076,8 @@ static void f2fs_dio_write_submit_io(const struct iomap_iter *iter,
        enum temp_type temp = f2fs_get_segment_temp(sbi, type);
 
        bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, DATA, temp);
+       bio->bi_write_stream =
+               f2fs_io_type_to_write_stream(bio->bi_bdev, DATA, temp);
        blk_crypto_submit_bio(bio);
 }
 
index 788f8b05024927cec89e379fc7fab2dce7b006e8..9cce4d94ac8258de5c0b8f7ab7fec062364fd3a8 100644 (file)
@@ -3636,6 +3636,19 @@ enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi,
        }
 }
 
+u8 f2fs_io_type_to_write_stream(struct block_device *bdev,
+                               enum page_type type, enum temp_type temp)
+{
+       unsigned short nr = bdev_max_write_streams(bdev);
+
+       if (type != DATA || !nr)
+               return 0;
+       if (nr < NR_TEMP_TYPE)
+               return temp == COLD ? nr : HOT + 1;
+
+       return temp + 1;
+}
+
 static int __get_segment_type_2(struct f2fs_io_info *fio)
 {
        if (fio->type == DATA)