f2fs_read_data_large_folio() relies on f2fs_map_blocks() to decide whether
a subpage should be zero-filled or queued to a read bio.
However, f2fs_map_blocks() can set F2FS_MAP_MAPPED for NULL_ADDR and
NEW_ADDR in the non-DIO, no-create path. The large folio read code then
treats such hole blocks as mapped blocks, and may account them
in read_pages_pending and attempt to build bios for them, which can
leave tasks stuck in readahead for heavily fragmented files.
Add a helper, f2fs_block_needs_zeroing(), which detects NULL_ADDR and
NEW_ADDR from struct f2fs_map_blocks. Use it to prioritize the zeroing
path by checking f2fs_block_needs_zeroing() before
(map.m_flags & F2FS_MAP_MAPPED) under got_it: label.
Signed-off-by: Nanzhe Zhao <nzzhao@126.com>
---
fs/f2fs/data.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 4bef04560924..66ab7a43a56f 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2413,6 +2413,11 @@ static void ffs_detach_free(struct folio *folio)
kmem_cache_free(ffs_entry_slab, ffs);
}
+static inline bool f2fs_block_needs_zeroing(const struct f2fs_map_blocks *map)
+{
+ return map->m_pblk == NULL_ADDR || map->m_pblk == NEW_ADDR;
+}
+
static int f2fs_read_data_large_folio(struct inode *inode,
struct readahead_control *rac, struct folio *folio)
{
@@ -2468,14 +2473,7 @@ static int f2fs_read_data_large_folio(struct inode *inode,
if (ret)
goto err_out;
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,
- DATA_GENERIC_ENHANCE_READ)) {
- ret = -EFSCORRUPTED;
- goto err_out;
- }
- } else {
+ if ((f2fs_block_needs_zeroing(&map))) {
folio_zero_range(folio, offset << PAGE_SHIFT, PAGE_SIZE);
if (f2fs_need_verity(inode, index) &&
!fsverity_verify_page(folio_file_page(folio,
@@ -2484,6 +2482,13 @@ static int f2fs_read_data_large_folio(struct inode *inode,
goto err_out;
}
continue;
+ } else 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,
+ DATA_GENERIC_ENHANCE_READ)) {
+ ret = -EFSCORRUPTED;
+ goto err_out;
+ }
}
/* We must increment read_pages_pending before possible BIOs submitting
--
2.34.1
Hi Nanzhe,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 48b5439e04ddf4508ecaf588219012dc81d947c0]
url: https://github.com/intel-lab-lkp/linux/commits/Nanzhe-Zhao/f2fs-Zero-f2fs_folio_state-on-allocation/20260106-005006
base: 48b5439e04ddf4508ecaf588219012dc81d947c0
patch link: https://lore.kernel.org/r/20260105153101.152892-4-nzzhao%40126.com
patch subject: [PATCH v1 3/5] f2fs: add f2fs_block_needs_zeroing() to handle hole blocks
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20260106/202601061013.MBnRTOrG-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260106/202601061013.MBnRTOrG-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601061013.MBnRTOrG-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> fs/f2fs/data.c:2485:13: warning: variable 'block_nr' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
2485 | } else if((map.m_flags & F2FS_MAP_MAPPED)) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/f2fs/data.c:2527:39: note: uninitialized use occurs here
2527 | f2fs_wait_on_block_writeback(inode, block_nr);
| ^~~~~~~~
fs/f2fs/data.c:2485:10: note: remove the 'if' if its condition is always true
2485 | } else if((map.m_flags & F2FS_MAP_MAPPED)) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/f2fs/data.c:2454:20: note: initialize the variable 'block_nr' to silence this warning
2454 | sector_t block_nr;
| ^
| = 0
1 warning generated.
vim +2485 fs/f2fs/data.c
2420
2421 static int f2fs_read_data_large_folio(struct inode *inode,
2422 struct readahead_control *rac, struct folio *folio)
2423 {
2424 struct bio *bio = NULL;
2425 sector_t last_block_in_bio = 0;
2426 struct f2fs_map_blocks map = {0, };
2427 pgoff_t index, offset;
2428 unsigned max_nr_pages = rac ? readahead_count(rac) :
2429 folio_nr_pages(folio);
2430 unsigned nrpages;
2431 struct f2fs_folio_state *ffs;
2432 int ret = 0;
2433
2434 if (!IS_IMMUTABLE(inode))
2435 return -EOPNOTSUPP;
2436
2437 if (f2fs_compressed_file(inode))
2438 return -EOPNOTSUPP;
2439
2440 map.m_seg_type = NO_CHECK_TYPE;
2441
2442 if (rac)
2443 folio = readahead_folio(rac);
2444 next_folio:
2445 if (!folio)
2446 goto out;
2447
2448 index = folio->index;
2449 offset = 0;
2450 ffs = NULL;
2451 nrpages = folio_nr_pages(folio);
2452
2453 for (; nrpages; nrpages--) {
2454 sector_t block_nr;
2455 /*
2456 * Map blocks using the previous result first.
2457 */
2458 if ((map.m_flags & F2FS_MAP_MAPPED) &&
2459 index > map.m_lblk &&
2460 index < (map.m_lblk + map.m_len))
2461 goto got_it;
2462
2463 /*
2464 * Then do more f2fs_map_blocks() calls until we are
2465 * done with this page.
2466 */
2467 memset(&map, 0, sizeof(map));
2468 map.m_seg_type = NO_CHECK_TYPE;
2469 map.m_lblk = index;
2470 map.m_len = max_nr_pages;
2471
2472 ret = f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_DEFAULT);
2473 if (ret)
2474 goto err_out;
2475 got_it:
2476 if ((f2fs_block_needs_zeroing(&map))) {
2477 folio_zero_range(folio, offset << PAGE_SHIFT, PAGE_SIZE);
2478 if (f2fs_need_verity(inode, index) &&
2479 !fsverity_verify_page(folio_file_page(folio,
2480 index))) {
2481 ret = -EIO;
2482 goto err_out;
2483 }
2484 continue;
> 2485 } else if((map.m_flags & F2FS_MAP_MAPPED)) {
2486 block_nr = map.m_pblk + index - map.m_lblk;
2487 if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr,
2488 DATA_GENERIC_ENHANCE_READ)) {
2489 ret = -EFSCORRUPTED;
2490 goto err_out;
2491 }
2492 }
2493
2494 /* We must increment read_pages_pending before possible BIOs submitting
2495 * to prevent from premature folio_end_read() call on folio
2496 */
2497 if (folio_test_large(folio)) {
2498 ffs = ffs_find_or_alloc(folio);
2499
2500 /* set the bitmap to wait */
2501 spin_lock_irq(&ffs->state_lock);
2502 ffs->read_pages_pending++;
2503 spin_unlock_irq(&ffs->state_lock);
2504 }
2505
2506 /*
2507 * This page will go to BIO. Do we need to send this
2508 * BIO off first?
2509 */
2510 if (bio && (!page_is_mergeable(F2FS_I_SB(inode), bio,
2511 last_block_in_bio, block_nr) ||
2512 !f2fs_crypt_mergeable_bio(bio, inode, index, NULL))) {
2513 submit_and_realloc:
2514 f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA);
2515 bio = NULL;
2516 }
2517 if (bio == NULL)
2518 bio = f2fs_grab_read_bio(inode, block_nr,
2519 max_nr_pages,
2520 f2fs_ra_op_flags(rac),
2521 index, false);
2522
2523 /*
2524 * If the page is under writeback, we need to wait for
2525 * its completion to see the correct decrypted data.
2526 */
2527 f2fs_wait_on_block_writeback(inode, block_nr);
2528
2529 if (!bio_add_folio(bio, folio, F2FS_BLKSIZE,
2530 offset << PAGE_SHIFT))
2531 goto submit_and_realloc;
2532
2533 inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA);
2534 f2fs_update_iostat(F2FS_I_SB(inode), NULL, FS_DATA_READ_IO,
2535 F2FS_BLKSIZE);
2536 last_block_in_bio = block_nr;
2537 index++;
2538 offset++;
2539 }
2540 trace_f2fs_read_folio(folio, DATA);
2541 if (rac) {
2542 folio = readahead_folio(rac);
2543 goto next_folio;
2544 }
2545 err_out:
2546 /* Nothing was submitted. */
2547 if (!bio) {
2548 if (!ret)
2549 folio_mark_uptodate(folio);
2550 folio_unlock(folio);
2551 return ret;
2552 }
2553
2554 if (ret) {
2555 f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA);
2556
2557 /* Wait bios and clear uptodate. */
2558 folio_lock(folio);
2559 folio_clear_uptodate(folio);
2560 folio_unlock(folio);
2561 }
2562 out:
2563 f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA);
2564 return ret;
2565 }
2566
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
On 1/5/2026 11:30 PM, Nanzhe Zhao wrote:
> f2fs_read_data_large_folio() relies on f2fs_map_blocks() to decide whether
> a subpage should be zero-filled or queued to a read bio.
>
> However, f2fs_map_blocks() can set F2FS_MAP_MAPPED for NULL_ADDR and
> NEW_ADDR in the non-DIO, no-create path. The large folio read code then
Nanzhe,
IIUC, f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_DEFAULT) won't map hole
space, or am I missing something?
Thanks,
> treats such hole blocks as mapped blocks, and may account them
> in read_pages_pending and attempt to build bios for them, which can
> leave tasks stuck in readahead for heavily fragmented files.
>
> Add a helper, f2fs_block_needs_zeroing(), which detects NULL_ADDR and
> NEW_ADDR from struct f2fs_map_blocks. Use it to prioritize the zeroing
> path by checking f2fs_block_needs_zeroing() before
> (map.m_flags & F2FS_MAP_MAPPED) under got_it: label.
>
> Signed-off-by: Nanzhe Zhao <nzzhao@126.com>
> ---
> fs/f2fs/data.c | 21 +++++++++++++--------
> 1 file changed, 13 insertions(+), 8 deletions(-)
>
> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> index 4bef04560924..66ab7a43a56f 100644
> --- a/fs/f2fs/data.c
> +++ b/fs/f2fs/data.c
> @@ -2413,6 +2413,11 @@ static void ffs_detach_free(struct folio *folio)
> kmem_cache_free(ffs_entry_slab, ffs);
> }
>
> +static inline bool f2fs_block_needs_zeroing(const struct f2fs_map_blocks *map)
> +{
> + return map->m_pblk == NULL_ADDR || map->m_pblk == NEW_ADDR;
> +}
> +
> static int f2fs_read_data_large_folio(struct inode *inode,
> struct readahead_control *rac, struct folio *folio)
> {
> @@ -2468,14 +2473,7 @@ static int f2fs_read_data_large_folio(struct inode *inode,
> if (ret)
> goto err_out;
> 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,
> - DATA_GENERIC_ENHANCE_READ)) {
> - ret = -EFSCORRUPTED;
> - goto err_out;
> - }
> - } else {
> + if ((f2fs_block_needs_zeroing(&map))) {
> folio_zero_range(folio, offset << PAGE_SHIFT, PAGE_SIZE);
> if (f2fs_need_verity(inode, index) &&
> !fsverity_verify_page(folio_file_page(folio,
> @@ -2484,6 +2482,13 @@ static int f2fs_read_data_large_folio(struct inode *inode,
> goto err_out;
> }
> continue;
> + } else 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,
> + DATA_GENERIC_ENHANCE_READ)) {
> + ret = -EFSCORRUPTED;
> + goto err_out;
> + }
> }
>
> /* We must increment read_pages_pending before possible BIOs submitting
> --
> 2.34.1
>
At 2026-01-06 17:19:14, "Chao Yu" <chao@kernel.org> wrote: >On 1/5/2026 11:30 PM, Nanzhe Zhao wrote: >>IIUC, f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_DEFAULT) won't map hole >>space, or am I missing something? >> >>Thanks, My fault, I missed the goto sync_out statement in default case. Thanks for pointing out.
© 2016 - 2026 Red Hat, Inc.