Commit 86d563ac authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull f2fs updates from Jaegeuk Kim:
 "This focuses on two primary updates for Android devices.

  First, it sets hash-based file name lookup as the default method to
  improve performance, while retaining an option to fall back to a
  linear lookup.

  Second, it resolves a persistent issue with the 'checkpoint=enable'
  feature.

  The update further boosts performance by prefetching node blocks,
  merging FUA writes more efficiently, and optimizing block allocation
  policies.

  The release is rounded out by a comprehensive set of bug fixes that
  address memory safety, data integrity, and potential system hangs,
  along with minor documentation and code clean-ups.

  Enhancements:
   - add mount option and sysfs entry to tune the lookup mode
   - dump more information and add a timeout when enabling/disabling
     checkpoints
   - readahead node blocks in F2FS_GET_BLOCK_PRECACHE mode
   - merge FUA command with the existing writes
   - allocate HOT_DATA for IPU writes
   - Use allocate_section_policy to control write priority in
     multi-devices setups
   - add reserved nodes for privileged users
   - Add bggc_io_aware to adjust the priority of BG_GC when issuing IO
   - show the list of donation files

  Bug fixes:
   - add missing dput() when printing the donation list
   - fix UAF issue in f2fs_merge_page_bio()
   - add sanity check on ei.len in __update_extent_tree_range()
   - fix infinite loop in __insert_extent_tree()
   - fix zero-sized extent for precache extents
   - fix to mitigate overhead of f2fs_zero_post_eof_page()
   - fix to avoid migrating empty section
   - fix to truncate first page in error path of f2fs_truncate()
   - fix to update map->m_next_extent correctly in f2fs_map_blocks()
   - fix wrong layout information on 16KB page
   - fix to do sanity check on node footer for non inode dnode
   - fix to avoid NULL pointer dereference in
     f2fs_check_quota_consistency()
   - fix to detect potential corrupted nid in free_nid_list
   - fix to clear unusable_cap for checkpoint=enable
   - fix to zero data after EOF for compressed file correctly
   - fix to avoid overflow while left shift operation
   - fix condition in __allow_reserved_blocks()"

* tag 'f2fs-for-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (43 commits)
  f2fs: add missing dput() when printing the donation list
  f2fs: fix UAF issue in f2fs_merge_page_bio()
  f2fs: readahead node blocks in F2FS_GET_BLOCK_PRECACHE mode
  f2fs: add sanity check on ei.len in __update_extent_tree_range()
  f2fs: fix infinite loop in __insert_extent_tree()
  f2fs: fix zero-sized extent for precache extents
  f2fs: fix to mitigate overhead of f2fs_zero_post_eof_page()
  f2fs: fix to avoid migrating empty section
  f2fs: fix to truncate first page in error path of f2fs_truncate()
  f2fs: fix to update map->m_next_extent correctly in f2fs_map_blocks()
  f2fs: fix wrong layout information on 16KB page
  f2fs: clean up error handing of f2fs_submit_page_read()
  f2fs: avoid unnecessary folio_clear_uptodate() for cleanup
  f2fs: merge FUA command with the existing writes
  f2fs: allocate HOT_DATA for IPU writes
  f2fs: Use allocate_section_policy to control write priority in multi-devices setups
  Documentation: f2fs: Reword title
  Documentation: f2fs: Indent compression_mode option list
  Documentation: f2fs: Wrap snippets in literal code blocks
  Documentation: f2fs: Span write hint table section rows
  ...
parents f0712c20 4e715744
Loading
Loading
Loading
Loading
+53 −3
Original line number Diff line number Diff line
@@ -822,8 +822,8 @@ What: /sys/fs/f2fs/<disk>/gc_valid_thresh_ratio
Date:		September 2024
Contact:	"Daeho Jeong" <daehojeong@google.com>
Description:	It controls the valid block ratio threshold not to trigger excessive GC
		for zoned deivces. The initial value of it is 95(%). F2FS will stop the
		background GC thread from intiating GC for sections having valid blocks
		for zoned devices. The initial value of it is 95(%). F2FS will stop the
		background GC thread from initiating GC for sections having valid blocks
		exceeding the ratio.

What:		/sys/fs/f2fs/<disk>/max_read_extent_count
@@ -847,7 +847,7 @@ Description: For several zoned storage devices, vendors will provide extra space
		filesystem level GC. To do that, we can reserve the space using
		reserved_blocks. However, it is not enough, since this extra space should
		not be shown to users. So, with this new sysfs node, we can hide the space
		by substracting reserved_blocks from total bytes.
		by subtracting reserved_blocks from total bytes.

What:		/sys/fs/f2fs/<disk>/encoding_flags
Date:		April 2025
@@ -883,3 +883,53 @@ Date: June 2025
Contact:	"Daeho Jeong" <daehojeong@google.com>
Description:	Control GC algorithm for boost GC. 0: cost benefit, 1: greedy
		Default: 1

What:		/sys/fs/f2fs/<disk>/effective_lookup_mode
Date:		August 2025
Contact:	"Daniel Lee" <chullee@google.com>
Description:
		This is a read-only entry to show the effective directory lookup mode
		F2FS is currently using for casefolded directories.
		This considers both the "lookup_mode" mount option and the on-disk
		encoding flag, SB_ENC_NO_COMPAT_FALLBACK_FL.

		Possible values are:
		- "perf": Hash-only lookup.
		- "compat": Hash-based lookup with a linear search fallback enabled
		- "auto:perf": lookup_mode is auto and fallback is disabled on-disk
		- "auto:compat": lookup_mode is auto and fallback is enabled on-disk

What:		/sys/fs/f2fs/<disk>/bggc_io_aware
Date:		August 2025
Contact:	"Liao Yuanhong" <liaoyuanhong@vivo.com>
Description:	Used to adjust the BG_GC priority when pending IO, with a default value
		of 0. Specifically, for ZUFS, the default value is 1.

		==================  ======================================================
		value				description
		bggc_io_aware = 0   skip background GC if there is any kind of pending IO
		bggc_io_aware = 1   skip background GC if there is pending read IO
		bggc_io_aware = 2   don't aware IO for background GC
		==================  ======================================================

What:		/sys/fs/f2fs/<disk>/allocate_section_hint
Date:		August 2025
Contact:	"Liao Yuanhong" <liaoyuanhong@vivo.com>
Description:	Indicates the hint section between the first device and others in multi-devices
		setup. It defaults to the end of the first device in sections. For a single storage
		device, it defaults to the total number of sections. It can be manually set to match
		scenarios where multi-devices are mapped to the same dm device.

What:		/sys/fs/f2fs/<disk>/allocate_section_policy
Date:		August 2025
Contact:	"Liao Yuanhong" <liaoyuanhong@vivo.com>
Description:	Controls write priority in multi-devices setups. A value of 0 means normal writing.
		A value of 1 prioritizes writing to devices before the allocate_section_hint. A value of 2
		prioritizes writing to devices after the allocate_section_hint. The default is 0.

		===========================  ==========================================================
		value					     description
		allocate_section_policy = 0  Normal writing
		allocate_section_policy = 1  Prioritize writing to section before allocate_section_hint
		allocate_section_policy = 2  Prioritize writing to section after allocate_section_hint
		===========================  ==========================================================
+78 −44
Original line number Diff line number Diff line
.. SPDX-License-Identifier: GPL-2.0

==========================================
WHAT IS Flash-Friendly File System (F2FS)?
==========================================
=================================
Flash-Friendly File System (F2FS)
=================================

Overview
========

NAND flash memory-based storage devices, such as SSD, eMMC, and SD cards, have
been equipped on a variety systems ranging from mobile to server systems. Since
@@ -173,9 +176,12 @@ data_flush Enable data flushing before checkpoint in order to
			 persist data of regular and symlink.
reserve_root=%d		 Support configuring reserved space which is used for
			 allocation from a privileged user with specified uid or
			 gid, unit: 4KB, the default limit is 0.2% of user blocks.
resuid=%d		 The user ID which may use the reserved blocks.
resgid=%d		 The group ID which may use the reserved blocks.
			 gid, unit: 4KB, the default limit is 12.5% of user blocks.
reserve_node=%d		 Support configuring reserved nodes which are used for
			 allocation from a privileged user with specified uid or
			 gid, the default limit is 12.5% of all nodes.
resuid=%d		 The user ID which may use the reserved blocks and nodes.
resgid=%d		 The group ID which may use the reserved blocks and nodes.
fault_injection=%d	 Enable fault injection in all supported types with
			 specified injection rate.
fault_type=%d		 Support configuring fault injection type, should be
@@ -291,9 +297,13 @@ compress_algorithm=%s Control compress algorithm, currently f2fs supports "lzo"
			 "lz4", "zstd" and "lzo-rle" algorithm.
compress_algorithm=%s:%d Control compress algorithm and its compress level, now, only
			 "lz4" and "zstd" support compress level config.

                         =========      ===========
			 algorithm	level range
                         =========      ===========
			 lz4		3 - 16
			 zstd		1 - 22
                         =========      ===========
compress_log_size=%u	 Support configuring compress cluster size. The size will
			 be 4KB * (1 << %u). The default and minimum sizes are 16KB.
compress_extension=%s	 Support adding specified extension, so that f2fs can enable
@@ -357,6 +367,7 @@ errors=%s Specify f2fs behavior on critical errors. This supports modes:
			 panic immediately, continue without doing anything, and remount
			 the partition in read-only mode. By default it uses "continue"
			 mode.

			 ====================== =============== =============== ========
			 mode			continue	remount-ro	panic
			 ====================== =============== =============== ========
@@ -370,6 +381,25 @@ errors=%s Specify f2fs behavior on critical errors. This supports modes:
			 ====================== =============== =============== ========
nat_bits		 Enable nat_bits feature to enhance full/empty nat blocks access,
			 by default it's disabled.
lookup_mode=%s		 Control the directory lookup behavior for casefolded
			 directories. This option has no effect on directories
			 that do not have the casefold feature enabled.

			 ================== ========================================
			 Value		    Description
			 ================== ========================================
			 perf		    (Default) Enforces a hash-only lookup.
					    The linear search fallback is always
					    disabled, ignoring the on-disk flag.
			 compat		    Enables the linear search fallback for
					    compatibility with directory entries
					    created by older kernel that used a
					    different case-folding algorithm.
					    This mode ignores the on-disk flag.
			 auto		    F2FS determines the mode based on the
					    on-disk `SB_ENC_NO_COMPAT_FALLBACK_FL`
					    flag.
			 ================== ========================================
======================== ============================================================

Debugfs Entries
@@ -795,11 +825,13 @@ ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME
extension list        "                        "

-- buffered io
------------------------------------------------------------------
N/A                   COLD_DATA                WRITE_LIFE_EXTREME
N/A                   HOT_DATA                 WRITE_LIFE_SHORT
N/A                   WARM_DATA                WRITE_LIFE_NOT_SET

-- direct io
------------------------------------------------------------------
WRITE_LIFE_EXTREME    COLD_DATA                WRITE_LIFE_EXTREME
WRITE_LIFE_SHORT      HOT_DATA                 WRITE_LIFE_SHORT
WRITE_LIFE_NOT_SET    WARM_DATA                WRITE_LIFE_NOT_SET
@@ -915,21 +947,23 @@ compression enabled files (refer to "Compression implementation" section for how
enable compression on a regular inode).

1) compress_mode=fs

   This is the default option. f2fs does automatic compression in the writeback of the
   compression enabled files.

2) compress_mode=user

   This disables the automatic compression and gives the user discretion of choosing the
   target file and the timing. The user can do manual compression/decompression on the
   compression enabled files using F2FS_IOC_DECOMPRESS_FILE and F2FS_IOC_COMPRESS_FILE
   ioctls like the below.

To decompress a file,
To decompress a file::

  fd = open(filename, O_WRONLY, 0);
  ret = ioctl(fd, F2FS_IOC_DECOMPRESS_FILE);

To compress a file,
To compress a file::

  fd = open(filename, O_WRONLY, 0);
  ret = ioctl(fd, F2FS_IOC_COMPRESS_FILE);
@@ -962,7 +996,7 @@ reserved and used by another filesystem or for different purposes. Once that
external usage is complete, the device aliasing file can be deleted, releasing
the reserved space back to F2FS for its own use.

<use-case>
.. code-block::

   # ls /dev/vd*
   /dev/vdb (32GB) /dev/vdc (32GB)
+53 −0
Original line number Diff line number Diff line
@@ -1442,6 +1442,34 @@ u64 f2fs_get_sectors_written(struct f2fs_sb_info *sbi)
	return get_sectors_written(sbi->sb->s_bdev);
}

static inline void stat_cp_time(struct cp_control *cpc, enum cp_time type)
{
	cpc->stats.times[type] = ktime_get();
}

static inline void check_cp_time(struct f2fs_sb_info *sbi, struct cp_control *cpc)
{
	unsigned long long sb_diff, cur_diff;
	enum cp_time ct;

	sb_diff = (u64)ktime_ms_delta(sbi->cp_stats.times[CP_TIME_END],
					sbi->cp_stats.times[CP_TIME_START]);
	cur_diff = (u64)ktime_ms_delta(cpc->stats.times[CP_TIME_END],
					cpc->stats.times[CP_TIME_START]);

	if (cur_diff > sb_diff) {
		sbi->cp_stats = cpc->stats;
		if (cur_diff < CP_LONG_LATENCY_THRESHOLD)
			return;

		f2fs_warn(sbi, "checkpoint was blocked for %llu ms", cur_diff);
		for (ct = CP_TIME_START; ct < CP_TIME_MAX - 1; ct++)
			f2fs_warn(sbi, "Step#%d: %llu ms", ct,
				(u64)ktime_ms_delta(cpc->stats.times[ct + 1],
						cpc->stats.times[ct]));
	}
}

static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
{
	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
@@ -1459,6 +1487,8 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
	/* Flush all the NAT/SIT pages */
	f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO);

	stat_cp_time(cpc, CP_TIME_SYNC_META);

	/* start to update checkpoint, cp ver is already updated previously */
	ckpt->elapsed_time = cpu_to_le64(get_mtime(sbi, true));
	ckpt->free_segment_count = cpu_to_le32(free_segments(sbi));
@@ -1555,20 +1585,26 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)

	/* Here, we have one bio having CP pack except cp pack 2 page */
	f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO);
	stat_cp_time(cpc, CP_TIME_SYNC_CP_META);

	/* Wait for all dirty meta pages to be submitted for IO */
	f2fs_wait_on_all_pages(sbi, F2FS_DIRTY_META);
	stat_cp_time(cpc, CP_TIME_WAIT_DIRTY_META);

	/* wait for previous submitted meta pages writeback */
	f2fs_wait_on_all_pages(sbi, F2FS_WB_CP_DATA);
	stat_cp_time(cpc, CP_TIME_WAIT_CP_DATA);

	/* flush all device cache */
	err = f2fs_flush_device_cache(sbi);
	if (err)
		return err;
	stat_cp_time(cpc, CP_TIME_FLUSH_DEVICE);

	/* barrier and flush checkpoint cp pack 2 page if it can */
	commit_checkpoint(sbi, ckpt, start_blk);
	f2fs_wait_on_all_pages(sbi, F2FS_WB_CP_DATA);
	stat_cp_time(cpc, CP_TIME_WAIT_LAST_CP);

	/*
	 * invalidate intermediate page cache borrowed from meta inode which are
@@ -1613,6 +1649,8 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
	unsigned long long ckpt_ver;
	int err = 0;

	stat_cp_time(cpc, CP_TIME_START);

	if (f2fs_readonly(sbi->sb) || f2fs_hw_is_readonly(sbi))
		return -EROFS;

@@ -1624,6 +1662,8 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
	if (cpc->reason != CP_RESIZE)
		f2fs_down_write(&sbi->cp_global_sem);

	stat_cp_time(cpc, CP_TIME_LOCK);

	if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
		((cpc->reason & CP_FASTBOOT) || (cpc->reason & CP_SYNC) ||
		((cpc->reason & CP_DISCARD) && !sbi->discard_blks)))
@@ -1639,6 +1679,8 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
	if (err)
		goto out;

	stat_cp_time(cpc, CP_TIME_OP_LOCK);

	trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops");

	f2fs_flush_merged_writes(sbi);
@@ -1678,6 +1720,8 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)

	f2fs_flush_sit_entries(sbi, cpc);

	stat_cp_time(cpc, CP_TIME_FLUSH_META);

	/* save inmem log status */
	f2fs_save_inmem_curseg(sbi);

@@ -1695,6 +1739,8 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
	stat_inc_cp_count(sbi);
stop:
	unblock_operations(sbi);
	stat_cp_time(cpc, CP_TIME_END);
	check_cp_time(sbi, cpc);

	if (cpc->reason & CP_RECOVERY)
		f2fs_notice(sbi, "checkpoint: version = %llx", ckpt_ver);
@@ -1778,6 +1824,7 @@ static void __checkpoint_and_complete_reqs(struct f2fs_sb_info *sbi)
	llist_for_each_entry_safe(req, next, dispatch_list, llnode) {
		diff = (u64)ktime_ms_delta(ktime_get(), req->queue_time);
		req->ret = ret;
		req->delta_time = diff;
		complete(&req->wait);

		sum_diff += diff;
@@ -1873,6 +1920,12 @@ int f2fs_issue_checkpoint(struct f2fs_sb_info *sbi)
	else
		flush_remained_ckpt_reqs(sbi, &req);

	if (unlikely(req.delta_time >= CP_LONG_LATENCY_THRESHOLD)) {
		f2fs_warn_ratelimited(sbi,
			"blocked on checkpoint for %u ms", cprc->peak_time);
		dump_stack();
	}

	return req.ret;
}

+24 −19
Original line number Diff line number Diff line
@@ -1215,9 +1215,11 @@ int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock)
{
	void *fsdata = NULL;
	struct page *pagep;
	struct page **rpages;
	int log_cluster_size = F2FS_I(inode)->i_log_cluster_size;
	pgoff_t start_idx = from >> (PAGE_SHIFT + log_cluster_size) <<
							log_cluster_size;
	int i;
	int err;

	err = f2fs_is_compressed_cluster(inode, start_idx);
@@ -1238,27 +1240,30 @@ int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock)
	if (err <= 0)
		return err;

	if (err > 0) {
		struct page **rpages = fsdata;
		int cluster_size = F2FS_I(inode)->i_cluster_size;
		int i;
	rpages = fsdata;

		for (i = cluster_size - 1; i >= 0; i--) {
	for (i = (1 << log_cluster_size) - 1; i >= 0; i--) {
		struct folio *folio = page_folio(rpages[i]);
			loff_t start = folio->index << PAGE_SHIFT;
		loff_t start = (loff_t)folio->index << PAGE_SHIFT;
		loff_t offset = from > start ? from - start : 0;

			if (from <= start) {
				folio_zero_segment(folio, 0, folio_size(folio));
			} else {
				folio_zero_segment(folio, from - start,
						folio_size(folio));
		folio_zero_segment(folio, offset, folio_size(folio));

		if (from >= start)
			break;
	}
		}

	f2fs_compress_write_end(inode, fsdata, start_idx, true);
	}
	return 0;

	err = filemap_write_and_wait_range(inode->i_mapping,
			round_down(from, 1 << log_cluster_size << PAGE_SHIFT),
			LLONG_MAX);
	if (err)
		return err;

	truncate_pagecache(inode, from);

	return f2fs_do_truncate_blocks(inode, round_up(from, PAGE_SIZE), lock);
}

static int f2fs_write_compressed_pages(struct compress_ctx *cc,
+20 −39
Original line number Diff line number Diff line
@@ -733,9 +733,11 @@ static bool page_is_mergeable(struct f2fs_sb_info *sbi, struct bio *bio,
static bool io_type_is_mergeable(struct f2fs_bio_info *io,
						struct f2fs_io_info *fio)
{
	blk_opf_t mask = ~(REQ_PREFLUSH | REQ_FUA);

	if (io->fio.op != fio->op)
		return false;
	return io->fio.op_flags == fio->op_flags;
	return (io->fio.op_flags & mask) == (fio->op_flags & mask);
}

static bool io_is_mergeable(struct f2fs_sb_info *sbi, struct bio *bio,
@@ -911,7 +913,7 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio)
	if (fio->io_wbc)
		wbc_account_cgroup_owner(fio->io_wbc, folio, folio_size(folio));

	inc_page_count(fio->sbi, WB_DATA_TYPE(data_folio, false));
	inc_page_count(fio->sbi, WB_DATA_TYPE(folio, false));

	*fio->last_block = fio->new_blkaddr;
	*fio->bio = bio;
@@ -1083,7 +1085,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
}

/* This can handle encryption stuffs */
static int f2fs_submit_page_read(struct inode *inode, struct folio *folio,
static void f2fs_submit_page_read(struct inode *inode, struct folio *folio,
				 block_t blkaddr, blk_opf_t op_flags,
				 bool for_write)
{
@@ -1092,23 +1094,16 @@ static int f2fs_submit_page_read(struct inode *inode, struct folio *folio,

	bio = f2fs_grab_read_bio(inode, blkaddr, 1, op_flags,
					folio->index, for_write);
	if (IS_ERR(bio))
		return PTR_ERR(bio);

	/* wait for GCed page writeback via META_MAPPING */
	f2fs_wait_on_block_writeback(inode, blkaddr);

	if (!bio_add_folio(bio, folio, PAGE_SIZE, 0)) {
		iostat_update_and_unbind_ctx(bio);
		if (bio->bi_private)
			mempool_free(bio->bi_private, bio_post_read_ctx_pool);
		bio_put(bio);
		return -EFAULT;
	}
	if (!bio_add_folio(bio, folio, PAGE_SIZE, 0))
		f2fs_bug_on(sbi, 1);

	inc_page_count(sbi, F2FS_RD_DATA);
	f2fs_update_iostat(sbi, NULL, FS_DATA_READ_IO, F2FS_BLKSIZE);
	f2fs_submit_read_bio(sbi, bio, DATA);
	return 0;
}

static void __set_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr)
@@ -1265,10 +1260,8 @@ struct folio *f2fs_get_read_data_folio(struct inode *inode, pgoff_t index,
		return folio;
	}

	err = f2fs_submit_page_read(inode, folio, dn.data_blkaddr,
	f2fs_submit_page_read(inode, folio, dn.data_blkaddr,
						op_flags, for_write);
	if (err)
		goto put_err;
	return folio;

put_err:
@@ -1572,6 +1565,9 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
	pgofs =	(pgoff_t)map->m_lblk;
	end = pgofs + maxblocks;

	if (flag == F2FS_GET_BLOCK_PRECACHE)
		mode = LOOKUP_NODE_RA;

next_dnode:
	if (map->m_may_create) {
		if (f2fs_lfs_mode(sbi))
@@ -1778,12 +1774,13 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
		if (map->m_flags & F2FS_MAP_MAPPED) {
			unsigned int ofs = start_pgofs - map->m_lblk;

			if (map->m_len > ofs)
				f2fs_update_read_extent_cache_range(&dn,
					start_pgofs, map->m_pblk + ofs,
					map->m_len - ofs);
		}
		if (map->m_next_extent)
			*map->m_next_extent = pgofs + 1;
			*map->m_next_extent = is_hole ? pgofs + 1 : pgofs;
	}
	f2fs_put_dnode(&dn);
unlock_out:
@@ -2145,16 +2142,10 @@ static int f2fs_read_single_page(struct inode *inode, struct folio *folio,
		f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA);
		bio = NULL;
	}
	if (bio == NULL) {
	if (bio == NULL)
		bio = f2fs_grab_read_bio(inode, block_nr, nr_pages,
				f2fs_ra_op_flags(rac), index,
				false);
		if (IS_ERR(bio)) {
			ret = PTR_ERR(bio);
			bio = NULL;
			goto out;
		}
	}

	/*
	 * If the page is under writeback, we need to wait for
@@ -2303,18 +2294,10 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
			bio = NULL;
		}

		if (!bio) {
		if (!bio)
			bio = f2fs_grab_read_bio(inode, blkaddr, nr_pages - i,
					f2fs_ra_op_flags(rac),
					folio->index, for_write);
			if (IS_ERR(bio)) {
				ret = PTR_ERR(bio);
				f2fs_decompress_end_io(dic, ret, true);
				f2fs_put_dnode(&dn);
				*bio_ret = NULL;
				return ret;
			}
		}

		if (!bio_add_folio(bio, folio, blocksize, 0))
			goto submit_and_realloc;
@@ -3639,11 +3622,9 @@ static int f2fs_write_begin(const struct kiocb *iocb,
			err = -EFSCORRUPTED;
			goto put_folio;
		}
		err = f2fs_submit_page_read(use_cow ?
		f2fs_submit_page_read(use_cow ?
				F2FS_I(inode)->cow_inode : inode,
				folio, blkaddr, 0, true);
		if (err)
			goto put_folio;

		folio_lock(folio);
		if (unlikely(folio->mapping != mapping)) {
Loading