[f2fs-dev] [RFC PATCH v2 10/10] f2fs: support partial uptodate large folio read
From: Nanzhe Zhao
Date: Mon Jun 22 2026 - 12:12:12 EST
Buffered write can have write bytes smaller than folio size for cases when
folio minimum order is not zero. This can left partially uptodate folio in
page cache. So we skip uptodate subpage read in read_data_large_folio. Also
mark hole subpage uptodate in uptodate bitmap.
Signed-off-by: Nanzhe Zhao <zhaonanzhe@xxxxxxxxxx>
---
fs/f2fs/data.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index c7c36dad0d46..c123831a2713 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2853,6 +2853,56 @@ static int f2fs_prealloc_large_folio_write_blocks(struct inode *inode,
return 0;
}
+static unsigned int ffs_next_uptodate_subpage(struct f2fs_folio_state *ffs,
+ unsigned int start, unsigned int end)
+{
+ return find_next_bit(ffs->state, end + 1, start);
+}
+
+static unsigned int ffs_next_nonuptodate_subpage(struct f2fs_folio_state *ffs,
+ unsigned int start, unsigned int end)
+{
+ return find_next_zero_bit(ffs->state, end + 1, start);
+}
+
+static void f2fs_skip_fully_uptodate_front(struct folio *folio,
+ pgoff_t *index, pgoff_t *offset, unsigned int *nrpages,
+ unsigned int *max_nr_pages)
+{
+ struct f2fs_folio_state *ffs;
+ unsigned int next, skipped;
+
+ if (!folio_has_ffs(folio) || !*nrpages)
+ return;
+
+ ffs = folio->private;
+ next = ffs_next_nonuptodate_subpage(ffs, *offset,
+ *offset + *nrpages - 1);
+ skipped = next - *offset;
+ if (!skipped)
+ return;
+
+ *index += skipped;
+ *offset += skipped;
+ *nrpages -= skipped;
+ *max_nr_pages -= skipped;
+}
+
+static void f2fs_truncate_read_extent(struct folio *folio, pgoff_t offset,
+ unsigned int *len_blks)
+{
+ struct f2fs_folio_state *ffs;
+ unsigned int next, end;
+
+ if (!folio_has_ffs(folio) || *len_blks <= 1)
+ return;
+
+ ffs = folio->private;
+ end = offset + *len_blks - 1;
+ next = ffs_next_uptodate_subpage(ffs, offset + 1, end);
+ if (next <= end)
+ *len_blks = next - offset;
+}
static int f2fs_read_data_large_folio(struct inode *inode,
struct fsverity_info *vi,
struct readahead_control *rac, struct folio *folio)
@@ -2899,6 +2949,11 @@ static int f2fs_read_data_large_folio(struct inode *inode,
len_blks = 1;
+ f2fs_skip_fully_uptodate_front(folio, &index, &offset,
+ &nrpages, &max_nr_pages);
+ if (!nrpages)
+ break;
+
/*
* Map blocks using the previous result first.
*/
@@ -2931,6 +2986,7 @@ static int f2fs_read_data_large_folio(struct inode *inode,
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));
+ f2fs_truncate_read_extent(folio, offset, &len_blks);
for (i = 0; i < len_blks; i++) {
if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode),
@@ -2957,6 +3013,13 @@ static int f2fs_read_data_large_folio(struct inode *inode,
ret = -EIO;
goto err_out;
}
+ if (folio_test_large(folio)) {
+ ffs = ffs_find_or_alloc(folio);
+ spin_lock_irq(&ffs->state_lock);
+ __ffs_mark_subrange_uptodate(folio, ffs,
+ page_offset, PAGE_SIZE);
+ spin_unlock_irq(&ffs->state_lock);
+ }
continue;
}
--
2.34.1