[f2fs-dev] [RFC PATCH v2 09/10] f2fs: optimize small block size large folio read
From: Nanzhe Zhao
Date: Mon Jun 22 2026 - 12:15:38 EST
The original f2fs_read_data_large_folio() implementation has limited
benefit with a 4KB block size, mainly because updating
read_pages_pending greatly increases the number of spinlock
operations.
Use len_blks to batch read_pages_pending and iostat updates for
contiguous mapped blocks. If the contiguous mapping covers the whole
folio, skip f2fs_folio_state allocation for that folio.
Signed-off-by: Nanzhe Zhao <zhaonanzhe@xxxxxxxxxx>
---
fs/f2fs/data.c | 69 ++++++++++++++++++++++++++++++++++++++------------
1 file changed, 53 insertions(+), 16 deletions(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index a53fe68640d9..c7c36dad0d46 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -126,6 +126,11 @@ struct bio_post_read_ctx {
block_t fs_blkaddr;
};
+static bool __ffs_mark_subrange_uptodate(struct folio *folio,
+ struct f2fs_folio_state *ffs, size_t offset, size_t len);
+static void ffs_mark_subrange_uptodate(struct folio *folio, size_t offset,
+ size_t len);
+
/*
* Update and unlock a bio's pages, and free the bio.
*
@@ -150,6 +155,7 @@ static void f2fs_finish_read_bio(struct bio *bio, bool in_task)
struct folio *folio = fi.folio;
unsigned nr_pages = fi.length >> PAGE_SHIFT;
bool finished = true;
+ bool uptodate = bio->bi_status == BLK_STS_OK;
if (!folio_test_large(folio) &&
f2fs_is_compressed_page(folio)) {
@@ -160,10 +166,14 @@ static void f2fs_finish_read_bio(struct bio *bio, bool in_task)
continue;
}
- if (folio_test_large(folio)) {
- struct f2fs_folio_state *ffs = folio->private;
+ if (folio_has_ffs(folio)) {
+ struct f2fs_folio_state *ffs =
+ (struct f2fs_folio_state *)folio->private;
spin_lock_irqsave(&ffs->state_lock, flags);
+ if (bio->bi_status == BLK_STS_OK)
+ uptodate = __ffs_mark_subrange_uptodate(folio, ffs,
+ fi.offset, fi.length);
ffs->read_pages_pending -= nr_pages;
finished = !ffs->read_pages_pending;
spin_unlock_irqrestore(&ffs->state_lock, flags);
@@ -179,7 +189,7 @@ static void f2fs_finish_read_bio(struct bio *bio, bool in_task)
bio->bi_status = BLK_STS_IOERR;
if (finished)
- folio_end_read(folio, bio->bi_status == BLK_STS_OK);
+ folio_end_read(folio, uptodate);
}
if (ctx)
@@ -2853,7 +2863,7 @@ static int f2fs_read_data_large_folio(struct inode *inode,
pgoff_t index, offset, next_pgofs = 0;
unsigned max_nr_pages = rac ? readahead_count(rac) :
folio_nr_pages(folio);
- unsigned nrpages;
+ unsigned int nrpages, len_blks;
struct f2fs_folio_state *ffs;
int ret = 0;
bool folio_in_bio = false;
@@ -2880,8 +2890,15 @@ static int f2fs_read_data_large_folio(struct inode *inode,
ffs = NULL;
nrpages = folio_nr_pages(folio);
- for (; nrpages; nrpages--, max_nr_pages--, index++, offset++) {
+ for (; nrpages;
+ nrpages -= len_blks, max_nr_pages -= len_blks,
+ index += len_blks, offset += len_blks) {
sector_t block_nr;
+ bool whole_folio_in_bio;
+ unsigned int i;
+
+ len_blks = 1;
+
/*
* Map blocks using the previous result first.
*/
@@ -2910,13 +2927,31 @@ static int f2fs_read_data_large_folio(struct inode *inode,
got_it:
if ((map.m_flags & F2FS_MAP_MAPPED)) {
block_nr = map.m_pblk + index - map.m_lblk;
- if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr,
+
+ len_blks = min_t(unsigned int, nrpages, max_nr_pages);
+ len_blks = min_t(unsigned int, len_blks,
+ (unsigned int)(map.m_lblk + map.m_len - index));
+
+ for (i = 0; i < len_blks; i++) {
+ if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode),
+ block_nr + i,
DATA_GENERIC_ENHANCE_READ)) {
- ret = -EFSCORRUPTED;
- goto err_out;
+ ret = -EFSCORRUPTED;
+ goto err_out;
+ }
}
+
+ /*
+ * If an entire folio is added to one bio,
+ * folio_end_read() can complete the folio read status
+ * without relying on f2fs_folio_state.
+ */
+ whole_folio_in_bio = offset == 0 &&
+ len_blks == folio_nr_pages(folio);
+
} else {
size_t page_offset = offset << PAGE_SHIFT;
+
folio_zero_range(folio, page_offset, PAGE_SIZE);
if (vi && !fsverity_verify_blocks(vi, folio, PAGE_SIZE, page_offset)) {
ret = -EIO;
@@ -2926,14 +2961,14 @@ static int f2fs_read_data_large_folio(struct inode *inode,
}
/* We must increment read_pages_pending before possible BIOs submitting
- * to prevent from premature folio_end_read() call on folio
+ * to prevent from premature folio_end_read() call on folio.
*/
- if (folio_test_large(folio)) {
+ if (folio_test_large(folio) && !whole_folio_in_bio) {
ffs = ffs_find_or_alloc(folio);
/* set the bitmap to wait */
spin_lock_irq(&ffs->state_lock);
- ffs->read_pages_pending++;
+ ffs->read_pages_pending += len_blks;
spin_unlock_irq(&ffs->state_lock);
}
@@ -2958,17 +2993,19 @@ static int f2fs_read_data_large_folio(struct inode *inode,
* If the page is under writeback, we need to wait for
* its completion to see the correct decrypted data.
*/
- f2fs_wait_on_block_writeback(inode, block_nr);
+ for (i = 0; i < len_blks; i++)
+ f2fs_wait_on_block_writeback(inode, block_nr + i);
- if (!bio_add_folio(bio, folio, F2FS_BLKSIZE,
+ if (!bio_add_folio(bio, folio, len_blks * F2FS_BLKSIZE,
offset << PAGE_SHIFT))
goto submit_and_realloc;
folio_in_bio = true;
- inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA);
+ for (i = 0; i < len_blks; i++)
+ inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA);
f2fs_update_iostat(F2FS_I_SB(inode), NULL, FS_DATA_READ_IO,
- F2FS_BLKSIZE);
- last_block_in_bio = block_nr;
+ len_blks * F2FS_BLKSIZE);
+ last_block_in_bio = block_nr + len_blks - 1;
}
trace_f2fs_read_folio(folio, DATA);
err_out:
--
2.34.1