[PATCH 11/14] f2fs: fix zeroing paths for large folios

From: Nanzhe Zhao

Date: Wed Aug 26 2026 - 09:10:22 EST


Several f2fs zeroing paths still use PAGE_SIZE based offsets after
a file mapping can contain large folios. This is fine for order-0
folios, but it zeros the wrong range once the target block is not at
offset 0 in a large folio.

Use offset_in_folio() to translate the file block index to the folio
offset before zeroing data in truncate_partial_data_page(), fill_zero(),
and f2fs_get_new_data_folio().

Signed-off-by: Nanzhe Zhao <zhaonanzhe@xxxxxxxxxx>
---
fs/f2fs/data.c | 13 ++++++++++---
fs/f2fs/f2fs.h | 1 +
fs/f2fs/file.c | 27 ++++++++++++++++++++++-----
3 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 15774e8e8795..0b167b14a9a5 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1582,9 +1582,16 @@ struct folio *f2fs_get_new_data_folio(struct inode *inode,
goto got_it;

if (dn.data_blkaddr == NEW_ADDR) {
- folio_zero_segment(folio, 0, folio_size(folio));
- if (!folio_test_uptodate(folio))
+ size_t off = offset_in_folio(folio,
+ (loff_t)index << PAGE_SHIFT);
+
+ folio_zero_segment(folio, off, off + PAGE_SIZE);
+ if (folio_test_large(folio)) {
+ f2fs_ffs_find_or_alloc(folio);
+ f2fs_ffs_mark_subrange_uptodate(folio, off, PAGE_SIZE);
+ } else if (!folio_test_uptodate(folio)) {
folio_mark_uptodate(folio);
+ }
} else {
f2fs_folio_put(folio, true);

@@ -2670,7 +2677,7 @@ static bool __ffs_mark_subrange_uptodate(struct folio *folio,
return bitmap_full(ffs->state, nr_subpages);
}

-static void f2fs_ffs_mark_subrange_uptodate(struct folio *folio, size_t offset,
+void f2fs_ffs_mark_subrange_uptodate(struct folio *folio, size_t offset,
size_t len)
{
struct f2fs_folio_state *ffs;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 0ffbe2bd04c8..6ae249bf9aa1 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -4330,6 +4330,7 @@ struct folio *f2fs_get_lock_data_folio(struct inode *inode, pgoff_t index,
bool for_write);
struct folio *f2fs_get_new_data_folio(struct inode *inode,
struct folio *ifolio, pgoff_t index, bool new_i_size);
+void f2fs_ffs_mark_subrange_uptodate(struct folio *folio, size_t offset, size_t len);
int f2fs_do_write_data_page(struct f2fs_io_info *fio);
int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag);
int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 02d687527241..4272013dbe38 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -811,12 +811,12 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
static int truncate_partial_data_page(struct inode *inode, u64 from,
bool cache_only)
{
- loff_t offset = from & (PAGE_SIZE - 1);
pgoff_t index = from >> PAGE_SHIFT;
struct address_space *mapping = inode->i_mapping;
struct folio *folio;
+ size_t folio_off;

- if (!offset && !cache_only)
+ if (!(from & (PAGE_SIZE - 1)) && !cache_only)
return 0;

if (cache_only) {
@@ -834,12 +834,21 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
return PTR_ERR(folio) == -ENOENT ? 0 : PTR_ERR(folio);
truncate_out:
f2fs_folio_wait_writeback(folio, DATA, true, true);
- folio_zero_segment(folio, offset, folio_size(folio));
+ folio_off = offset_in_folio(folio, from);
+ folio_zero_segment(folio, folio_off, folio_size(folio));

/* An encrypted inode should have a key and truncate the last page. */
f2fs_bug_on(F2FS_I_SB(inode), cache_only && IS_ENCRYPTED(inode));
- if (!cache_only)
+ if (!cache_only) {
+ if (folio_test_large(folio)) {
+ f2fs_ffs_find_or_alloc(folio);
+ f2fs_ffs_mark_subrange_uptodate(folio, folio_off,
+ folio_size(folio) - folio_off);
+ f2fs_ffs_mark_subrange_dirty(folio, folio_off,
+ folio_size(folio) - folio_off);
+ }
folio_mark_dirty(folio);
+ }
f2fs_folio_put(folio, true);
return 0;
}
@@ -1310,6 +1319,7 @@ static int fill_zero(struct inode *inode, pgoff_t index,
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct folio *folio;
struct f2fs_lock_context lc;
+ size_t folio_off;

if (!len)
return 0;
@@ -1324,7 +1334,14 @@ static int fill_zero(struct inode *inode, pgoff_t index,
return PTR_ERR(folio);

f2fs_folio_wait_writeback(folio, DATA, true, true);
- folio_zero_range(folio, start, len);
+ folio_off = offset_in_folio(folio,
+ (loff_t)index << PAGE_SHIFT) + start;
+ folio_zero_range(folio, folio_off, len);
+ if (folio_test_large(folio)) {
+ f2fs_ffs_find_or_alloc(folio);
+ f2fs_ffs_mark_subrange_uptodate(folio, folio_off, len);
+ f2fs_ffs_mark_subrange_dirty(folio, folio_off, len);
+ }
folio_mark_dirty(folio);
f2fs_folio_put(folio, true);
return 0;
--
2.43.0