From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [220.197.31.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B92C63B841D for ; Wed, 26 Aug 2026 08:27:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.6 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732856; cv=none; b=KN8hFyo0OqG2+VCOswguY0FN57y42NCfMErp1faRAbezm1P9FaZXGcBkem0BVWS+6UfBWEEKCA0kY4mosrGV32ZXhC0zgQaiNFYyFdxT8fmMbcUd1aEhUAPJ58FeiKJ4lnJW8cbvFRnBG3EdDNO3i2jw78aAy2Q8Kpe7+RjWmcM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732856; c=relaxed/simple; bh=t5od9Yc5i3Fw1HdQCXnCIDbw560tuhX+mnH/WpbiBIQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TAsKF2TscV49PnV0Jo/CsuncSqLFIpn4wN23wobFe1tLMGExW+lIuuugsIL3dMmtaH0npc+acy0iHM/w4oVDMxccXs9uJitkQ0Z4FR5Kky0OqbBF+kPzW6YXY6Lj1EAer46mRkTP7BO08mjeLbsIYeRR+aeyDNMC0B9NbciPj14= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=St5FoTsx; arc=none smtp.client-ip=220.197.31.6 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="St5FoTsx" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=sB 6LWXZ+VBUkvgJIuzNg7cADEKZxEoT9cqS1MKodOLg=; b=St5FoTsxEHI50hU61Z WAC/X95U0bH/xQWsjWHber1EqkfUzSXOWBG5PhqTjrIvMwCHBhU/3odQkNWul6+T J13eGP/YqdiFL/xUxDUd5E3DQhcYjmeGJ0RpjCHDnDJTZZaLl1JiWs1Iuoy/kYTS 5ku5RXH9MbVyw10x7lXqPrVso= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S3; Wed, 26 Aug 2026 16:26:43 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 01/14] f2fs: extend folio state for large folio write path Date: Wed, 26 Aug 2026 16:26:28 +0800 Message-ID: <20260826082641.2007658-2-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S3 X-Coremail-Antispam: 1Uf129KBjvJXoW3tr1rXF13ZryDtw13Wr45KFg_yoWkAFWxpF Z5Gr1Dtr4rJryUWF1xJF1rZr1Sk347Xa1UAFZ7G34fJ3Waq3Z5WF1kJ34jvF43Jry8ZFna vayYkr15WF4YyF7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jj6wZUUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBowM3B2qOo0P3dAAA3X Content-Type: text/plain; charset="utf-8" Large folio write path needs a subpage status bitmap and write pages pending counter, while keeping compatible with f2fs private flags. Move struct f2fs_folio_state to f2fs.h, add private_flags and subpage state bitmap, and change PAGE_PRIVATE functions to be compatible with f2fs_folio_state. Allocate f2fs_folio_state via kzalloc instead of kmem_cache, since the state size depends on the folio order. Note: Now if a path wants to use f2fs_folio_state, it must call `folio_has_ffs` instead of `folio_test_large`` to make check. Signed-off-by: Nanzhe Zhao --- fs/f2fs/compress.c | 2 + fs/f2fs/data.c | 60 ++++++++++++++++-------------- fs/f2fs/f2fs.h | 92 ++++++++++++++++++++++++++++++++++++---------- fs/f2fs/segment.c | 2 +- 4 files changed, 108 insertions(+), 48 deletions(-) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 91855d91bbdd..84da3e39efb4 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -78,6 +78,8 @@ bool f2fs_is_compressed_page(struct folio *folio) return false; if (folio_test_f2fs_nonpointer(folio)) return false; + if (f2fs_folio_has_ffs(folio)) + return false; =20 f2fs_bug_on(F2FS_F_SB(folio), *((u32 *)folio->private) !=3D F2FS_COMPRESSED_PAGE_MAGIC); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 6ae0eb37d20f..578a90d427e2 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -32,20 +32,13 @@ =20 static struct kmem_cache *bio_post_read_ctx_cache; static struct kmem_cache *bio_entry_slab; -static struct kmem_cache *ffs_entry_slab; static mempool_t *bio_post_read_ctx_pool; static struct bio_set f2fs_bioset; =20 -struct f2fs_folio_state { - spinlock_t state_lock; - unsigned int read_pages_pending; -}; - struct f2fs_bio { struct work_struct work; struct bio bio; }; - #define F2FS_BIO_POOL_SIZE NR_CURSEG_TYPE =20 int __init f2fs_init_bioset(void) @@ -133,6 +126,9 @@ struct bio_post_read_ctx { block_t fs_blkaddr; }; =20 +static bool __ffs_mark_subrange_uptodate(struct folio *folio, + struct f2fs_folio_state *ffs, size_t offset, size_t len); + /* * Update and unlock a bio's pages, and free the bio. * @@ -155,7 +151,7 @@ static void f2fs_finish_read_bio(struct bio *bio, bool = in_task) =20 bio_for_each_folio_all(fi, bio) { struct folio *folio =3D fi.folio; - unsigned nr_pages =3D fi.length >> PAGE_SHIFT; + unsigned int nr_pages =3D fi.length >> PAGE_SHIFT; bool finished =3D true; =20 if (!folio_test_large(folio) && @@ -360,6 +356,7 @@ static void f2fs_write_end_bio(struct bio *bio) =20 bio_for_each_folio_all(fi, bio) { struct folio *folio =3D fi.folio; + unsigned int nr_pages =3D fi.length >> PAGE_SHIFT; enum count_type type; =20 if (fscrypt_is_bounce_folio(folio)) { @@ -2516,17 +2513,32 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, = struct bio **bio_ret, } #endif =20 -static struct f2fs_folio_state *ffs_find_or_alloc(struct folio *folio) +struct f2fs_folio_state *f2fs_ffs_find_or_alloc(struct folio *folio) { - struct f2fs_folio_state *ffs =3D folio->private; + struct f2fs_folio_state *ffs; + unsigned int nr_subpages =3D folio_nr_pages(folio); + unsigned long private_flags =3D 0; + + f2fs_bug_on(F2FS_F_SB(folio), !folio_test_large(folio)); =20 - if (ffs) - return ffs; + if (f2fs_folio_has_ffs(folio)) + return (struct f2fs_folio_state *)folio->private; =20 - ffs =3D f2fs_kmem_cache_alloc(ffs_entry_slab, - GFP_NOIO | __GFP_ZERO, true, NULL); + if (folio_test_private(folio) && folio_test_f2fs_nonpointer(folio)) + private_flags =3D (unsigned long)folio->private; + + ffs =3D kzalloc(struct_size(ffs, state, BITS_TO_LONGS(2 * nr_subpages)), + GFP_NOIO | __GFP_NOFAIL); =20 spin_lock_init(&ffs->state_lock); + ffs->private_flags =3D private_flags; + if (folio_test_uptodate(folio)) + bitmap_set(ffs->state, 0, nr_subpages); + if (folio_test_dirty(folio)) + bitmap_set(ffs->state, nr_subpages, nr_subpages); + + if (folio_test_private(folio)) + folio_detach_private(folio); folio_attach_private(folio, ffs); return ffs; } @@ -2535,7 +2547,7 @@ static void ffs_detach_free(struct folio *folio) { struct f2fs_folio_state *ffs; =20 - if (!folio_test_large(folio)) { + if (!f2fs_folio_has_ffs(folio)) { folio_detach_private(folio); return; } @@ -2545,7 +2557,8 @@ static void ffs_detach_free(struct folio *folio) return; =20 WARN_ON_ONCE(ffs->read_pages_pending !=3D 0); - kmem_cache_free(ffs_entry_slab, ffs); + WARN_ON_ONCE(atomic_read(&ffs->write_pages_pending)); + kfree(ffs); } =20 static int f2fs_read_data_large_folio(struct inode *inode, @@ -2558,7 +2571,7 @@ static int f2fs_read_data_large_folio(struct inode *i= node, pgoff_t index, offset, next_pgofs =3D 0; unsigned max_nr_pages =3D rac ? readahead_count(rac) : folio_nr_pages(folio); - unsigned nrpages; + unsigned int nrpages, len_blks; struct f2fs_folio_state *ffs; int ret =3D 0; bool folio_in_bio =3D false; @@ -2634,7 +2647,7 @@ static int f2fs_read_data_large_folio(struct inode *i= node, * to prevent from premature folio_end_read() call on folio */ if (folio_test_large(folio)) { - ffs =3D ffs_find_or_alloc(folio); + ffs =3D f2fs_ffs_find_or_alloc(folio); =20 /* set the bitmap to wait */ spin_lock_irq(&ffs->state_lock); @@ -2987,7 +3000,7 @@ bool f2fs_should_update_outplace(struct inode *inode,= struct f2fs_io_info *fio) return true; =20 if (fio) { - if (page_private_gcing(fio->page)) + if (folio_test_f2fs_gcing(fio->folio)) return true; if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) && f2fs_is_checkpointed_data(sbi, fio->old_blkaddr))) @@ -4586,21 +4599,12 @@ int __init f2fs_init_bio_entry_cache(void) if (!bio_entry_slab) return -ENOMEM; =20 - ffs_entry_slab =3D f2fs_kmem_cache_create("f2fs_ffs_slab", - sizeof(struct f2fs_folio_state)); - - if (!ffs_entry_slab) { - kmem_cache_destroy(bio_entry_slab); - return -ENOMEM; - } - return 0; } =20 void f2fs_destroy_bio_entry_cache(void) { kmem_cache_destroy(bio_entry_slab); - kmem_cache_destroy(ffs_entry_slab); } =20 static int f2fs_iomap_begin(struct inode *inode, loff_t offset, loff_t len= gth, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 16720f1f0a9c..2e8f85cea6d0 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1628,6 +1628,18 @@ static inline void f2fs_clear_bit(unsigned int nr, c= har *addr); * Layout B: lowest bit should be 0 * page.private is a wrapped pointer. */ + +struct f2fs_folio_state { + spinlock_t state_lock; + unsigned int read_pages_pending; + atomic_t write_pages_pending; + unsigned long private_flags; + /* state[0..nr_subpages - 1] tracks uptodate subpages. + * state[nr_subpages..2 * nr_subpages - 1] tracks dirty subpages. + */ + unsigned long state[]; +}; + enum { PAGE_PRIVATE_NOT_POINTER, /* private contains non-pointer data */ PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating= */ @@ -1637,6 +1649,14 @@ enum { PAGE_PRIVATE_MAX }; =20 +static inline bool f2fs_folio_has_ffs(const struct folio *folio) +{ + unsigned long private =3D (unsigned long)folio->private; + + return folio_test_large(folio) && private && + !(private & BIT(PAGE_PRIVATE_NOT_POINTER)); +} + /* For compression */ enum compress_algorithm_type { COMPRESS_LZO, @@ -2682,10 +2702,57 @@ static inline int inc_valid_block_count(struct f2fs= _sb_info *sbi, return -ENOSPC; } =20 +static inline unsigned long f2fs_folio_get_private_flags(const struct foli= o *folio) +{ + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D folio->private; + + return ffs->private_flags; + } + + return (unsigned long)folio->private; +} + +static inline void f2fs_folio_set_private_flags(struct folio *folio, + unsigned long flags) +{ + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D folio->private; + + ffs->private_flags |=3D flags; + return; + } + + if (!folio_test_private(folio)) + folio_attach_private(folio, (void *)flags); + else + folio->private =3D (void *)((unsigned long)folio->private | flags); +} + +static inline void f2fs_folio_clear_private_flags(struct folio *folio, + unsigned long flags) +{ + unsigned long private; + + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D folio->private; + + ffs->private_flags &=3D ~flags; + return; + } + + private =3D (unsigned long)folio->private; + private &=3D ~flags; + if (private =3D=3D BIT(PAGE_PRIVATE_NOT_POINTER)) + folio_detach_private(folio); + else + folio->private =3D (void *)private; +} + #define PAGE_PRIVATE_GET_FUNC(name, flagname) \ static inline bool folio_test_f2fs_##name(const struct folio *folio) \ { \ - unsigned long priv =3D (unsigned long)folio->private; \ + unsigned long priv =3D f2fs_folio_get_private_flags(folio); \ unsigned long v =3D (1UL << PAGE_PRIVATE_NOT_POINTER) | \ (1UL << PAGE_PRIVATE_##flagname); \ return (priv & v) =3D=3D v; \ @@ -2702,12 +2769,7 @@ static inline void folio_set_f2fs_##name(struct foli= o *folio) \ { \ unsigned long v =3D (1UL << PAGE_PRIVATE_NOT_POINTER) | \ (1UL << PAGE_PRIVATE_##flagname); \ - if (!folio->private) \ - folio_attach_private(folio, (void *)v); \ - else { \ - v |=3D (unsigned long)folio->private; \ - folio->private =3D (void *)v; \ - } \ + f2fs_folio_set_private_flags(folio, v); \ } \ static inline void set_page_private_##name(struct page *page) \ { \ @@ -2720,13 +2782,8 @@ static inline void set_page_private_##name(struct pa= ge *page) \ #define PAGE_PRIVATE_CLEAR_FUNC(name, flagname) \ static inline void folio_clear_f2fs_##name(struct folio *folio) \ { \ - unsigned long v =3D (unsigned long)folio->private; \ - \ - v &=3D ~(1UL << PAGE_PRIVATE_##flagname); \ - if (v =3D=3D (1UL << PAGE_PRIVATE_NOT_POINTER)) \ - folio_detach_private(folio); \ - else \ - folio->private =3D (void *)v; \ + f2fs_folio_clear_private_flags(folio, \ + 1UL << PAGE_PRIVATE_##flagname); \ } \ static inline void clear_page_private_##name(struct page *page) \ { \ @@ -2752,7 +2809,7 @@ PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE); =20 static inline unsigned long folio_get_f2fs_data(struct folio *folio) { - unsigned long data =3D (unsigned long)folio->private; + unsigned long data =3D f2fs_folio_get_private_flags(folio); =20 if (!test_bit(PAGE_PRIVATE_NOT_POINTER, &data)) return 0; @@ -2763,10 +2820,7 @@ static inline void folio_set_f2fs_data(struct folio = *folio, unsigned long data) { data =3D (1UL << PAGE_PRIVATE_NOT_POINTER) | (data << PAGE_PRIVATE_MAX); =20 - if (!folio_test_private(folio)) - folio_attach_private(folio, (void *)data); - else - folio->private =3D (void *)((unsigned long)folio->private | data); + f2fs_folio_set_private_flags(folio, data); } =20 static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ed6f2947210b..df10119d94ad 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3803,7 +3803,7 @@ static int __get_segment_type_6(struct f2fs_io_info *= fio) if (is_inode_flag_set(inode, FI_ALIGNED_WRITE)) return CURSEG_COLD_DATA_PINNED; =20 - if (page_private_gcing(fio->page)) { + if (folio_test_f2fs_gcing(fio->folio)) { if (fio->sbi->am.atgc_enabled && (fio->io_type =3D=3D FS_DATA_IO) && (fio->sbi->gc_mode !=3D GC_URGENT_HIGH) && --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [220.197.31.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A9D9B3BB124 for ; Wed, 26 Aug 2026 08:27:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.6 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732857; cv=none; b=d7wLPWvIIoG5aDniqodnqXA/jOMcCG9ZQjHk1KuGb70cSy2KAGygqX0xXfOscLIPKgpwAqmVbHw5nn2v2X/kQOpcqxX7NtiuefVG2hswC8/nasIA1YFAVigyXYOM8oSv7b/aLN5KIlDuZ8kE9Y6nHSZqboISnNFJShnYgnEB01s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732857; c=relaxed/simple; bh=QIevWWtPXwtmRVvaykbukCXHJDcNnRUVC4bpONw28sY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ipSGgbai0H+ok8SHGAioFX11N8z0bkJkkPCpaVdKq9nY3ghvwXbadqjCEXQQMPLDP2Wn3siOd0UeL7NjOtA8404RhQ2OyVlpSuY45wtkBw8id0qDPqsXTKJJHOkc2nCYfg3kSohYQJlGz0cEQiUA8qyZbXWe4N2420jN31f/Rng= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=ikBitTiQ; arc=none smtp.client-ip=220.197.31.6 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="ikBitTiQ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=1b b3LT9hd7rV2qBMVWcJYbeCWsPtuVYax84wsQl9zg8=; b=ikBitTiQPgWPEzS/Qa c321PmD4CVRYdrdrheASsjnyEHHAE6/M6HSnk6zuabGXNNQJW4fX8yYR9mIyTn8z vYWGScFNz6lRFFYKLBRWStSqwcrHEzNKrJAW4j186c70l+bav7m4cStj8+YWNepK cmXGPhwrf9JN/xwTQAV5E0FFs= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S4; Wed, 26 Aug 2026 16:26:44 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 02/14] f2fs: carry subpage offset and count in write IO Date: Wed, 26 Aug 2026 16:26:29 +0800 Message-ID: <20260826082641.2007658-3-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S4 X-Coremail-Antispam: 1Uf129KBjvJXoW3GrWUXFyrCFWrXFW8Ww4DArb_yoWfXw1rpF 15Can5GrWfJ3WfWwn2qFn5XF1fK347Wr18GFZ3Ca4S9a1jqwnagF1fZw18AFy3Kr4DCFWk XF1FkF15WF1UJF7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07j5Z23UUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBsAQ4CGqOo0Rd3gAA3E Content-Type: text/plain; charset="utf-8" Large folio write paths need to submit I/O for a range inside a folio instead of always submitting the whole folio from offset zero. Add folio_offset and folio_blkcnt to f2fs_io_info to describe the block offset inside the folio and the number of contiguous blocks covered by the I/O. Apply the new fields to the bio submit paths that need the subpage offset or contiguous block count. Signed-off-by: Nanzhe Zhao --- fs/f2fs/data.c | 59 ++++++++++++++++++++++++++++++++--------------- fs/f2fs/f2fs.h | 11 +++++++++ fs/f2fs/segment.c | 3 ++- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 578a90d427e2..8649d443ca74 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -783,6 +783,9 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) struct folio *fio_folio =3D fio->folio; struct folio *data_folio =3D fio->encrypted_page ? page_folio(fio->encrypted_page) : fio_folio; + pgoff_t fio_lblk =3D F2FS_FIO_LBLK(fio_folio, fio); + size_t bio_offset =3D F2FS_FIO_BIO_OFFSET(fio); + size_t bio_len =3D F2FS_FIO_BIO_SIZE(fio); =20 if (!f2fs_is_valid_blkaddr(fio->sbi, fio->new_blkaddr, fio->is_por ? META_POR : (__is_meta_io(fio) ? @@ -795,11 +798,11 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) bio =3D __bio_alloc(fio, 1); =20 f2fs_set_bio_crypt_ctx(bio, fio_folio->mapping->host, - fio_folio->index, fio, GFP_NOIO); - bio_add_folio_nofail(bio, data_folio, folio_size(data_folio), 0); + fio_lblk, fio, GFP_NOIO); + bio_add_folio_nofail(bio, data_folio, bio_len, bio_offset); =20 if (fio->io_wbc && !is_read_io(fio->op)) - wbc_account_cgroup_owner(fio->io_wbc, fio_folio, PAGE_SIZE); + wbc_account_cgroup_owner(fio->io_wbc, fio_folio, bio_len); =20 inc_page_count(fio->sbi, is_read_io(fio->op) ? __read_io_type(data_folio) : WB_DATA_TYPE(fio->folio, false)); @@ -844,7 +847,8 @@ static bool io_is_mergeable(struct f2fs_sb_info *sbi, s= truct bio *bio, } =20 static void add_bio_entry(struct f2fs_sb_info *sbi, struct bio *bio, - struct folio *folio, enum temp_type temp) + struct folio *folio, size_t len, size_t offset, + enum temp_type temp) { struct f2fs_bio_info *io =3D sbi->write_io[DATA] + temp; struct bio_entry *be; @@ -853,7 +857,7 @@ static void add_bio_entry(struct f2fs_sb_info *sbi, str= uct bio *bio, be->bio =3D bio; bio_get(bio); =20 - bio_add_folio_nofail(bio, folio, folio_size(folio), 0); + bio_add_folio_nofail(bio, folio, len, offset); =20 f2fs_down_write(&io->bio_list_lock); list_add_tail(&be->list, &io->bio_list); @@ -870,6 +874,9 @@ static int add_ipu_page(struct f2fs_io_info *fio, struc= t bio **bio, struct folio *folio) { struct folio *fio_folio =3D fio->folio; + pgoff_t fio_lblk =3D F2FS_FIO_LBLK(fio_folio, fio); + size_t bio_offset =3D F2FS_FIO_BIO_OFFSET(fio); + size_t bio_len =3D F2FS_FIO_BIO_SIZE(fio); struct f2fs_sb_info *sbi =3D fio->sbi; enum temp_type temp; bool found =3D false; @@ -892,8 +899,8 @@ static int add_ipu_page(struct f2fs_io_info *fio, struc= t bio **bio, fio->new_blkaddr)); if (f2fs_crypt_mergeable_bio(*bio, fio_folio->mapping->host, - fio_folio->index, fio) && - bio_add_folio(*bio, folio, folio_size(folio), 0)) { + fio_lblk, fio) && + bio_add_folio(*bio, folio, bio_len, bio_offset)) { ret =3D 0; break; } @@ -1007,6 +1014,10 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio) struct folio *data_folio =3D fio->encrypted_page ? page_folio(fio->encrypted_page) : fio->folio; struct folio *folio =3D fio->folio; + pgoff_t fio_lblk =3D F2FS_FIO_LBLK(folio, fio); + unsigned int fio_cnt =3D F2FS_FIO_BLKCNT(fio); + size_t bio_offset =3D F2FS_FIO_BIO_OFFSET(fio); + size_t bio_len =3D F2FS_FIO_BIO_SIZE(fio); =20 if (!f2fs_is_valid_blkaddr(fio->sbi, fio->new_blkaddr, __is_meta_io(fio) ? META_GENERIC : DATA_GENERIC)) @@ -1021,9 +1032,10 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio) if (!bio) { bio =3D __bio_alloc(fio, BIO_MAX_VECS); f2fs_set_bio_crypt_ctx(bio, folio->mapping->host, - folio->index, fio, GFP_NOIO); + fio_lblk, fio, GFP_NOIO); =20 - add_bio_entry(fio->sbi, bio, data_folio, fio->temp); + add_bio_entry(fio->sbi, bio, data_folio, bio_len, + bio_offset, fio->temp); } else { if (add_ipu_page(fio, &bio, data_folio)) goto alloc_new; @@ -1034,7 +1046,7 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio) =20 inc_page_count(fio->sbi, WB_DATA_TYPE(folio, false)); =20 - *fio->last_block =3D fio->new_blkaddr; + *fio->last_block =3D fio->new_blkaddr + fio_cnt - 1; *fio->bio =3D bio; =20 return 0; @@ -1070,6 +1082,10 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio) struct folio *bio_folio; struct f2fs_lock_context lc; enum count_type type; + pgoff_t fio_lblk; + unsigned int fio_cnt; + size_t bio_offset; + size_t bio_len; =20 f2fs_bug_on(sbi, is_read_io(fio->op)); =20 @@ -1108,6 +1124,9 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio) /* set submitted =3D true as a return value */ fio->submitted =3D 1; =20 + fio_lblk =3D F2FS_FIO_LBLK(fio->folio, fio); + fio_cnt =3D F2FS_FIO_BLKCNT(fio); + type =3D WB_DATA_TYPE(bio_folio, fio->compressed_page); inc_page_count(sbi, type); =20 @@ -1115,26 +1134,28 @@ void f2fs_submit_page_write(struct f2fs_io_info *fi= o) (!io_is_mergeable(sbi, io->bio, io, fio, io->last_block_in_bio, fio->new_blkaddr) || !f2fs_crypt_mergeable_bio(io->bio, fio_inode(fio), - bio_folio->index, fio))) + fio_lblk, fio))) __submit_merged_bio(io); alloc_new: if (io->bio =3D=3D NULL) { io->bio =3D __bio_alloc(fio, BIO_MAX_VECS); f2fs_set_bio_crypt_ctx(io->bio, fio_inode(fio), - bio_folio->index, fio, GFP_NOIO); + fio_lblk, fio, GFP_NOIO); io->fio =3D *fio; } =20 - if (!bio_add_folio(io->bio, bio_folio, folio_size(bio_folio), 0)) { + bio_offset =3D F2FS_FIO_BIO_OFFSET(fio); + bio_len =3D F2FS_FIO_BIO_SIZE(fio); + + if (!bio_add_folio(io->bio, bio_folio, bio_len, bio_offset)) { __submit_merged_bio(io); goto alloc_new; } =20 if (fio->io_wbc) - wbc_account_cgroup_owner(fio->io_wbc, fio->folio, - folio_size(fio->folio)); + wbc_account_cgroup_owner(fio->io_wbc, fio->folio, bio_len); =20 - io->last_block_in_bio =3D fio->new_blkaddr; + io->last_block_in_bio =3D fio->new_blkaddr + fio_cnt - 1; =20 trace_f2fs_submit_folio_write(fio->folio, fio); #ifdef CONFIG_BLK_DEV_ZONED @@ -3039,7 +3060,8 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) set_new_dnode(&dn, inode, NULL, NULL, 0); =20 if (need_inplace_update(fio) && - f2fs_lookup_read_extent_cache_block(inode, folio->index, + f2fs_lookup_read_extent_cache_block(inode, + F2FS_FIO_LBLK(folio, fio), &fio->old_blkaddr)) { if (!f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr, DATA_GENERIC_ENHANCE)) @@ -3058,7 +3080,8 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) if (fio->need_lock =3D=3D LOCK_REQ && !f2fs_trylock_op(fio->sbi, &lc)) return -EAGAIN; =20 - err =3D f2fs_get_dnode_of_data(&dn, folio->index, LOOKUP_NODE); + err =3D f2fs_get_dnode_of_data(&dn, F2FS_FIO_LBLK(folio, fio), + LOOKUP_NODE); if (err) goto out; =20 diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2e8f85cea6d0..31ddd321daf6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1357,6 +1357,8 @@ struct f2fs_io_info { blk_opf_t op_flags; /* req_flag_bits */ block_t new_blkaddr; /* new block address to be written */ block_t old_blkaddr; /* old block address before Cow */ + pgoff_t folio_offset; /* offset in large folio */ + unsigned int folio_blkcnt; /* block count in large folio */ union { struct page *page; /* page to be written */ struct folio *folio; @@ -1378,6 +1380,15 @@ struct f2fs_io_info { sector_t *last_block; /* last block number in bio */ }; =20 +#define F2FS_FIO_LBLK(folio, fio) \ + ((folio)->index + (fio)->folio_offset) +#define F2FS_FIO_BLKCNT(fio) \ + ((fio)->folio_blkcnt ? (fio)->folio_blkcnt : 1) +#define F2FS_FIO_BIO_OFFSET(fio) \ + ((fio)->folio_offset << PAGE_SHIFT) +#define F2FS_FIO_BIO_SIZE(fio) \ + F2FS_BLK_TO_BYTES(F2FS_FIO_BLKCNT(fio)) + struct bio_entry { struct bio *bio; struct list_head list; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index df10119d94ad..5bb1e51a38a0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3816,7 +3816,8 @@ static int __get_segment_type_6(struct f2fs_io_info *= fio) if (file_is_cold(inode) || f2fs_need_compress_data(inode)) return CURSEG_COLD_DATA; =20 - type =3D __get_age_segment_type(inode, fio->folio->index); + type =3D __get_age_segment_type(inode, + F2FS_FIO_LBLK(fio->folio, fio)); if (type !=3D NO_CHECK_TYPE) return type; =20 --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD113353A9B for ; Wed, 26 Aug 2026 08:29:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.9 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732990; cv=none; b=hR6x3xa532QJDvWcsN8Ut4a+vOXO6DKpCqwxO29MbyZWUz6d7ZyZTq7jwAlJyMK7A1tO2/hH7QQ51sw54nsvV9+iOwN04l3wXUlntJ19GW65lpydjqGxKcN5c3znxIoTLPUp+95nF2Tw3R4eLY+mUQWdiMWHMmzJVhslZsI8Bkc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732990; c=relaxed/simple; bh=t2CzDk5h4qL/Yb3pWA1aTT1bBYiGIuBzfV3OrSVNMug=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fRArFQKsNrmmikAXeQ2Lo3v55Aj3O5iwYdWxabiYDDMiLHyez7qlye1yp5c/4uiFuN+haIcMdp39DoeZJ0h2wUxa+fXLEBSBPrUl1wbzhssHpQdz570SCuUOuvQhWPCw9mFCLrAwBXxtY9fATtDB6baV17ZCqGl27MBJZqdvvKc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=kv2mZt0S; arc=none smtp.client-ip=117.135.210.9 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="kv2mZt0S" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=jl AYafO9SNRbeWjgogI7of7VfPEXzoI+12pBdKASLDs=; b=kv2mZt0SV5Q2Ix2Ppb 4h5RG2TXXSxHZOhYEgsjzkLFF/5FlcuPQzf7UPrSd5ggsdo+IGe4sXyui71mkI5k NiarOnBZx5yzv7k4xXm/qCPeVA6zKquLXGHiBB2zOekNGONt2CreCv+2cmaJs/3+ X1RCRFQd2Ks5vO7O0TyB7Zgbc= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S5; Wed, 26 Aug 2026 16:26:45 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 03/14] f2fs: support regular file buffered writes on large folios Date: Wed, 26 Aug 2026 16:26:30 +0800 Message-ID: <20260826082641.2007658-4-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S5 X-Coremail-Antispam: 1Uf129KBjvAXoW3ZFWxZw15GrWxArWkGw4rZrb_yoW8Jw45Go WfJws0qw4rXrnrZrWjkw17tFyUuan8C3s5JFs3ursxuFnrX3s093y7Kwn8XayxWr15GF17 uryxJa13GrW3Jr1fn29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjxUcXo2UUUUU Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBogU4CGqOo0VSqAAA3v Content-Type: text/plain; charset="utf-8" To avoid the complexity of unlocking a large folio in write_begin, preallocate partial blocks for inodes that can use large folios. During write_begin, read only the partial head and tail 4K subpages that need read-before-write, and skip read I/O for the full middle subpages covered by the write. Signed-off-by: Nanzhe Zhao --- fs/f2fs/data.c | 289 +++++++++++++++++++++++++++++++++++++++++++++++-- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 17 ++- 3 files changed, 293 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8649d443ca74..7ce96ae02cfc 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1249,6 +1249,40 @@ static void f2fs_submit_page_read(struct inode *inod= e, struct fsverity_info *vi, f2fs_submit_read_bio(sbi, bio, DATA); } =20 +/* + * Synchronously read a single 4K subpage by reusing f2fs_submit_page_read= () + * so that iostat, trace, blk-crypto and post-read handling are all preser= ved. + * The caller must have already allocated ffs for the folio. + */ +static int f2fs_submit_page_read_sync(struct inode *inode, struct folio *f= olio, + pgoff_t index, block_t blkaddr) +{ + struct f2fs_folio_state *ffs =3D folio->private; + unsigned long flags; + + /* Add bias so end_io does not call folio_end_read(). */ + spin_lock_irqsave(&ffs->state_lock, flags); + ffs->read_pages_pending++; + spin_unlock_irqrestore(&ffs->state_lock, flags); + + f2fs_submit_page_read(inode, NULL, folio, index, blkaddr, + REQ_OP_READ, false); + + /* Wait for pending drops back to bias which indicates all bio have compl= eted. */ + while (READ_ONCE(ffs->read_pages_pending) !=3D 1) + f2fs_io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); + + /* Remove the bias. */ + spin_lock_irqsave(&ffs->state_lock, flags); + ffs->read_pages_pending--; + spin_unlock_irqrestore(&ffs->state_lock, flags); + + if (!f2fs_ffs_test_blk_uptodate(folio, index)) + return -EIO; + + return 0; +} + static void __set_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr) { __le32 *addr =3D get_dnode_addr(dn->inode, dn->node_folio); @@ -2582,6 +2616,112 @@ static void ffs_detach_free(struct folio *folio) kfree(ffs); } =20 +bool f2fs_ffs_test_blk_uptodate(const struct folio *folio, pgoff_t index) +{ + struct f2fs_folio_state *ffs; + unsigned int idx; + + if (!f2fs_folio_has_ffs(folio)) + return folio_test_uptodate(folio); + + if (!folio_contains(folio, index)) + return false; + + ffs =3D (struct f2fs_folio_state *)folio->private; + idx =3D index - folio->index; + return test_bit(idx, ffs->state); +} + +static bool __ffs_mark_subrange_uptodate(struct folio *folio, + struct f2fs_folio_state *ffs, size_t offset, size_t len) +{ + unsigned int nr_subpages =3D folio_nr_pages(folio); + unsigned int start, end; + + start =3D offset >> PAGE_SHIFT; + end =3D (offset + len + PAGE_SIZE - 1) >> PAGE_SHIFT; + end =3D min(end, nr_subpages); + + bitmap_set(ffs->state, start, end - start); + return bitmap_full(ffs->state, nr_subpages); +} + +static void f2fs_ffs_mark_subrange_uptodate(struct folio *folio, size_t of= fset, + size_t len) +{ + struct f2fs_folio_state *ffs; + unsigned long flags; + bool mark_uptodate =3D false; + + f2fs_bug_on(F2FS_F_SB(folio), offset + len > folio_size(folio)); + + if (!f2fs_folio_has_ffs(folio)) { + folio_mark_uptodate(folio); + return; + } + + ffs =3D (struct f2fs_folio_state *)folio->private; + spin_lock_irqsave(&ffs->state_lock, flags); + mark_uptodate =3D __ffs_mark_subrange_uptodate(folio, ffs, offset, len) && + !ffs->read_pages_pending; + spin_unlock_irqrestore(&ffs->state_lock, flags); + if (mark_uptodate) + folio_mark_uptodate(folio); +} + +static void f2fs_ffs_mark_subrange_dirty(struct folio *folio, + size_t offset, size_t len) +{ + struct f2fs_folio_state *ffs; + unsigned int nr_subpages, start, end; + unsigned long flags; + + f2fs_bug_on(F2FS_F_SB(folio), offset + len > folio_size(folio)); + + if (!f2fs_folio_has_ffs(folio)) + return; + + ffs =3D (struct f2fs_folio_state *)folio->private; + nr_subpages =3D folio_nr_pages(folio); + start =3D offset >> PAGE_SHIFT; + end =3D (offset + len + PAGE_SIZE - 1) >> PAGE_SHIFT; + end =3D min(end, nr_subpages); + + spin_lock_irqsave(&ffs->state_lock, flags); + bitmap_set(ffs->state, nr_subpages + start, end - start); + spin_unlock_irqrestore(&ffs->state_lock, flags); +} + +static bool find_next_valid_block(const struct folio *folio, + size_t orig_off, size_t *need_off, + size_t len) +{ + size_t start =3D orig_off; + size_t end =3D start + len; + size_t head, tail; + pgoff_t index; + + if (start & (PAGE_SIZE - 1)) { + head =3D round_down(start, PAGE_SIZE); + index =3D folio->index + (head >> PAGE_SHIFT); + if (!f2fs_ffs_test_blk_uptodate(folio, index)) { + *need_off =3D head; + return true; + } + } + + if (end & (PAGE_SIZE - 1)) { + tail =3D round_down(end - 1, PAGE_SIZE); + index =3D folio->index + (tail >> PAGE_SHIFT); + if (!f2fs_ffs_test_blk_uptodate(folio, index)) { + *need_off =3D tail; + return true; + } + } + + return false; +} + static int f2fs_read_data_large_folio(struct inode *inode, struct fsverity_info *vi, struct readahead_control *rac, struct folio *folio) @@ -3975,6 +4115,102 @@ static int prepare_atomic_write_begin(struct f2fs_s= b_info *sbi, return 0; } =20 +static int prepare_large_folio_write_begin(struct inode *inode, + struct folio *folio, loff_t pos, + unsigned int len) +{ + struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); + struct f2fs_folio_state *ffs; + size_t ori_off =3D offset_in_folio(folio, pos); + size_t need_off =3D ori_off; + int err =3D 0; + + len =3D min_t(unsigned int, len, folio_size(folio) - ori_off); + + /* + * When folio minimum order is non-zero, the fsverity + * page_cache_write() path enters f2fs_write_begin() via + * aops->write_begin without going through f2fs_write_iter(), + * so preallocation from f2fs_write_iter() is skipped. In that + * case, if FI_PREALLOCATED_ALL is not set, we must preallocate + * the write blocks here. + */ + if (!is_inode_flag_set(inode, FI_PREALLOCATED_ALL)) { + struct f2fs_map_blocks map =3D {}; + + map.m_lblk =3D F2FS_BYTES_TO_BLK(pos); + map.m_len =3D F2FS_BLK_ALIGN(pos + len) - map.m_lblk; + + if (!IS_DEVICE_ALIASING(inode)) + map.m_may_create =3D true; + map.m_seg_type =3D NO_CHECK_TYPE; + + err =3D f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_PRE_AIO); + if (err) + return err; + } + + /* + * Skip read and ffs allocation if the folio is already fully + * uptodate or the write covers the entire folio. + */ + if (folio_test_uptodate(folio) || len =3D=3D folio_size(folio)) + return 0; + + ffs =3D f2fs_ffs_find_or_alloc(folio); + if (!ffs) + return -ENOMEM; + + /* Inline data must have been converted before reaching here. */ + f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); + + while (find_next_valid_block(folio, ori_off, + &need_off, len)) { + struct dnode_of_data dn; + pgoff_t index =3D folio->index + (need_off >> PAGE_SHIFT); + block_t blkaddr; + bool get_dn =3D false; + + if (!f2fs_lookup_read_extent_cache_block(inode, index, + &blkaddr)) { + if (IS_DEVICE_ALIASING(inode)) + return -ENODATA; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err =3D f2fs_get_dnode_of_data(&dn, index, LOOKUP_NODE); + if (err) + return err; + get_dn =3D true; + blkaddr =3D dn.data_blkaddr; + + if (blkaddr =3D=3D NEW_ADDR) { + size_t off =3D offset_in_folio(folio, + index << PAGE_SHIFT); + + folio_zero_segment(folio, off, off + PAGE_SIZE); + f2fs_ffs_mark_subrange_uptodate(folio, off, + PAGE_SIZE); + goto out; + } + + if (!f2fs_is_valid_blkaddr(sbi, blkaddr, + DATA_GENERIC_ENHANCE_READ)) { + err =3D -EFSCORRUPTED; + goto out; + } + } + + err =3D f2fs_submit_page_read_sync(inode, folio, index, blkaddr); +out: + if (get_dn) + f2fs_put_dnode(&dn); + if (err) + return err; + } + + return 0; +} + static int f2fs_write_begin(const struct kiocb *iocb, struct address_space *mapping, loff_t pos, unsigned len, struct folio **foliop, @@ -3986,6 +4222,7 @@ static int f2fs_write_begin(const struct kiocb *iocb, pgoff_t index =3D pos >> PAGE_SHIFT; bool need_balance =3D false; block_t blkaddr =3D NULL_ADDR; + fgf_t fgp =3D FGP_LOCK | FGP_WRITE | FGP_CREAT; int err =3D 0; =20 trace_f2fs_write_begin(inode, pos, len); @@ -4033,9 +4270,9 @@ static int f2fs_write_begin(const struct kiocb *iocb, * Do not use FGP_STABLE to avoid deadlock. * Will wait that below with our IO control. */ - folio =3D f2fs_filemap_get_folio(mapping, index, - FGP_LOCK | FGP_WRITE | FGP_CREAT, - mapping_gfp_mask(mapping)); + fgp |=3D fgf_set_order(len); + folio =3D __filemap_get_folio(mapping, index, fgp, + mapping_gfp_mask(mapping)); if (IS_ERR(folio)) { err =3D PTR_ERR(folio); goto fail; @@ -4048,7 +4285,7 @@ static int f2fs_write_begin(const struct kiocb *iocb, if (f2fs_is_atomic_file(inode)) err =3D prepare_atomic_write_begin(sbi, folio, pos, len, &blkaddr, &need_balance); - else + else if (!folio_test_large(folio)) err =3D prepare_write_begin(sbi, folio, pos, len, &blkaddr, &need_balance); if (err) @@ -4069,6 +4306,14 @@ static int f2fs_write_begin(const struct kiocb *iocb, =20 f2fs_folio_wait_writeback(folio, DATA, false, true); =20 + if (folio_test_large(folio)) { + err =3D prepare_large_folio_write_begin(inode, + folio, pos, len); + if (!err) + return 0; + goto put_folio; + } + if (len =3D=3D folio_size(folio) || folio_test_uptodate(folio)) return 0; =20 @@ -4129,15 +4374,20 @@ static int f2fs_write_end(const struct kiocb *iocb, trace_f2fs_write_end(inode, pos, len, copied); =20 /* - * This should be come from len =3D=3D PAGE_SIZE, and we expect copied - * should be PAGE_SIZE. Otherwise, we treat it with zero copied and - * let generic_perform_write() try to copy data again through copied=3D0. + * If a short copy happens on a folio that isn't uptodate, we treat + * it with zero copied and let generic_perform_write() try to copy + * data again through copied=3D0. */ if (!folio_test_uptodate(folio)) { - if (unlikely(copied !=3D len)) + if (unlikely(copied !=3D len)) { copied =3D 0; - else + } else if (folio_test_large(folio)) { + f2fs_ffs_mark_subrange_uptodate(folio, + offset_in_folio(folio, pos), len); + } else { + /* This should be come from len =3D=3D PAGE_SIZE */ folio_mark_uptodate(folio); + } } =20 #ifdef CONFIG_F2FS_FS_COMPRESSION @@ -4156,6 +4406,9 @@ static int f2fs_write_end(const struct kiocb *iocb, if (!copied) goto unlock_out; =20 + if (folio_test_large(folio)) + f2fs_ffs_mark_subrange_dirty(folio, offset_in_folio(folio, pos), + copied); folio_mark_dirty(folio); =20 if (f2fs_is_atomic_file(inode)) @@ -4218,8 +4471,22 @@ static bool f2fs_dirty_data_folio(struct address_spa= ce *mapping, =20 trace_f2fs_set_page_dirty(folio, DATA); =20 - if (!folio_test_uptodate(folio)) - folio_mark_uptodate(folio); + if (!folio_test_uptodate(folio)) { + bool uptodate =3D true; + + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D + (struct f2fs_folio_state *)folio->private; + unsigned long flags; + + spin_lock_irqsave(&ffs->state_lock, flags); + uptodate =3D bitmap_full(ffs->state, folio_nr_pages(folio)) && + !ffs->read_pages_pending; + spin_unlock_irqrestore(&ffs->state_lock, flags); + } + if (uptodate) + folio_mark_uptodate(folio); + } BUG_ON(folio_test_swapcache(folio)); =20 if (filemap_dirty_folio(mapping, folio)) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 31ddd321daf6..ae6031693700 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4342,6 +4342,7 @@ int f2fs_write_single_data_page(struct folio *folio, = int *submitted, struct writeback_control *wbc, enum iostat_type io_type, int compr_blocks, bool allow_balance); +bool f2fs_ffs_test_blk_uptodate(const struct folio *folio, pgoff_t index); void f2fs_write_failed(struct inode *inode, loff_t to); void f2fs_invalidate_folio(struct folio *folio, size_t offset, size_t leng= th); bool f2fs_release_folio(struct folio *folio, gfp_t wait); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d440231b8cb9..e40285d4f9c2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -5405,9 +5405,20 @@ static int f2fs_preallocate_blocks(struct kiocb *ioc= b, struct iov_iter *iter, return ret; } =20 - /* Do not preallocate blocks that will be written partially in 4KB. */ - map.m_lblk =3D F2FS_BLK_ALIGN(pos); - map.m_len =3D F2FS_BYTES_TO_BLK(pos + count); + if (mapping_large_folio_support(inode->i_mapping)) { + /* + * Preallocate all blocks touched by a large-folio buffered write so + * the regular write_begin path does not need to unlock the folio for + * f2fs_balance_fs(). Rechecking large-folio state after unlock is + * unreliable since partial truncation can split the folio. + */ + map.m_lblk =3D F2FS_BYTES_TO_BLK(pos); + map.m_len =3D F2FS_BLK_ALIGN(pos + count); + } else { + /* Do not preallocate blocks that will be written partially in 4KB. */ + map.m_lblk =3D F2FS_BLK_ALIGN(pos); + map.m_len =3D F2FS_BYTES_TO_BLK(pos + count); + } if (map.m_len > map.m_lblk) map.m_len -=3D map.m_lblk; else --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 67D573B7770 for ; Wed, 26 Aug 2026 08:27:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732856; cv=none; b=Q7STgy7tn81hcauwj35QXrgdazWf70nwrxX4SD68MS0V3dAKvaGVXgBm1vsu849TjZuGTPzgAA6F1CB1aLbeDjCSaQIDT6Y7WMuGLARsvUjCPZ4/Jr1zvNUaAnVUOjEtMjVtvssuYyVaz4CriG7C3Oj1DkyhmW+Dp7n+t8g4gp0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732856; c=relaxed/simple; bh=+uEQGjGm2BKY5GVO0Xm/FNqChMJ52IHNcjh7YLcrauE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uqPXDlF8MkIgDpmup4i2k21fGrBNO8RVNjX8LgWUkqMaXZjAQfucwI1EzArmHig92TBDEqpMNznSa7nSx+SPl0qjBBYz0y2Z9KV87YGh4S17cIsN9DWeQ9uI5WCPRxKO492UfjRzyqJ9RR22gH+OZzgdzgBk3uJFMuhFRo0CL1Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=TmSX8Zi7; arc=none smtp.client-ip=117.135.210.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="TmSX8Zi7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=R5 VhOkEDRJp/Z+fhFGqawuZ5o6XlbmqLabVb2kU3S+g=; b=TmSX8Zi7yh0NsN8sZK XgOYkKqXN58yIMVpEy+dFBQx92Ze6FYH1QrsXKc+SNSmO2wpDQDki7LABbKRx0mZ u2zQ8XlwioLzVUYUlaTnSF+zQKELgsmjmDGxhdCNPwvUsZ9aX5jyAZt5IAw48oDP Ksx34ejEnIZW0GzIeICDpXSK8= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S6; Wed, 26 Aug 2026 16:26:45 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 04/14] f2fs: support atomic file large folios buffered write Date: Wed, 26 Aug 2026 16:26:31 +0800 Message-ID: <20260826082641.2007658-5-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S6 X-Coremail-Antispam: 1Uf129KBjvJXoWxKF1fuFyDJFWDWF4kCrW8Crg_yoW7GF1fpF WUKwn5GrWrJa4furn3XF1jvr1Y934Iqw47GayfGw1fZ3W5J3sxK3Wvyw1YkF1YqrZ7A3W8 Xa17tFWrW3WkJrJanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jUNVkUUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBsQY4CGqOo0ZikAAA30 Content-Type: text/plain; charset="utf-8" ioctl can convert an inode with large folio support into an atomic file. Support large folio buffered writes for atomic files as well. Add a large folio atomic write_begin helper that reserves COW mappings for the write range. For partial head and tail subpages, read the existing data from either the COW inode or the original inode before marking the subpage uptodate. Signed-off-by: Nanzhe Zhao --- fs/f2fs/data.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 7ce96ae02cfc..dff802725544 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -4211,6 +4211,117 @@ static int prepare_large_folio_write_begin(struct i= node *inode, return 0; } =20 +static int prepare_large_folio_atomic_write_begin(struct inode *inode, + struct address_space *mapping, struct folio *folio, loff_t pos, + unsigned int len) +{ + struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); + struct inode *cow_inode =3D F2FS_I(inode)->cow_inode; + size_t ori_off =3D offset_in_folio(folio, pos); + size_t need_off =3D ori_off; + pgoff_t index; + int err =3D 0; + unsigned int orig_order; + bool need_balance =3D false; + + len =3D min_t(unsigned int, len, folio_size(folio) - ori_off); + + f2fs_ffs_find_or_alloc(folio); + + /* Inline data must have been converted before reaching here. */ + f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); + + /* Reserve COW blocks for all covered 4K subpages first. */ + pgoff_t start_index =3D folio->index + (ori_off >> PAGE_SHIFT); + pgoff_t end_index =3D folio->index + + ((ori_off + len - 1) >> PAGE_SHIFT); + + for (index =3D start_index; index <=3D end_index; index++) { + block_t cow_blkaddr =3D NULL_ADDR; + bool node_changed =3D false; + int ret; + + ret =3D __find_data_block(cow_inode, index, &cow_blkaddr); + if (ret) + return ret; + if (cow_blkaddr !=3D NULL_ADDR) + continue; + + ret =3D __reserve_data_block(cow_inode, index, &cow_blkaddr, + &node_changed); + if (ret) + return ret; + + inc_atomic_write_cnt(inode); + need_balance |=3D node_changed; + } + + if (need_balance && !IS_NOQUOTA(inode) && + has_not_enough_free_secs(sbi, 0, 0)) { + orig_order =3D folio_order(folio); + folio_unlock(folio); + f2fs_balance_fs(sbi, true); + folio_lock(folio); + if (unlikely(folio->mapping !=3D mapping || + folio_order(folio) !=3D orig_order)) + return -EAGAIN; + } + + if (folio_test_uptodate(folio) || len =3D=3D folio_size(folio)) + return 0; + + /* Then read partial 4K subpages. */ + while (find_next_valid_block(folio, ori_off, &need_off, len)) { + size_t off; + block_t cow_blkaddr =3D NULL_ADDR; + block_t ori_blkaddr =3D NULL_ADDR; + struct inode *read_inode =3D NULL; + block_t read_blkaddr =3D NULL_ADDR; + + index =3D folio->index + (need_off >> PAGE_SHIFT); + off =3D offset_in_folio(folio, index << PAGE_SHIFT); + + err =3D __find_data_block(cow_inode, index, &cow_blkaddr); + if (err) + return err; + + if (__is_valid_data_blkaddr(cow_blkaddr)) { + if (!f2fs_is_valid_blkaddr(sbi, cow_blkaddr, + DATA_GENERIC_ENHANCE_READ)) + return -EFSCORRUPTED; + read_inode =3D cow_inode; + read_blkaddr =3D cow_blkaddr; + } else if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE)) { + folio_zero_segment(folio, off, off + PAGE_SIZE); + f2fs_ffs_mark_subrange_uptodate(folio, off, PAGE_SIZE); + continue; + } else { + err =3D __find_data_block(inode, index, &ori_blkaddr); + if (err) + return err; + + if (!__is_valid_data_blkaddr(ori_blkaddr)) { + folio_zero_segment(folio, off, off + PAGE_SIZE); + f2fs_ffs_mark_subrange_uptodate(folio, off, PAGE_SIZE); + continue; + } + + if (!f2fs_is_valid_blkaddr(sbi, ori_blkaddr, + DATA_GENERIC_ENHANCE_READ)) + return -EFSCORRUPTED; + read_inode =3D inode; + read_blkaddr =3D ori_blkaddr; + } + + err =3D f2fs_submit_page_read_sync(read_inode, folio, + index, read_blkaddr); + if (err) + return err; + } + + return 0; +} + static int f2fs_write_begin(const struct kiocb *iocb, struct address_space *mapping, loff_t pos, unsigned len, struct folio **foliop, @@ -4282,7 +4393,7 @@ static int f2fs_write_begin(const struct kiocb *iocb, =20 *foliop =3D folio; =20 - if (f2fs_is_atomic_file(inode)) + if (f2fs_is_atomic_file(inode) && !folio_test_large(folio)) err =3D prepare_atomic_write_begin(sbi, folio, pos, len, &blkaddr, &need_balance); else if (!folio_test_large(folio)) @@ -4307,10 +4418,18 @@ static int f2fs_write_begin(const struct kiocb *ioc= b, f2fs_folio_wait_writeback(folio, DATA, false, true); =20 if (folio_test_large(folio)) { - err =3D prepare_large_folio_write_begin(inode, + if (f2fs_is_atomic_file(inode)) + err =3D prepare_large_folio_atomic_write_begin(inode, + mapping, folio, pos, len); + else + err =3D prepare_large_folio_write_begin(inode, folio, pos, len); if (!err) return 0; + if (err =3D=3D -EAGAIN) { + f2fs_folio_put(folio, true); + goto repeat; + } goto put_folio; } =20 --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A65D63BB119 for ; Wed, 26 Aug 2026 08:27:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.9 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732858; cv=none; b=KjNcXHO8Aoe4Niha9fFliOeefQsoUhEZ7e8reK3HqqWXBJI/o68p0VgkpJSPBhXvcxqHgXSKUCqzYEybbLj7RnHNWPchy4DcCjKFNxy+iMjeBleQOGdwz6QBUnBasFHiAR5Y9BKlq3zbYOWGCCiVBZ+KWu6S76g4yKeq8h1boME= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732858; c=relaxed/simple; bh=ogieAsykI9Tm3+/a2o+xUSqqa/Zxynt5MiHE6/XLzgw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QABb6RJtpxB590mY/RF5OKZ4jkjxN7Zczc/YHtpFL+StUjzCIpzhtu1rkw1QxGYQ4Z6Ky1x2QLOrAMQdYmHZ3RysI5Fmktjjo7Fzarld8i3SthFjxwcl6xLnVVKpBn4e1NQgitsHh1CzegnoroeTBSNlRPiuj6AQLlPemHHRu8g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=BRgvZO3x; arc=none smtp.client-ip=117.135.210.9 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="BRgvZO3x" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=tH oS6VHkyedYpyVt1cx7W73L4pmoi9arcOG6lCrl9tk=; b=BRgvZO3xh9jzOGbWeu YySz6kXVA9cWgIypWYqRAU11nM4D3Xu+NEPGfECCnRey3Tk0xuD5Y+0UpIlRZ5dA 7lRxaC8f35MAVswVXE6TuBShnXbr97da5e+CfhPBsDvzxnL1RAAMf37N73x20aeM 3RFMKZelzsYtTNFmIUSWltCGI= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S7; Wed, 26 Aug 2026 16:26:46 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 05/14] f2fs: support large folio writeback Date: Wed, 26 Aug 2026 16:26:32 +0800 Message-ID: <20260826082641.2007658-6-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S7 X-Coremail-Antispam: 1Uf129KBjvAXoW3KFW5WF13WFW8AFykGFyDZFb_yoW8JFWfCo WfJw4qqr1rKr17ArWj9w12qFyUuws8AF95AF4furs8Z3ZrX3s0gws7Kwn8X3W7Zr1Ykr12 9ryxK3ZxJrWxA3Wfn29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjxU7FksDUUUU Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBsQY4CGqOo0ZimgAA3+ Content-Type: text/plain; charset="utf-8" Large folio can contain multiple dirty ranges. Add a folio-based writeback path for large-folio mapping files and keep the legacy f2fs_write_cache_pages() path unchanged for non large-folio mapping files. Signed-off-by: Nanzhe Zhao --- fs/f2fs/data.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 398 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index dff802725544..608d8ea8e607 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -358,6 +358,7 @@ static void f2fs_write_end_bio(struct bio *bio) struct folio *folio =3D fi.folio; unsigned int nr_pages =3D fi.length >> PAGE_SHIFT; enum count_type type; + bool finished =3D true; =20 if (fscrypt_is_bounce_folio(folio)) { struct folio *io_folio =3D folio; @@ -388,11 +389,20 @@ static void f2fs_write_end_bio(struct bio *bio) folio->index, NODE_TYPE_REGULAR, true); f2fs_bug_on(sbi, folio->index !=3D nid_of_node(folio)); } + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D + (struct f2fs_folio_state *)folio->private; + + finished =3D atomic_sub_and_test(nr_pages, + &ffs->write_pages_pending); + } + + while (nr_pages--) + dec_page_count(sbi, type); + if (f2fs_in_warm_node_list(folio)) f2fs_del_fsync_node_entry(sbi, folio); =20 - dec_page_count(sbi, type); - /* * we should access sbi before folio_end_writeback() to * avoid racing w/ kill_f2fs_super() @@ -401,8 +411,10 @@ static void f2fs_write_end_bio(struct bio *bio) wq_has_sleeper(&sbi->cp_wait)) wake_up(&sbi->cp_wait); =20 - folio_clear_f2fs_gcing(folio); - folio_end_writeback(folio); + if (finished) { + folio_clear_f2fs_gcing(folio); + folio_end_writeback(folio); + } } =20 bio_put(bio); @@ -2669,7 +2681,7 @@ static void f2fs_ffs_mark_subrange_uptodate(struct fo= lio *folio, size_t offset, folio_mark_uptodate(folio); } =20 -static void f2fs_ffs_mark_subrange_dirty(struct folio *folio, +void f2fs_ffs_mark_subrange_dirty(struct folio *folio, size_t offset, size_t len) { struct f2fs_folio_state *ffs; @@ -2692,6 +2704,86 @@ static void f2fs_ffs_mark_subrange_dirty(struct foli= o *folio, spin_unlock_irqrestore(&ffs->state_lock, flags); } =20 +static bool __ffs_clear_subrange_dirty(struct folio *folio, + struct f2fs_folio_state *ffs, size_t offset, size_t len) +{ + unsigned int nr_subpages =3D folio_nr_pages(folio); + unsigned int start, end; + + start =3D offset >> PAGE_SHIFT; + end =3D (offset + len + PAGE_SIZE - 1) >> PAGE_SHIFT; + end =3D min(end, nr_subpages); + + bitmap_clear(ffs->state, nr_subpages + start, end - start); + return find_next_bit(ffs->state, 2 * nr_subpages, nr_subpages) < + 2 * nr_subpages; +} + +void f2fs_ffs_clear_subrange_dirty(struct folio *folio, size_t offset, siz= e_t len) +{ + struct f2fs_folio_state *ffs; + unsigned long flags; + + if (!f2fs_folio_has_ffs(folio)) + return; + + ffs =3D (struct f2fs_folio_state *)folio->private; + spin_lock_irqsave(&ffs->state_lock, flags); + __ffs_clear_subrange_dirty(folio, ffs, offset, len); + spin_unlock_irqrestore(&ffs->state_lock, flags); +} + +static unsigned int ffs_next_dirty_subpage(struct f2fs_folio_state *ffs, + const struct folio *folio, unsigned int start, + unsigned int end) +{ + unsigned int nr_subpages =3D folio_nr_pages(folio); + + return find_next_bit(ffs->state, nr_subpages + end + 1, + nr_subpages + start) - nr_subpages; +} + +static unsigned int ffs_next_clean_subpage(struct f2fs_folio_state *ffs, + const struct folio *folio, unsigned int start, + unsigned int end) +{ + unsigned int nr_subpages =3D folio_nr_pages(folio); + + return find_next_zero_bit(ffs->state, nr_subpages + end + 1, + nr_subpages + start) - nr_subpages; +} + +static unsigned int ffs_find_dirty_range(struct folio *folio, + u64 *range_start, u64 range_end) +{ + struct f2fs_folio_state *ffs; + unsigned int start, end, nr_pages; + + if (*range_start >=3D range_end) + return 0; + + if (!f2fs_folio_has_ffs(folio)) + return range_end - *range_start; + + ffs =3D (struct f2fs_folio_state *)folio->private; + start =3D offset_in_folio(folio, *range_start) >> PAGE_SHIFT; + end =3D DIV_ROUND_UP(min_not_zero(offset_in_folio(folio, range_end), + folio_size(folio)), PAGE_SIZE) - 1; + + start =3D ffs_next_dirty_subpage(ffs, folio, start, end); + if (start > end) + return 0; + + if (start =3D=3D end) + nr_pages =3D 1; + else + nr_pages =3D ffs_next_clean_subpage(ffs, folio, + start + 1, end) - start; + + *range_start =3D folio_pos(folio) + ((u64)start << PAGE_SHIFT); + return (u64)nr_pages << PAGE_SHIFT; +} + static bool find_next_valid_block(const struct folio *folio, size_t orig_off, size_t *need_off, size_t len) @@ -3309,6 +3401,139 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fi= o) return err; } =20 +static int f2fs_write_single_data_folio(struct folio *folio, int *submitte= d, + struct writeback_control *wbc, + enum iostat_type io_type, + u64 start, u64 end) +{ + struct inode *inode =3D folio->mapping->host; + struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); + bool atomic_commit =3D f2fs_is_atomic_file(inode) && + folio_test_f2fs_atomic(folio); + struct inode *dn_inode =3D atomic_commit ? + F2FS_I(inode)->cow_inode : inode; + u64 pos =3D folio_pos(folio); + pgoff_t start_idx =3D (start - pos) >> PAGE_SHIFT; + pgoff_t end_idx =3D (end - 1 - pos) >> PAGE_SHIFT; + int local_submitted =3D 0; + int err =3D 0; + + for (pgoff_t i =3D start_idx; i <=3D end_idx; i++) { + struct dnode_of_data dn; + struct node_info ni; + pgoff_t data_idx =3D folio->index + i; + bool ipu_force =3D false; + struct f2fs_io_info fio =3D { + .sbi =3D sbi, + .ino =3D inode->i_ino, + .type =3D DATA, + .op =3D REQ_OP_WRITE, + .op_flags =3D wbc_to_write_flags(wbc), + .old_blkaddr =3D NULL_ADDR, + .folio =3D folio, + .folio_offset =3D i, + .folio_blkcnt =3D 1, + .encrypted_page =3D NULL, + .submitted =3D 0, + .need_lock =3D LOCK_DONE, + .meta_gc =3D f2fs_meta_inode_gc_required(inode) ? 1 : 0, + .io_type =3D io_type, + .io_wbc =3D wbc, + }; + + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D + (struct f2fs_folio_state *)folio->private; + + atomic_inc(&ffs->write_pages_pending); + } + + set_new_dnode(&dn, dn_inode, NULL, NULL, 0); + + if (!atomic_commit && need_inplace_update(&fio) && + f2fs_lookup_read_extent_cache_block(inode, data_idx, + &fio.old_blkaddr)) { + if (!f2fs_is_valid_blkaddr(sbi, fio.old_blkaddr, + DATA_GENERIC_ENHANCE)) { + err =3D -EFSCORRUPTED; + goto rollback; + } + ipu_force =3D true; + goto got_it; + } + + err =3D f2fs_get_dnode_of_data(&dn, data_idx, LOOKUP_NODE); + if (err) + goto rollback; + + fio.old_blkaddr =3D dn.data_blkaddr; + +got_it: + if (__is_valid_data_blkaddr(fio.old_blkaddr) && + !f2fs_is_valid_blkaddr(sbi, fio.old_blkaddr, + DATA_GENERIC_ENHANCE)) { + err =3D -EFSCORRUPTED; + goto rollback; + } + + if (fio.meta_gc) + f2fs_wait_on_block_writeback(inode, fio.old_blkaddr); + + if (!atomic_commit && (ipu_force || + (__is_valid_data_blkaddr(fio.old_blkaddr) && + need_inplace_update(&fio)))) { + err =3D f2fs_encrypt_one_page(&fio); + if (err) + goto rollback; + + f2fs_put_dnode(&dn); + err =3D f2fs_inplace_write_data(&fio); + if (err) { + if (fscrypt_inode_uses_fs_layer_crypto(inode)) + fscrypt_finalize_bounce_page( + &fio.encrypted_page); + goto rollback_no_dnode; + } + + local_submitted++; + set_inode_flag(inode, FI_UPDATE_WRITE); + continue; + } + + err =3D f2fs_get_node_info(sbi, dn.nid, &ni, false); + if (err) + goto rollback; + + fio.version =3D ni.version; + + err =3D f2fs_encrypt_one_page(&fio); + if (err) + goto rollback; + + f2fs_outplace_write_data(&dn, &fio); + local_submitted++; + set_inode_flag(inode, FI_APPEND_WRITE); + trace_f2fs_do_write_data_page(folio, OPU); + f2fs_put_dnode(&dn); + continue; + +rollback: + f2fs_put_dnode(&dn); +rollback_no_dnode: + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D + (struct f2fs_folio_state *)folio->private; + + atomic_dec(&ffs->write_pages_pending); + } + break; + } + + if (submitted) + *submitted =3D local_submitted; + return err; +} + int f2fs_write_single_data_page(struct folio *folio, int *submitted, struct bio **bio, sector_t *last_block, @@ -3757,6 +3982,170 @@ static int f2fs_write_cache_pages(struct address_sp= ace *mapping, return ret; } =20 +static int f2fs_write_cache_folios(struct address_space *mapping, + struct writeback_control *wbc, + enum iostat_type io_type) +{ + struct folio *folio =3D NULL; + struct inode *inode =3D mapping->host; + struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); + struct f2fs_lock_context lc; + u64 pos =3D 0; + u64 end_pos =3D 0; + u32 r_len =3D 0; + int err =3D 0; + int submitted =3D 0; + int nwritten =3D 0; + bool op_locked =3D false; + bool next =3D false; + bool retry =3D false; + + if (get_dirty_pages(inode) <=3D SM_I(sbi)->min_hot_blocks) + set_inode_flag(inode, FI_HOT_DATA); + else + clear_inode_flag(inode, FI_HOT_DATA); + + while ((folio =3D writeback_iter(mapping, wbc, folio, &err))) { + struct f2fs_folio_state *ffs =3D NULL; + u64 isize; + size_t poff; + pgoff_t end_index; + bool verity_in_progress; + int folio_submitted =3D 0; + bool bias_added =3D false; + + submitted =3D 0; + next =3D true; + retry =3D false; + + if (atomic_read(&sbi->wb_sync_req[DATA]) && + wbc->sync_mode =3D=3D WB_SYNC_NONE) { + folio_redirty_for_writepage(wbc, folio); + next =3D false; + goto retry_out; + } +retry: + pos =3D folio_pos(folio); + end_pos =3D pos + folio_size(folio); + isize =3D i_size_read(inode); + verity_in_progress =3D f2fs_verity_in_progress(inode); + poff =3D 0; + end_index =3D 0; + + if (retry) { + if (unlikely(folio->mapping !=3D mapping)) + goto retry_out; + + if (!folio_test_dirty(folio)) + goto retry_out; + + if (folio_test_writeback(folio)) { + if (wbc->sync_mode =3D=3D WB_SYNC_NONE) + goto retry_out; + f2fs_folio_wait_writeback(folio, DATA, true, true); + } + + if (!folio_clear_dirty_for_io(folio)) + goto retry_out; + } + + /* To avoid dealing with the complexity for one subrange is in bio + * while we trylock_op failed before writing another subrange. + * Try to lock_op before any subrange write for the folio. + */ + if (!op_locked) { + if (!f2fs_trylock_op(sbi, &lc)) { + folio_redirty_for_writepage(wbc, folio); + err =3D 0; + if (wbc->sync_mode !=3D WB_SYNC_ALL) + goto retry_out; + + retry =3D true; + folio_unlock(folio); + f2fs_io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); + folio_lock(folio); + goto retry; + } + op_locked =3D true; + } + + if (!verity_in_progress) { + poff =3D offset_in_folio(folio, isize); + end_index =3D isize >> PAGE_SHIFT; + + if (folio->index > end_index || + (folio->index =3D=3D end_index && poff =3D=3D 0)) + goto out; + + if (end_pos > isize) { + folio_zero_segment(folio, poff, folio_size(folio)); + end_pos =3D isize; + } + } + + folio_start_writeback(folio); + + if (folio_test_large(folio)) { + if (!f2fs_folio_has_ffs(folio)) { + ffs =3D f2fs_ffs_find_or_alloc(folio); + f2fs_ffs_mark_subrange_dirty(folio, 0, end_pos - pos); + } else { + ffs =3D (struct f2fs_folio_state *)folio->private; + } + if (f2fs_folio_has_ffs(folio) && !bias_added) { + WARN_ON_ONCE(atomic_read(&ffs->write_pages_pending) !=3D 0); + atomic_inc(&ffs->write_pages_pending); + bias_added =3D true; + } + } + + while ((r_len =3D ffs_find_dirty_range(folio, &pos, end_pos))) { + err =3D f2fs_write_single_data_folio(folio, &submitted, + wbc, io_type, pos, pos + r_len); + folio_submitted +=3D submitted; + if (err) + goto out; + + nwritten +=3D submitted; + pos +=3D r_len; + } + + if (!err && folio_submitted && + f2fs_is_atomic_file(inode) && + folio_test_f2fs_atomic(folio)) + folio_clear_f2fs_atomic(folio); + +out: + f2fs_ffs_clear_subrange_dirty(folio, 0, folio_size(folio)); + inode_dec_dirty_pages(inode); + + if (bias_added) { + if (atomic_dec_and_test(&ffs->write_pages_pending)) + folio_end_writeback(folio); + } else if (!folio_submitted && folio_test_writeback(folio)) { + folio_end_writeback(folio); + } + +retry_out: + if (folio_test_locked(folio)) + folio_unlock(folio); + + if (op_locked) { + f2fs_unlock_op(sbi, &lc); + op_locked =3D false; + } + + if (err || !next) + break; + } + + if (nwritten) + f2fs_submit_merged_write_cond(F2FS_M_SB(mapping), mapping->host, + NULL, 0, DATA); + + return err; +} + static inline bool __should_serialize_io(struct inode *inode, struct writeback_control *wbc) { @@ -3851,7 +4240,10 @@ static int __f2fs_write_data_pages(struct address_sp= ace *mapping, account_writeback(inode, true); =20 blk_start_plug(&plug); - ret =3D f2fs_write_cache_pages(mapping, wbc, io_type); + if (mapping_large_folio_support(inode->i_mapping)) + ret =3D f2fs_write_cache_folios(mapping, wbc, io_type); + else + ret =3D f2fs_write_cache_pages(mapping, wbc, io_type); blk_finish_plug(&plug); =20 account_writeback(inode, false); --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [220.197.31.9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F19243B27DF for ; Wed, 26 Aug 2026 08:27:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.9 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732859; cv=none; b=iHnf3khkO3/bPPP7rbXhb7oZUvsP4aN4BHDhdqXD5b9SdoHdB5w/ZdrwHnRpOYDYqokzTuB+rdDVPkEbL5UW3msNW8A670y/4YVEnt/aZ6bICDMxJtET5s0WV0rm9uUFajTmmlXj30qy8/63JDrtc0W7CWIr4Xl8quXuXI3BP8A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732859; c=relaxed/simple; bh=1ObZQC0O7swbPinkkp0hd3rBqt8QABQMWh799HsLq40=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XfkJesqv8dogVe7Cba8zToUQWT+uA2B0M+7XWUbclvsoG8RJwX3MxjFzEJmWYgYYoMRIBTJ08ZA18pOMmKS3yhXf6mHMkYZci897sR9sswUOG5zg1klenNrDW1xbL/z7FU/yfj+7e5jvHdiOuPc17TIiIhzmSFa7KqAtwkbBJj0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=m3GlKtxd; arc=none smtp.client-ip=220.197.31.9 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="m3GlKtxd" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=g4 2vObmFjuVDxmSE/jvHANejGmsXT6JqqX+qP71lxCg=; b=m3GlKtxdxm7K/yNgf9 afgVN7iIKNOLhzACmR17QrlhsMiG4Vc1Nghe/SjOkKZYeusHOzc+xVdAJBXQX2zO Up0kayv7uJojpjJzmr/CJ63PKO6tRrPh+LmmAZWM2wTW0gNfJYwO7xN1doJWNPyf zyEoAIEqdSvQhGAjUsJsmoA/s= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S8; Wed, 26 Aug 2026 16:26:47 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 06/14] f2fs: prepare mmap write faults for large folios Date: Wed, 26 Aug 2026 16:26:33 +0800 Message-ID: <20260826082641.2007658-7-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S8 X-Coremail-Antispam: 1Uf129KBjvJXoW3Wr4kZF4kJr1rtw4fArW3GFg_yoW7uF1DpF W5G393JrZ3Xw15Wrn7KFn8Zw1rK34fWrWUZFWfG3W7Z3Wjvwn3KF18ta48ZF1ktry5AFn7 XFWjyFy5Wa4UGF7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jUZ2-UUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBsQc4CGqOo0dipAAA3A Content-Type: text/plain; charset="utf-8" Now write protect `mmap` also need to support large folio, Change `f2fs_vm_page_mkwrite` to acheive that. Note it currently marks the whole large folio dirty to avoid data loss which causes write amplification. Further optimization is welcome. PG_mappedtodisk is useless in f2fs, so drop the folio_test_mappedtodisk() check and its goto out_sem shortcut in f2fs_vm_page_mkwrite(). We extend the folio_zero_segment() in mkwrite to zero the post-EOF part of the faulted folio for both order-0 and large folios, so the f2fs_zero_post_eof_page() call added to cover that shortcut is no longer needed. Signed-off-by: Nanzhe Zhao --- fs/f2fs/f2fs.h | 5 +++++ fs/f2fs/file.c | 59 +++++++++++++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ae6031693700..71e6d7e34c7b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4343,6 +4343,11 @@ int f2fs_write_single_data_page(struct folio *folio,= int *submitted, enum iostat_type io_type, int compr_blocks, bool allow_balance); bool f2fs_ffs_test_blk_uptodate(const struct folio *folio, pgoff_t index); +struct f2fs_folio_state *f2fs_ffs_find_or_alloc(struct folio *folio); +void f2fs_ffs_mark_subrange_dirty(struct folio *folio, size_t offset, size= _t len); +bool f2fs_ffs_clear_subrange_dirty_and_test(struct folio *folio, size_t of= fset, + size_t len); +void f2fs_ffs_clear_subrange_dirty(struct folio *folio, size_t offset, siz= e_t len); void f2fs_write_failed(struct inode *inode, loff_t to); void f2fs_invalidate_folio(struct folio *folio, size_t offset, size_t leng= th); bool f2fs_release_folio(struct folio *folio, gfp_t wait); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e40285d4f9c2..02d687527241 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -124,6 +124,13 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault= *vmf) struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); struct dnode_of_data dn; bool need_alloc =3D !f2fs_is_pinned_file(inode); + pgoff_t pidx =3D folio->index + folio_page_idx(folio, vmf->page); + loff_t pos =3D (loff_t)pidx << PAGE_SHIFT; + loff_t isize; + loff_t folio_start; + loff_t valid_end; + size_t dirty_len; + size_t subpage_off; int err =3D 0; vm_fault_t ret; =20 @@ -160,7 +167,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault = *vmf) =20 #ifdef CONFIG_F2FS_FS_COMPRESSION if (f2fs_compressed_file(inode)) { - int ret =3D f2fs_is_compressed_cluster(inode, folio->index); + int ret =3D f2fs_is_compressed_cluster(inode, pidx); =20 if (ret < 0) { err =3D ret; @@ -178,18 +185,20 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_faul= t *vmf) =20 f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); =20 - err =3D f2fs_zero_post_eof_page(inode, - (folio->index + 1) << PAGE_SHIFT, true, false); - if (err) - goto out_pagefault; - file_update_time(vmf->vma->vm_file); filemap_invalidate_lock_shared(inode->i_mapping); =20 folio_lock(folio); + isize =3D i_size_read(inode); + folio_start =3D folio_pos(folio); + subpage_off =3D offset_in_folio(folio, pos); + valid_end =3D min_t(loff_t, folio_start + folio_size(folio), isize); + dirty_len =3D valid_end > folio_start ? valid_end - folio_start : 0; + if (unlikely(folio->mapping !=3D inode->i_mapping || - folio_pos(folio) > i_size_read(inode) || - !folio_test_uptodate(folio))) { + pos >=3D isize || + !f2fs_ffs_test_blk_uptodate(folio, + folio->index + (subpage_off >> PAGE_SHIFT)))) { folio_unlock(folio); err =3D -EFAULT; goto out_sem; @@ -198,9 +207,19 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault= *vmf) set_new_dnode(&dn, inode, NULL, NULL, 0); if (need_alloc) { /* block allocation */ - err =3D f2fs_get_block_locked(&dn, folio->index); + if (folio_test_large(folio)) { + pgoff_t i, nr =3D DIV_ROUND_UP(dirty_len, PAGE_SIZE); + + for (i =3D 0; i < nr; i++) { + err =3D f2fs_get_block_locked(&dn, folio->index + i); + if (err) + break; + } + } else { + err =3D f2fs_get_block_locked(&dn, pidx); + } } else { - err =3D f2fs_get_dnode_of_data(&dn, folio->index, LOOKUP_NODE); + err =3D f2fs_get_dnode_of_data(&dn, pidx, LOOKUP_NODE); f2fs_put_dnode(&dn); if (f2fs_is_pinned_file(inode) && !__is_valid_data_blkaddr(dn.data_blkaddr)) @@ -217,20 +236,17 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_faul= t *vmf) /* wait for GCed page writeback via META_MAPPING */ f2fs_wait_on_block_writeback(inode, dn.data_blkaddr); =20 - /* - * check to see if the page is mapped already (no holes) - */ - if (folio_test_mappedtodisk(folio)) - goto out_sem; - /* page is wholly or partially inside EOF */ - if (((loff_t)(folio->index + 1) << PAGE_SHIFT) > - i_size_read(inode)) { - loff_t offset; + if (folio_start + folio_size(folio) > isize) { + size_t offset =3D offset_in_folio(folio, isize); =20 - offset =3D i_size_read(inode) & ~PAGE_MASK; folio_zero_segment(folio, offset, folio_size(folio)); } + + if (folio_test_large(folio)) { + f2fs_ffs_find_or_alloc(folio); + f2fs_ffs_mark_subrange_dirty(folio, 0, dirty_len); + } folio_mark_dirty(folio); =20 f2fs_update_iostat(sbi, inode, APP_MAPPED_IO, F2FS_BLKSIZE); @@ -238,12 +254,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_faul= t *vmf) =20 out_sem: filemap_invalidate_unlock_shared(inode->i_mapping); -out_pagefault: sb_end_pagefault(inode->i_sb); out: ret =3D vmf_fs_error(err); =20 - trace_f2fs_vm_page_mkwrite(inode, folio->index, vmf->vma->vm_flags, ret); + trace_f2fs_vm_page_mkwrite(inode, pidx, vmf->vma->vm_flags, ret); return ret; } =20 --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [220.197.31.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 514FD35E922 for ; Wed, 26 Aug 2026 08:29:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.8 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732990; cv=none; b=M2FZn4QyhnN9osfgtWzDxxfi+C71n0VDIDe8CRsfH88jDt8sMRS+yJXaMVpnWMsuNpirmWHflNZ7vTY92yeI8tcVMldicYGXz0s4nbyc6jc9fynVcMqwMm6mYDXR8IsFmSc7f+mRTxfMMYdJQO/+/Wl46+KL21iKPkWOoyU57Vc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732990; c=relaxed/simple; bh=tqOMG86PcnAOnZ5ukQK1iwu1FekK1Wwf54ATyRB8gTw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=q9+KJ0ypFKWcJxggJUicrZGwSFgmBYEpZMtxJxflphX5pL3no3f/aDO21nf8AXx3bQGIlGWHTUxpg0UsaCzhyBBiXR2aIAW9FBWiIXn34TWFyY1OOMqsvRceB8j5y30TN6SYs572vL2L2qkvLRRp/voNqcQvGUQLM6uVXYIyR7M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=LAjPcbWi; arc=none smtp.client-ip=220.197.31.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="LAjPcbWi" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=BI JgKgjhKZ35i6Si83pVpQBISkM4kojv3OGaN6QBAy8=; b=LAjPcbWii70OnqNNQA vNZEU+KXVV6+chTko+U2dD/5AIWOso01/7/HsI/9kHEQKAfDpnpa01/e/YkmLaYK I0uu/TBsV4dWP2EKHjljoRRMLyeeDr5wOQH2OIJh3tbTQ+hkfaKjX15ipU2/vq4m ZzvtMHKWFbiS0TFfXv2HMMCHE= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S9; Wed, 26 Aug 2026 16:26:48 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 07/14] f2fs: make GC migration large-folio aware Date: Wed, 26 Aug 2026 16:26:34 +0800 Message-ID: <20260826082641.2007658-8-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S9 X-Coremail-Antispam: 1Uf129KBjvJXoW3Kr4DCrWUAry8Zw1xJw43KFg_yoWkJr1UpF WUGasxtrWDJF17urn7JF1DZr1rK34Sqr48ZayxG34fA3WYqwn5KF1kt34YkF1ftry8AFna qF1UKa45WF1UJF7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07UShFsUUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBowg5CWqOo0j3uAAA3b Content-Type: text/plain; charset="utf-8" GC can operate on a 4K block that is cached inside a large folio. The data lookup helpers therefore need to test and update uptodate state for the addressed subpage instead of rejecting large folios or treating the whole folio as the target block. Let f2fs_get_read_data_folio(), f2fs_find_data_folio(), and f2fs_get_lock_data_folio() to use subpage uptodate state. Submit single-block reads at the requested folio offset and zero only the addressed 4K range for NEW_ADDR. Also update `move_data_page` to mark, clear, and restore dirty state for the target subpage, and submit write I/O with the subpage offset recorded in f2fs_io_info. Signed-off-by: Nanzhe Zhao --- fs/f2fs/data.c | 96 ++++++++++++++++++++++++++++++++++---------------- fs/f2fs/f2fs.h | 6 ++-- fs/f2fs/gc.c | 30 ++++++++++++++-- 3 files changed, 97 insertions(+), 35 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 608d8ea8e607..0e54b1e25893 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1241,19 +1241,31 @@ static struct bio *f2fs_grab_read_bio(struct inode = *inode, =20 /* This can handle encryption stuffs */ static void f2fs_submit_page_read(struct inode *inode, struct fsverity_inf= o *vi, - struct folio *folio, block_t blkaddr, - blk_opf_t op_flags, bool for_write) + struct folio *folio, pgoff_t index, + block_t blkaddr, blk_opf_t op_flags, + bool for_write) { struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); struct bio *bio; + size_t offset =3D 0; =20 - bio =3D f2fs_grab_read_bio(inode, vi, blkaddr, 1, op_flags, folio->index, + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D folio->private; + unsigned long flags; + + offset =3D offset_in_folio(folio, (loff_t)index << PAGE_SHIFT); + spin_lock_irqsave(&ffs->state_lock, flags); + ffs->read_pages_pending++; + spin_unlock_irqrestore(&ffs->state_lock, flags); + } + + bio =3D f2fs_grab_read_bio(inode, vi, blkaddr, 1, op_flags, index, for_write); =20 /* wait for GCed page writeback via META_MAPPING */ f2fs_wait_on_block_writeback(inode, blkaddr); =20 - if (!bio_add_folio(bio, folio, PAGE_SIZE, 0)) + if (!bio_add_folio(bio, folio, PAGE_SIZE, offset)) f2fs_bug_on(sbi, 1); =20 inc_page_count(sbi, F2FS_RD_DATA); @@ -1399,21 +1411,13 @@ struct folio *f2fs_get_read_data_folio(struct inode= *inode, pgoff_t index, struct dnode_of_data dn; struct folio *folio; int err; -retry: + folio =3D f2fs_grab_cache_folio(mapping, index, for_write); if (IS_ERR(folio)) return folio; =20 - if (folio_test_large(folio)) { - pgoff_t folio_index =3D mapping_align_index(mapping, index); - unsigned long nr_pages =3D folio_nr_pages(folio); - - f2fs_folio_put(folio, true); - invalidate_inode_pages2_range(mapping, folio_index, - folio_index + nr_pages - 1); - f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); - goto retry; - } + if (folio_test_large(folio)) + f2fs_ffs_find_or_alloc(folio); =20 if (f2fs_lookup_read_extent_cache_block(inode, index, &dn.data_blkaddr)) { @@ -1448,7 +1452,7 @@ struct folio *f2fs_get_read_data_folio(struct inode *= inode, pgoff_t index, goto put_err; } got_it: - if (folio_test_uptodate(folio)) { + if (f2fs_ffs_test_blk_uptodate(folio, index)) { folio_unlock(folio); return folio; } @@ -1461,15 +1465,17 @@ struct folio *f2fs_get_read_data_folio(struct inode= *inode, pgoff_t index, * f2fs_init_inode_metadata. */ if (dn.data_blkaddr =3D=3D NEW_ADDR) { - folio_zero_segment(folio, 0, folio_size(folio)); - if (!folio_test_uptodate(folio)) - folio_mark_uptodate(folio); + size_t offset =3D offset_in_folio(folio, + (loff_t)index << PAGE_SHIFT); + + folio_zero_segment(folio, offset, offset + PAGE_SIZE); + f2fs_ffs_mark_subrange_uptodate(folio, offset, PAGE_SIZE); folio_unlock(folio); return folio; } =20 - f2fs_submit_page_read(inode, f2fs_need_verity(inode, folio->index), - folio, dn.data_blkaddr, op_flags, for_write); + f2fs_submit_page_read(inode, f2fs_need_verity(inode, index), + folio, index, dn.data_blkaddr, op_flags, for_write); return folio; =20 put_err: @@ -1486,7 +1492,7 @@ struct folio *f2fs_find_data_folio(struct inode *inod= e, pgoff_t index, folio =3D f2fs_filemap_get_folio(mapping, index, FGP_ACCESSED, 0); if (IS_ERR(folio)) goto read; - if (folio_test_uptodate(folio)) + if (f2fs_ffs_test_blk_uptodate(folio, index)) return folio; f2fs_folio_put(folio, false); =20 @@ -1495,11 +1501,11 @@ struct folio *f2fs_find_data_folio(struct inode *in= ode, pgoff_t index, if (IS_ERR(folio)) return folio; =20 - if (folio_test_uptodate(folio)) + if (f2fs_ffs_test_blk_uptodate(folio, index)) return folio; =20 folio_wait_locked(folio); - if (unlikely(!folio_test_uptodate(folio))) { + if (unlikely(!f2fs_ffs_test_blk_uptodate(folio, index))) { f2fs_folio_put(folio, false); return ERR_PTR(-EIO); } @@ -1523,7 +1529,8 @@ struct folio *f2fs_get_lock_data_folio(struct inode *= inode, pgoff_t index, =20 /* wait for read completion */ folio_lock(folio); - if (unlikely(folio->mapping !=3D mapping || !folio_test_uptodate(folio)))= { + if (unlikely(folio->mapping !=3D mapping || + !f2fs_ffs_test_blk_uptodate(folio, index))) { f2fs_folio_put(folio, true); return ERR_PTR(-EIO); } @@ -2681,6 +2688,23 @@ static void f2fs_ffs_mark_subrange_uptodate(struct f= olio *folio, size_t offset, folio_mark_uptodate(folio); } =20 +bool f2fs_ffs_test_blk_dirty(const struct folio *folio, pgoff_t index) +{ + struct f2fs_folio_state *ffs; + unsigned int idx, nr_subpages; + + if (!f2fs_folio_has_ffs(folio)) + return folio_test_dirty(folio); + + if (!folio_contains(folio, index)) + return false; + + ffs =3D folio->private; + nr_subpages =3D folio_nr_pages(folio); + idx =3D index - folio->index; + return test_bit(nr_subpages + idx, ffs->state); +} + void f2fs_ffs_mark_subrange_dirty(struct folio *folio, size_t offset, size_t len) { @@ -2719,18 +2743,30 @@ static bool __ffs_clear_subrange_dirty(struct folio= *folio, 2 * nr_subpages; } =20 -void f2fs_ffs_clear_subrange_dirty(struct folio *folio, size_t offset, siz= e_t len) +/** + * f2fs_ffs_clear_subrange_dirty - clear the dirty bits of a folio subrange + * @folio: the large folio + * @offset: start byte offset within the folio + * @len: byte length of the subrange + * + * Clear the dirty bits of the 4K subpages covered by [offset, offset + le= n), + * and return whether the folio still has any dirty subpage left. + */ +bool f2fs_ffs_clear_subrange_dirty(struct folio *folio, size_t offset, + size_t len) { struct f2fs_folio_state *ffs; unsigned long flags; + bool dirty; =20 if (!f2fs_folio_has_ffs(folio)) - return; + return false; =20 - ffs =3D (struct f2fs_folio_state *)folio->private; + ffs =3D folio->private; spin_lock_irqsave(&ffs->state_lock, flags); - __ffs_clear_subrange_dirty(folio, ffs, offset, len); + dirty =3D __ffs_clear_subrange_dirty(folio, ffs, offset, len); spin_unlock_irqrestore(&ffs->state_lock, flags); + return dirty; } =20 static unsigned int ffs_next_dirty_subpage(struct f2fs_folio_state *ffs, @@ -4853,7 +4889,7 @@ static int f2fs_write_begin(const struct kiocb *iocb, */ f2fs_submit_page_read(inode, NULL, /* can't write to fsverity files */ - folio, blkaddr, 0, true); + folio, index, blkaddr, 0, true); =20 folio_lock(folio); if (unlikely(folio->mapping !=3D mapping)) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 71e6d7e34c7b..0ffbe2bd04c8 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4344,10 +4344,10 @@ int f2fs_write_single_data_page(struct folio *folio= , int *submitted, int compr_blocks, bool allow_balance); bool f2fs_ffs_test_blk_uptodate(const struct folio *folio, pgoff_t index); struct f2fs_folio_state *f2fs_ffs_find_or_alloc(struct folio *folio); +bool f2fs_ffs_test_blk_dirty(const struct folio *folio, pgoff_t index); void f2fs_ffs_mark_subrange_dirty(struct folio *folio, size_t offset, size= _t len); -bool f2fs_ffs_clear_subrange_dirty_and_test(struct folio *folio, size_t of= fset, - size_t len); -void f2fs_ffs_clear_subrange_dirty(struct folio *folio, size_t offset, siz= e_t len); +bool f2fs_ffs_clear_subrange_dirty(struct folio *folio, size_t offset, + size_t len); void f2fs_write_failed(struct inode *inode, loff_t to); void f2fs_invalidate_folio(struct folio *folio, size_t offset, size_t leng= th); bool f2fs_release_folio(struct folio *folio, gfp_t wait); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 0c17038fcfd7..42e6dfcbf98d 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1504,12 +1504,19 @@ static int move_data_page(struct inode *inode, bloc= k_t bidx, int gc_type, unsigned int segno, int off) { struct folio *folio; + size_t foff =3D 0; + bool large =3D false; int err =3D 0; =20 folio =3D f2fs_get_lock_data_folio(inode, bidx, true); if (IS_ERR(folio)) return PTR_ERR(folio); =20 + if (f2fs_folio_has_ffs(folio)) { + large =3D true; + foff =3D offset_in_folio(folio, (loff_t)bidx << PAGE_SHIFT); + } + if (!check_valid_map(F2FS_I_SB(inode), segno, off)) { err =3D -ENOENT; goto out; @@ -1524,6 +1531,8 @@ static int move_data_page(struct inode *inode, block_= t bidx, int gc_type, err =3D -EAGAIN; goto out; } + if (large) + f2fs_ffs_mark_subrange_dirty(folio, foff, PAGE_SIZE); folio_mark_dirty(folio); folio_set_f2fs_gcing(folio); } else { @@ -1536,32 +1545,49 @@ static int move_data_page(struct inode *inode, bloc= k_t bidx, int gc_type, .op_flags =3D REQ_SYNC, .old_blkaddr =3D NULL_ADDR, .folio =3D folio, + .folio_offset =3D bidx - folio->index, + .folio_blkcnt =3D 1, .encrypted_page =3D NULL, .need_lock =3D LOCK_REQ, .io_type =3D FS_GC_DATA_IO, }; - bool is_dirty =3D folio_test_dirty(folio); + struct f2fs_folio_state *ffs =3D NULL; + bool is_dirty =3D f2fs_ffs_test_blk_dirty(folio, bidx); =20 retry: f2fs_folio_wait_writeback(folio, DATA, true, true); =20 + if (large) { + ffs =3D folio->private; + f2fs_ffs_mark_subrange_dirty(folio, foff, PAGE_SIZE); + } folio_mark_dirty(folio); if (folio_clear_dirty_for_io(folio)) { inode_dec_dirty_pages(inode); f2fs_remove_dirty_inode(inode); + if (large && + f2fs_ffs_clear_subrange_dirty(folio, foff, PAGE_SIZE)) + folio_mark_dirty(folio); } =20 + if (large) + atomic_inc(&ffs->write_pages_pending); folio_set_f2fs_gcing(folio); =20 err =3D f2fs_do_write_data_page(&fio); if (err) { folio_clear_f2fs_gcing(folio); + if (large) + atomic_dec(&ffs->write_pages_pending); if (err =3D=3D -ENOMEM) { memalloc_retry_wait(GFP_NOFS); goto retry; } - if (is_dirty) + if (is_dirty) { + if (large) + f2fs_ffs_mark_subrange_dirty(folio, foff, PAGE_SIZE); folio_mark_dirty(folio); + } } } out: --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 824B93B9D84 for ; Wed, 26 Aug 2026 08:27:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732857; cv=none; b=d1Loqe4jUwbooa6x/oFDNV2YVrVze3/QREGPGZYh0cNhFXidd9KgsogZe8A+p86wF0L88iOfZUS/PJ5FaGGolJaHj1mZiPjMNBlZKK6c4MM8pMyVnpQq/SVXZOUXtDxKybJvtqBmEA2FSBiXKgADrdIMHLcHOIxXg7LeugjgNBQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732857; c=relaxed/simple; bh=efaM4x09aYquKH/IcCF0j2Sh7rEQE1b8dwabElWkb/Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sZW8sKkctmT5lDT3KD0Puy/XVgAI6sMa5OLvbRPOC1bNBn17Gs8Z6/NrJ6kzl21ex7B6HGy3zb8kvBR4BAADltxf3wAlb6OKShLovvABlmyo8rlpq92MkBlWW9DE8SChRWhIhZ15P5LutoAalOendwyzU+SFPtFYsglKhuJHTHs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=lhJ6ohbM; arc=none smtp.client-ip=117.135.210.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="lhJ6ohbM" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=/q tpC58izgX6U/rQqVoA/r5hSEzSCHQCTdPAveMHdn8=; b=lhJ6ohbM7YbQBEdKN3 zCEQlqjrRijAd5ePXn0f2pMc/PCMhIxwWXVMLN6h4fEZTCwZY2TswjYDFDZc/C/P DzmXlWWgwq9RTP5NJ6FeughLHoLdL21pmqHH7NPVyqZL0xYRV95yiRZf6H9Q7MBD Dq5GnL1EyQuh4SSnFqhQUG1XI= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S10; Wed, 26 Aug 2026 16:26:49 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 08/14] f2fs: optimize small block size large folio read Date: Wed, 26 Aug 2026 16:26:35 +0800 Message-ID: <20260826082641.2007658-9-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S10 X-Coremail-Antispam: 1Uf129KBjvJXoWxJFW7urWUGF4UWw48uFWfXwb_yoW7Gr18pF WUKrn5KrZ8WFW7Xr1SqF1DZr1Sg348Ww4UGFZ7G34fJF1Yq3sakF1vk34YgF4rtr1kuFn7 XF48GryDWF4UXF7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jUZ2-UUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBogk5CWqOo0lSzQAA3K Content-Type: text/plain; charset="utf-8" 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 --- fs/f2fs/data.c | 62 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0e54b1e25893..48c1bb6c02e3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -153,6 +153,7 @@ static void f2fs_finish_read_bio(struct bio *bio, bool = in_task) struct folio *folio =3D fi.folio; unsigned int nr_pages =3D fi.length >> PAGE_SHIFT; bool finished =3D true; + bool uptodate =3D bio->bi_status =3D=3D BLK_STS_OK; =20 if (!folio_test_large(folio) && f2fs_is_compressed_page(folio)) { @@ -163,10 +164,14 @@ static void f2fs_finish_read_bio(struct bio *bio, boo= l in_task) continue; } =20 - if (folio_test_large(folio)) { - struct f2fs_folio_state *ffs =3D folio->private; + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D + (struct f2fs_folio_state *)folio->private; =20 spin_lock_irqsave(&ffs->state_lock, flags); + if (bio->bi_status =3D=3D BLK_STS_OK) + uptodate =3D __ffs_mark_subrange_uptodate(folio, ffs, + fi.offset, fi.length); ffs->read_pages_pending -=3D nr_pages; finished =3D !ffs->read_pages_pending; spin_unlock_irqrestore(&ffs->state_lock, flags); @@ -182,7 +187,7 @@ static void f2fs_finish_read_bio(struct bio *bio, bool = in_task) bio->bi_status =3D BLK_STS_IOERR; =20 if (finished) - folio_end_read(folio, bio->bi_status =3D=3D BLK_STS_OK); + folio_end_read(folio, uptodate); } =20 if (ctx) @@ -2887,8 +2892,15 @@ static int f2fs_read_data_large_folio(struct inode *= inode, ffs =3D NULL; nrpages =3D folio_nr_pages(folio); =20 - for (; nrpages; nrpages--, max_nr_pages--, index++, offset++) { + for (; nrpages; + nrpages -=3D len_blks, max_nr_pages -=3D len_blks, + index +=3D len_blks, offset +=3D len_blks) { sector_t block_nr; + bool whole_folio_in_bio; + unsigned int i; + + len_blks =3D 1; + /* * Map blocks using the previous result first. */ @@ -2917,13 +2929,31 @@ static int f2fs_read_data_large_folio(struct inode = *inode, got_it: if ((map.m_flags & F2FS_MAP_MAPPED)) { block_nr =3D map.m_pblk + index - map.m_lblk; - if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr, + + len_blks =3D min_t(unsigned int, nrpages, max_nr_pages); + len_blks =3D min_t(unsigned int, len_blks, + (unsigned int)(map.m_lblk + map.m_len - index)); + + for (i =3D 0; i < len_blks; i++) { + if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), + block_nr + i, DATA_GENERIC_ENHANCE_READ)) { - ret =3D -EFSCORRUPTED; - goto err_out; + ret =3D -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 =3D offset =3D=3D 0 && + len_blks =3D=3D folio_nr_pages(folio); + } else { size_t page_offset =3D offset << PAGE_SHIFT; + folio_zero_range(folio, page_offset, PAGE_SIZE); if (vi && !fsverity_verify_blocks(vi, folio, PAGE_SIZE, page_offset)) { ret =3D -EIO; @@ -2933,14 +2963,14 @@ static int f2fs_read_data_large_folio(struct inode = *inode, } =20 /* 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 =3D f2fs_ffs_find_or_alloc(folio); =20 /* set the bitmap to wait */ spin_lock_irq(&ffs->state_lock); - ffs->read_pages_pending++; + ffs->read_pages_pending +=3D len_blks; spin_unlock_irq(&ffs->state_lock); } =20 @@ -2965,17 +2995,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 =3D 0; i < len_blks; i++) + f2fs_wait_on_block_writeback(inode, block_nr + i); =20 - 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; =20 folio_in_bio =3D true; - inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA); + for (i =3D 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 =3D block_nr; + len_blks * F2FS_BLKSIZE); + last_block_in_bio =3D block_nr + len_blks - 1; } trace_f2fs_read_folio(folio, DATA); err_out: --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 84F993BB108 for ; Wed, 26 Aug 2026 08:27:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732857; cv=none; b=n293khfPJsg18hbH3FBV6cHrSZDSAR0MEWi9SKHqCU23Li9jr5nwr1R1zhSBwzohDZUT+l6oZDVAxYLpGww6H23hOQpmfVBRPp2hEU6Bjt6qnw1iBvPsmwK/J56+wsaPRFeD8U5lnTC9II3N7PF72aKYGa6b+cnMsPWYb1zPYWM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732857; c=relaxed/simple; bh=QU9yo/thfseoolDl95jpvAkK6fK/RAvgKEZxe48nm84=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=b+6A/yJ+F0/xwLQ+WZO4jEfgco1H+eBgx5HqBYduxmCZhSdfasu555tKvi52wAdRG3UvHImt9ZZXyhp0lmrSqCpNE+Z0iXKdE6Up9b8JKODEmBKGqYbQrUKKcfwD9yxJIeOv0OVWEHnsgeuWTTn8/dIkfcnFPN3x01TyhNReOLU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=fjsBuWTK; arc=none smtp.client-ip=117.135.210.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="fjsBuWTK" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=87 l8UBIwU+CaPrhamZWH2XD9LKTJw4XDkrfKsS5634Y=; b=fjsBuWTKPuASpZceNf 5qF/THWNGxhVg3C2KD7hG7JRXqzK5Aik3xkVkUYoDPQpp3DzUVHBLeDroKrm89Bx ONejDGPetG2sNFh4kKVWbfghc9WLajiJx46w136zYrDY3jD8jxY1t7d6uV4e/xy1 JkK0Pk88mseVJpGXWAjO3NSNM= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S11; Wed, 26 Aug 2026 16:26:49 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 09/14] f2fs: support partial uptodate large folio read Date: Wed, 26 Aug 2026 16:26:36 +0800 Message-ID: <20260826082641.2007658-10-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S11 X-Coremail-Antispam: 1Uf129KBjvJXoWxuF43KrWrJr15WFW5ZFWfGrg_yoW5ZF43pF y8KrZ5Kr45Kr43WF1ftFn3Zr1SqryxXa1xGFZ3G3s3A3WYqa4fCFykt34Ykr4rtrykCFna vF45GryUWF4UXFJanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jUZ2-UUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBogo5CWqOo0pS3AAA3b Content-Type: text/plain; charset="utf-8" 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 --- fs/f2fs/data.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 48c1bb6c02e3..59b07b83b216 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2855,6 +2855,56 @@ static bool find_next_valid_block(const struct folio= *folio, return false; } =20 +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 (!f2fs_folio_has_ffs(folio) || !*nrpages) + return; + + ffs =3D folio->private; + next =3D ffs_next_nonuptodate_subpage(ffs, *offset, + *offset + *nrpages - 1); + skipped =3D next - *offset; + if (!skipped) + return; + + *index +=3D skipped; + *offset +=3D skipped; + *nrpages -=3D skipped; + *max_nr_pages -=3D 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 (!f2fs_folio_has_ffs(folio) || *len_blks <=3D 1) + return; + + ffs =3D folio->private; + end =3D offset + *len_blks - 1; + next =3D ffs_next_uptodate_subpage(ffs, offset + 1, end); + if (next <=3D end) + *len_blks =3D next - offset; +} static int f2fs_read_data_large_folio(struct inode *inode, struct fsverity_info *vi, struct readahead_control *rac, struct folio *folio) @@ -2901,6 +2951,11 @@ static int f2fs_read_data_large_folio(struct inode *= inode, =20 len_blks =3D 1; =20 + f2fs_skip_fully_uptodate_front(folio, &index, &offset, + &nrpages, &max_nr_pages); + if (!nrpages) + break; + /* * Map blocks using the previous result first. */ @@ -2933,6 +2988,7 @@ static int f2fs_read_data_large_folio(struct inode *i= node, len_blks =3D min_t(unsigned int, nrpages, max_nr_pages); len_blks =3D min_t(unsigned int, len_blks, (unsigned int)(map.m_lblk + map.m_len - index)); + f2fs_truncate_read_extent(folio, offset, &len_blks); =20 for (i =3D 0; i < len_blks; i++) { if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), @@ -2959,6 +3015,13 @@ static int f2fs_read_data_large_folio(struct inode *= inode, ret =3D -EIO; goto err_out; } + if (folio_test_large(folio)) { + ffs =3D f2fs_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; } =20 --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4CE123B9D86 for ; Wed, 26 Aug 2026 08:27:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732861; cv=none; b=I02gZA8rbOLwaFG20bdpVx4IHW8gO3Hd7Gdl+L5IxnYA0k9CIVHGHHfCNU1pnlJKo3lRkqsQM12o+0dHre+8B2Y3utyJO555wMKjwlEXb5308PQZ1F2xIC9bcJBZQ6YU83ROQ2deSX9gYdHITYDLtB1cmfa+nFdraMFbTSm8kMQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787732861; c=relaxed/simple; bh=q3Oh3hC6FUJp+o3Bah3UUp4TCMWjoAFSlM4dEh6iBGE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uuGqQOV4HRTEm7C1Uk+s2fstuDyLUvtslLjdFWCzfGSwXR82VI2/76vjVq2DZV0QNXUg5t6SqGPjJ881mmOr8B/lhqzV1m1i/sGo+y03JU5Pw4+jyDWsvisVYprDng1GEewmTsrBYVUoQTi5R7S4EfiN/j7+2sMVagiWKHIJRo8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=mDLRGLYP; arc=none smtp.client-ip=117.135.210.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="mDLRGLYP" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=xT xcu4E3tmYM+GICCGyDOJXm+kl6S84p+izHnMzMsVU=; b=mDLRGLYP/67yelO3er st/2I++uemgflUHxHG2pGl6/23gNgODYa+1B/LNuEjIG1Vw7pxADykAkMpinSfK8 kZJssYtfEYxn9NzglTIfCri0uoDqKafaWx8twTns+Wj5DTusk7yHgm0wzSPFdr8o PYpV0WcJYS7mpPrCYwJSLrpZc= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-1 (Coremail) with SMTP id _____wD3F29Bo45qLzIoGQ--.14929S12; Wed, 26 Aug 2026 16:26:50 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 10/14] f2fs: handle partial truncate of large folio dirty subpages Date: Wed, 26 Aug 2026 16:26:37 +0800 Message-ID: <20260826082641.2007658-11-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wD3F29Bo45qLzIoGQ--.14929S12 X-Coremail-Antispam: 1Uf129KBjvJXoWxAr4fuFyrWr43GFW5Aw4fKrg_yoWrJw1DpF WDG3s8Jrs8J3Zruw1fXFn8Xr13K3s7Xr4UCa97Gw1fJ3WYv3sxKF18t34FgFs8Jry8AF1f Xr1jyay5WFyUKF7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jjeHgUUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBogo5CWqOo0pS6AAA3v Content-Type: text/plain; charset="utf-8" A large folio can be partial truncated and stays in folio mapping, we need to clear the subrange dirty bits and uptodate bits that the partial truncate covers. If this partial truncate happens to clear the last subrange dirty bits, then cancel the whole folio dirty state. Also add a guard in f2fs_write_single_data_folio() so a large folio subpage whose disk block was already truncated (NULL_ADDR) is skipped Signed-off-by: Nanzhe Zhao --- fs/f2fs/data.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 59b07b83b216..15774e8e8795 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2693,6 +2693,34 @@ static void f2fs_ffs_mark_subrange_uptodate(struct f= olio *folio, size_t offset, folio_mark_uptodate(folio); } =20 +static void ffs_clear_subrange_uptodate(struct folio *folio, + size_t offset, size_t len) +{ + struct f2fs_folio_state *ffs; + unsigned int nr_subpages, start, end; + unsigned long flags; + + f2fs_bug_on(F2FS_F_SB(folio), offset + len > folio_size(folio)); + + if (!f2fs_folio_has_ffs(folio)) { + if (folio_test_uptodate(folio)) + folio_clear_uptodate(folio); + return; + } + + ffs =3D (struct f2fs_folio_state *)folio->private; + nr_subpages =3D folio_nr_pages(folio); + start =3D offset >> PAGE_SHIFT; + end =3D (offset + len + PAGE_SIZE - 1) >> PAGE_SHIFT; + end =3D min(end, nr_subpages); + + spin_lock_irqsave(&ffs->state_lock, flags); + bitmap_clear(ffs->state, start, end - start); + spin_unlock_irqrestore(&ffs->state_lock, flags); + if (folio_test_uptodate(folio)) + folio_clear_uptodate(folio); +} + bool f2fs_ffs_test_blk_dirty(const struct folio *folio, pgoff_t index) { struct f2fs_folio_state *ffs; @@ -3599,6 +3627,21 @@ static int f2fs_write_single_data_folio(struct folio= *folio, int *submitted, =20 fio.old_blkaddr =3D dn.data_blkaddr; =20 + /* This page is already truncated */ + if (fio.old_blkaddr =3D=3D NULL_ADDR) { + ffs_clear_subrange_uptodate(folio, + i << PAGE_SHIFT, PAGE_SIZE); + folio_clear_f2fs_gcing(folio); + f2fs_put_dnode(&dn); + if (f2fs_folio_has_ffs(folio)) { + struct f2fs_folio_state *ffs =3D + (struct f2fs_folio_state *)folio->private; + + atomic_dec(&ffs->write_pages_pending); + } + continue; + } + got_it: if (__is_valid_data_blkaddr(fio.old_blkaddr) && !f2fs_is_valid_blkaddr(sbi, fio.old_blkaddr, @@ -5075,8 +5118,37 @@ void f2fs_invalidate_folio(struct folio *folio, size= _t offset, size_t length) struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); =20 if (inode->i_ino >=3D F2FS_ROOT_INO(sbi) && - (offset || length !=3D folio_size(folio))) + (offset || length !=3D folio_size(folio))) { + size_t clear_start =3D ALIGN(offset, PAGE_SIZE); + size_t clear_end =3D round_down(offset + length, PAGE_SIZE); + size_t clear_length =3D 0; + + /* + * If the truncated range falls within a single subpage, no + * subpage state needs to be cleared. + */ + if (clear_start < clear_end && f2fs_folio_has_ffs(folio)) { + bool dirty; + + clear_length =3D clear_end - clear_start; + dirty =3D f2fs_ffs_clear_subrange_dirty(folio, + clear_start, clear_length); + ffs_clear_subrange_uptodate(folio, clear_start, + clear_length); + + /* + * If the truncated subrange happens to clear the + * remaining dirty bitmap of the whole folio, cancel + * the folio-level dirty state. + */ + if (!dirty && folio_test_dirty(folio)) { + inode_dec_dirty_pages(inode); + f2fs_remove_dirty_inode(inode); + folio_cancel_dirty(folio); + } + } return; + } =20 if (folio_test_dirty(folio)) { if (inode->i_ino =3D=3D F2FS_META_INO(sbi)) { --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8B59E3F1AD7 for ; Wed, 26 Aug 2026 13:10:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.6 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787749806; cv=none; b=ie2PGNZOkTFIf2ezhccsKsoU6uRklJvsczuQBw/uXyvFJL15GPwi4W+kZZI5udjvPB7cMxUN2ByiREEEJpy4X/Sm8P1NQhuWLiWN2JZHW/YDy0C4pYmim69vzjp+HLXmupve6nh67nQKt1oyyy1FuhBaQ/8qvP9G+Ta1awur94c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787749806; c=relaxed/simple; bh=2e+ePTt7fAXSGB0chgyASrs/asZF1pXCIoSOUMvObIU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dZAPNXRJqwL/tqZa5IsNUDxeRtz9L1dM04ZCSbZFfk++MsZ56SSt+CS2/s6BpDSRe/1uK94tnZTk+nuyMYuGWMDooD77CnK+Pwy/zy+H8ADiWp1I9LXIRRaVrf3lkg/i1CggTbvG2qTSwqDNnoNsMDPPfYuDPNRdmyzeKoDIMeE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=lPIzxeKD; arc=none smtp.client-ip=117.135.210.6 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="lPIzxeKD" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=Xs BIZC7IR9A7r1RXPLN8NZNQ5js4QQye/w5nXDK3zng=; b=lPIzxeKD1l8InKp8ad tYXOp5UU7idKQ6+qk5VKhBcLPJ7VwxbCCMk52rmHG72sI+20v+2hugj0CWKWgeVh 1fq8+FMsklwqy5A2/CSYzWEyU/UlRvGM4xQ1TSI07OOkGfaPlYWXUsaDWDA0ZgaR lC7wnusgqHJUEr+grSwaifYx4= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-4 (Coremail) with SMTP id _____wB3P5N85Y5qx4G3Gg--.15517S2; Wed, 26 Aug 2026 21:09:17 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 11/14] f2fs: fix zeroing paths for large folios Date: Wed, 26 Aug 2026 21:09:12 +0800 Message-ID: <20260826130916.2231342-1-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wB3P5N85Y5qx4G3Gg--.15517S2 X-Coremail-Antispam: 1Uf129KBjvJXoWxWF4DKF1xAFy7Gr15CFW7Jwb_yoW7Jr45pF W5GasrGrs5Gr48ur1xXF1DZw1rK3s3Wr4UCFWxGasIya4Yq3s5KF18t34j9Fs8tr48Arna q3W8KFyrWF1UJF7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07UCQ6JUUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBsB3WpmqO5X1ULQAA3Y Content-Type: text/plain; charset="utf-8" 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 --- 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; =20 if (dn.data_blkaddr =3D=3D NEW_ADDR) { - folio_zero_segment(folio, 0, folio_size(folio)); - if (!folio_test_uptodate(folio)) + size_t off =3D 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); =20 @@ -2670,7 +2677,7 @@ static bool __ffs_mark_subrange_uptodate(struct folio= *folio, return bitmap_full(ffs->state, nr_subpages); } =20 -static void f2fs_ffs_mark_subrange_uptodate(struct folio *folio, size_t of= fset, +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, s= ize_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 =3D from & (PAGE_SIZE - 1); pgoff_t index =3D from >> PAGE_SHIFT; struct address_space *mapping =3D inode->i_mapping; struct folio *folio; + size_t folio_off; =20 - if (!offset && !cache_only) + if (!(from & (PAGE_SIZE - 1)) && !cache_only) return 0; =20 if (cache_only) { @@ -834,12 +834,21 @@ static int truncate_partial_data_page(struct inode *i= node, u64 from, return PTR_ERR(folio) =3D=3D -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 =3D offset_in_folio(folio, from); + folio_zero_segment(folio, folio_off, folio_size(folio)); =20 /* 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 ind= ex, struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); struct folio *folio; struct f2fs_lock_context lc; + size_t folio_off; =20 if (!len) return 0; @@ -1324,7 +1334,14 @@ static int fill_zero(struct inode *inode, pgoff_t in= dex, return PTR_ERR(folio); =20 f2fs_folio_wait_writeback(folio, DATA, true, true); - folio_zero_range(folio, start, len); + folio_off =3D 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; --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0650B3C2BA4 for ; Wed, 26 Aug 2026 13:10:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.8 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787749805; cv=none; b=GLNpcCh70wAHuw4psDyHHu+oPU3zjX7J3pExPw2LIrZZ9CsGn2gtEmtDMXamf3IiOatICyLD11p7OeGgrWd8Tef3FoJgdeGosxHdwyXV2quKNNb0VvxkoS4aqjgowzP91LHDJZrR7ybGHqhDS9Z90OAIuDhrw9yrLfx9qfwlVZc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787749805; c=relaxed/simple; bh=LQq2bbrpK3S5Agc2rjRfvdEQJmcm17YwgmSXKWGHcdc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uQRU+LD3Ko9bMo2mkocNETdKDRFsbsdD2RT2Uy4OBfxEYB7fJOKEY+Mstl6AMk2uaPCF8xC4L2gxJ6JXaXYUC9BkP6iB5yndyrnmkcB+u7/zBIusJt1ZWwkz/lDGjP14AwijivdaTbEMBqzm6+hVDXR5MVQzHXIZnXaVp4F+8Lg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=PWbAbtyF; arc=none smtp.client-ip=117.135.210.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="PWbAbtyF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=6v N/j7vfxBIgKWR2Ht5PrFZDmAgFaU/24vLDpliFyBE=; b=PWbAbtyFNE3RE+mBP0 TrCRuCrH0fI387IjXxD8Y/KvYAnQnlAxNF/uNbCFLjVPvGaFt0YzAo3MlLIUKiSm +CIsqtH+xPOXSHSZXgFMG6do1883xEoTs6StL8XJa7Wl+lkbt6FNQa2sdgM43BD7 3OvIha1Z9FKfI5nL3bfDN2exw= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-4 (Coremail) with SMTP id _____wB3P5N85Y5qx4G3Gg--.15517S3; Wed, 26 Aug 2026 21:09:18 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 12/14] f2fs: handle block cloning within the same large folio Date: Wed, 26 Aug 2026 21:09:13 +0800 Message-ID: <20260826130916.2231342-2-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wB3P5N85Y5qx4G3Gg--.15517S3 X-Coremail-Antispam: 1Uf129KBjvJXoWxXF4UWFyUAry7AF4UtFW5Wrg_yoW5tFWkpF y5GrZ8Z3ykCFyxXr1ftF47ZryrKrZ7Ww4UJFs7u3s3C3W5X3Z3Kw4Iqw4j9F1F9rWUA3y8 XryUJFy8uF1UXFJanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07jjmhwUUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBsB7WpmqO5X5UMQAA3E Content-Type: text/plain; charset="utf-8" With large folios, the source and destination block in __clone_blkaddrs() can belong to the same folio during same-inode operations such as collapse range and insert range. In that case, locking the source folio and then looking up the destination folio can try to lock the same folio again. It also copies from and to offset 0, which is only correct for order-0 folios. Detect the same-inode, same-folio case before looking up the destination folio. Then in this case,we reserve the destination block explicitly. Signed-off-by: Nanzhe Zhao --- fs/f2fs/file.c | 76 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4272013dbe38..b82acbc3240f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1581,25 +1581,77 @@ static int __clone_blkaddrs(struct inode *src_inode= , struct inode *dst_inode, f2fs_put_dnode(&dn); } else { struct folio *fsrc, *fdst; + size_t src_off, dst_off; =20 fsrc =3D f2fs_get_lock_data_folio(src_inode, src + i, true); if (IS_ERR(fsrc)) return PTR_ERR(fsrc); - fdst =3D f2fs_get_new_data_folio(dst_inode, NULL, dst + i, - true); - if (IS_ERR(fdst)) { - f2fs_folio_put(fsrc, true); - return PTR_ERR(fdst); - } =20 - f2fs_folio_wait_writeback(fdst, DATA, true, true); + src_off =3D offset_in_folio(fsrc, + (loff_t)(src + i) << PAGE_SHIFT); + + /* + * For same-inode operations (collapse/insert), src and + * dst may fall within the same large folio. Detect this + * to avoid self-deadlock on folio lock. + */ + if (src_inode =3D=3D dst_inode && + folio_contains(fsrc, dst + i)) { + struct dnode_of_data dn; + + /* Reserve block for dst before copying */ + set_new_dnode(&dn, dst_inode, NULL, NULL, 0); + ret =3D f2fs_reserve_block(&dn, dst + i); + if (ret) { + f2fs_folio_put(fsrc, true); + return ret; + } + + dst_off =3D offset_in_folio(fsrc, + (loff_t)(dst + i) << PAGE_SHIFT); + f2fs_folio_wait_writeback(fsrc, DATA, true, true); + memcpy_folio(fsrc, dst_off, fsrc, src_off, + PAGE_SIZE); + if (folio_test_large(fsrc)) { + f2fs_ffs_find_or_alloc(fsrc); + f2fs_ffs_mark_subrange_uptodate(fsrc, + dst_off, PAGE_SIZE); + f2fs_ffs_mark_subrange_dirty(fsrc, dst_off, + PAGE_SIZE); + } + folio_mark_dirty(fsrc); + if (i_size_read(dst_inode) < + ((loff_t)(dst + i + 1) << PAGE_SHIFT)) + f2fs_i_size_write(dst_inode, + ((loff_t)(dst + i + 1) << PAGE_SHIFT)); + folio_set_f2fs_gcing(fsrc); + f2fs_folio_put(fsrc, true); + } else { + fdst =3D f2fs_get_new_data_folio(dst_inode, NULL, + dst + i, true); + if (IS_ERR(fdst)) { + f2fs_folio_put(fsrc, true); + return PTR_ERR(fdst); + } =20 - memcpy_folio(fdst, 0, fsrc, 0, PAGE_SIZE); - folio_mark_dirty(fdst); - folio_set_f2fs_gcing(fdst); - f2fs_folio_put(fdst, true); - f2fs_folio_put(fsrc, true); + dst_off =3D offset_in_folio(fdst, + (loff_t)(dst + i) << PAGE_SHIFT); + f2fs_folio_wait_writeback(fdst, DATA, true, true); + memcpy_folio(fdst, dst_off, fsrc, src_off, + PAGE_SIZE); + if (folio_test_large(fdst)) { + f2fs_ffs_find_or_alloc(fdst); + f2fs_ffs_mark_subrange_uptodate(fdst, + dst_off, PAGE_SIZE); + f2fs_ffs_mark_subrange_dirty(fdst, dst_off, + PAGE_SIZE); + } + folio_mark_dirty(fdst); + folio_set_f2fs_gcing(fdst); + f2fs_folio_put(fdst, true); + f2fs_folio_put(fsrc, true); + } =20 ret =3D f2fs_truncate_hole(src_inode, src + i, src + i + 1); --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B5D033822B1 for ; Wed, 26 Aug 2026 13:10:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.8 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787749805; cv=none; b=Qe1r4HKiYKSNzPrqW8kz4w5HKK2DuG+oKpgGxMAdVp6hKyTfX9dLd6OxWgZTM3NGWeP68Aaed1qiUg5u7QnHhCyZne3dLpefcXJy0pB7gal1B1gKjmmaZshDqLrXzDXH8Riqtzr0//sDBh5/LzY/qarjY+IiFjcX4/KUdBvRiWc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787749805; c=relaxed/simple; bh=UVKr1GB3R6vP+7Rlv6Fyg7g9JqBgpeXH27thzrWTT1s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=USJIWJfvD5tIlMXmt5MzacKZdjLT0X2afQ6Z5dLWGRYKnckM5hkeBAqa5/cN4hs28ZgYmRZmlKlFFo3zei0HNY1sXuHnMtXW/hwzK/Kd0ro2iq6M9bKgasbmqoRLqsB3tRgRNb9xEVotMMFMb4FJmZm/JRZmJ++yA743zbbBxl8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=OwAEA4po; arc=none smtp.client-ip=117.135.210.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="OwAEA4po" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=sT xI5UdO0rtdKSksJfWZZPHZ6Bbg4ffM5X8VWwPtsHg=; b=OwAEA4poKDftAzbuCg Y6KGhngaRK9IbIsoTobhlpRLw/XTUQ3NsXogeORNEXMyDKOl59J500cSzc4hctnh VBI0GOmXhfmSEkOWA3hsHr1hkhNSOH/wxq38E1ZOJoLSmQcsaA2n6U/eEIc2K1zp B3xz1UAyM3ipRN0KKCp3bo1vk= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-4 (Coremail) with SMTP id _____wB3P5N85Y5qx4G3Gg--.15517S4; Wed, 26 Aug 2026 21:09:18 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 13/14] f2fs: allow large folio support to writeable files Date: Wed, 26 Aug 2026 21:09:14 +0800 Message-ID: <20260826130916.2231342-3-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wB3P5N85Y5qx4G3Gg--.15517S4 X-Coremail-Antispam: 1Uf129KBjvJXoWxXr1fXr43CF48JF1UtF4fZrb_yoWrKFykpF Z8Gr1rGr4DuFyYgr1ktFs0vr1Yva4xW3yUAa97Aws5A3WDX34SgF1Ika45uF45trW8Jw4I qF40kryUWr15GaDanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07j5Ma8UUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBsR-WpmqO5X9YmAAA3g Content-Type: text/plain; charset="utf-8" Now we make all write path support large folios, so we open permission to let writeable file set large folio mapping. Keep fs-layer encrypted files excluded unless inline encryption is enabled, since f2fs_encrypt_one_page() still encrypts one PAGE_SIZE page and cannot handle large folios. Signed-off-by: Nanzhe Zhao --- fs/f2fs/data.c | 9 ++++++++- fs/f2fs/f2fs.h | 13 +++++++++++++ fs/f2fs/file.c | 16 ---------------- fs/f2fs/inode.c | 4 +--- fs/f2fs/namei.c | 1 + 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0b167b14a9a5..2e20833c6417 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2955,7 +2955,7 @@ static int f2fs_read_data_large_folio(struct inode *i= node, int ret =3D 0; bool folio_in_bio =3D false; =20 - if (!IS_IMMUTABLE(inode) || f2fs_compressed_file(inode)) { + if (f2fs_compressed_file(inode)) { if (folio) folio_unlock(folio); return -EOPNOTSUPP; @@ -3321,6 +3321,13 @@ int f2fs_encrypt_one_page(struct f2fs_io_info *fio) if (fscrypt_inode_uses_inline_crypto(inode)) return 0; =20 + if (folio_test_large(page_folio(page))) { + f2fs_warn_ratelimited(F2FS_I_SB(inode), + "large folio does not support fs-layer encryption, ino=3D%llu", + (unsigned long long)inode->i_ino); + return -EOPNOTSUPP; + } + fio->encrypted_page =3D fscrypt_encrypt_pagecache_blocks(page_folio(page), PAGE_SIZE, 0, GFP_NOFS); if (IS_ERR(fio->encrypted_page)) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6ae249bf9aa1..04ed0ea92b26 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -5201,6 +5201,19 @@ static inline bool f2fs_quota_file(struct f2fs_sb_in= fo *sbi, nid_t ino) return false; } =20 +static inline void f2fs_mapping_set_large_folio(struct inode *inode) +{ + if (!S_ISREG(inode->i_mode) || + f2fs_has_inline_data(inode) || + f2fs_compressed_file(inode) || + f2fs_quota_file(F2FS_I_SB(inode), inode->i_ino) || + (f2fs_encrypted_file(inode) && + !(inode->i_sb->s_flags & SB_INLINECRYPT))) + return; + + mapping_set_folio_min_order(inode->i_mapping, 0); +} + static inline bool f2fs_block_unit_discard(struct f2fs_sb_info *sbi) { return F2FS_OPTION(sbi).discard_unit =3D=3D DISCARD_UNIT_BLOCK; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index b82acbc3240f..c8426821e701 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -134,17 +134,6 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault= *vmf) int err =3D 0; vm_fault_t ret; =20 - /* - * We only support large folio on the read case. - * Don't make any dirty pages. - */ - if (unlikely(IS_IMMUTABLE(inode)) || - mapping_large_folio_support(inode->i_mapping)) { - f2fs_err(sbi, "Not expected: immutable: %d large_folio: %d", - IS_IMMUTABLE(inode), - mapping_large_folio_support(inode->i_mapping)); - return VM_FAULT_SIGBUS; - } =20 if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) { err =3D -EIO; @@ -698,9 +687,6 @@ static int f2fs_file_open(struct inode *inode, struct f= ile *filp) if (!f2fs_is_compress_backend_ready(inode)) return -EOPNOTSUPP; =20 - if (mapping_large_folio_support(inode->i_mapping) && - filp->f_mode & FMODE_WRITE) - return -EOPNOTSUPP; =20 err =3D fsverity_file_open(inode, filp); if (err) @@ -1177,8 +1163,6 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dent= ry *dentry, return -EPERM; =20 if ((attr->ia_valid & ATTR_SIZE)) { - if (mapping_large_folio_support(inode->i_mapping)) - return -EOPNOTSUPP; if (IS_DEVICE_ALIASING(inode)) return -EPERM; if (!f2fs_is_compress_backend_ready(inode)) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 96cc0e777567..7c4b5cdfe276 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -642,9 +642,7 @@ struct inode *f2fs_iget(struct super_block *sb, unsigne= d long ino) inode->i_op =3D &f2fs_file_inode_operations; inode->i_fop =3D &f2fs_file_operations; inode->i_mapping->a_ops =3D &f2fs_dblock_aops; - if (IS_IMMUTABLE(inode) && !f2fs_compressed_file(inode) && - !f2fs_quota_file(sbi, inode->i_ino)) - mapping_set_folio_min_order(inode->i_mapping, 0); + f2fs_mapping_set_large_folio(inode); } else if (S_ISDIR(inode->i_mode)) { inode->i_op =3D &f2fs_dir_inode_operations; inode->i_fop =3D &f2fs_dir_operations; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index afaab7739283..bdf4e586bfcf 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -341,6 +341,7 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *i= dmap, f2fs_set_inode_flags(inode); =20 f2fs_init_extent_tree(inode); + f2fs_mapping_set_large_folio(inode); =20 trace_f2fs_new_inode(inode, 0); return inode; --=20 2.43.0 From nobody Mon Sep 28 05:45:36 2026 Received: from m16.mail.126.com (m16.mail.126.com [117.135.210.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 438793DA5C7 for ; Wed, 26 Aug 2026 13:10:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787749806; cv=none; b=aPLLBzsqh5NyTfgVlGmx8HsuBoUw3Xq/AlRk34nRB2zMITdIdAMKgpALDXPqEo6mKDp3LGxIdEnG54q4n9Z1xwXPKTZVYyTWTFuNneAq79ON8fAq3uHxbk1W+RzYjtFagdaxba5FDH0MzNO9ZrfAFKZxT1By5jRRaqtysfOyoVU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787749806; c=relaxed/simple; bh=OSP6XW7ojsv6vtfB2+FqCcMXN0Ss/bmzysMjgxGZ0dM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=G9rFpcLG1zNrpXuONmY2+oCQbHMsg190dNNuGPqWOI9RmVTkiopuCl1inrYHvaCpVeyrF+q/lGi68y50aj4zbtTGNBu64sDlmasCd9P/933zfI8US9uwSNl5rUZb7S2eztQqxvqbLR9QlTwHe0iAJYUqLFDVbEv2kmB3Tqx5P+8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com; spf=pass smtp.mailfrom=126.com; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b=JRRbX9XS; arc=none smtp.client-ip=117.135.210.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=xiaomi.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=126.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=126.com header.i=@126.com header.b="JRRbX9XS" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=126.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=HB 8vD9Vghuu1IqwMhGeSD/BVqt+ianc/vWTayOF09mg=; b=JRRbX9XSxud2L0P30k ZonoXSo4ojNeZmJT+OX252a7M/igxPQOqZaqmIbBzmPXkH6Mbkb1LQvMF8Mbn7Vi o5eg2QvOuA5kbjQ5XEBSTl79EMwwUUCLeV22DD6drCdrbAlWAtbUcrqS4EzYmsDA VbQt4JRRwhRpYGWEban9kvNdg= Received: from nzzhao-ThinkCentre-M760t.mioffice.cn (unknown []) by gzga-smtp-mtada-g0-4 (Coremail) with SMTP id _____wB3P5N85Y5qx4G3Gg--.15517S5; Wed, 26 Aug 2026 21:09:19 +0800 (CST) From: Nanzhe Zhao To: linux-f2fs-devel@lists.sourceforge.net, Jaegeuk Kim , Chao Yu Cc: Barry Song , Nanzhe Zhao , Juan Yescas , Dev Jain , linux-kernel@vger.kernel.org, David Hildenbrand , Bo Zhang , Kalesh Singh , Ryan Roberts , Pengfei Li Subject: [PATCH 14/14] f2fs: make compressed files compatible with large folio Date: Wed, 26 Aug 2026 21:09:15 +0800 Message-ID: <20260826130916.2231342-4-zhaonanzhe@xiaomi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> References: <20260826082641.2007658-1-zhaonanzhe@xiaomi.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wB3P5N85Y5qx4G3Gg--.15517S5 X-Coremail-Antispam: 1Uf129KBjvJXoW7Zr43KrW7Aw4kJFWDtF4DXFb_yoW8Jw48pr WUGryrKrZ5Xa4j9w18Ja1qgr1YyFykGay7ZFWxGw18AF15Jw1F9FyxK3WS9FWUKrZ3Z3Wv va15KFy8Xan5taUanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07joc_fUUUUU= Sender: nzzhao@126.com X-CM-SenderInfo: xq22xtbr6rjloofrz/xtbBsR-WpmqO5X9YngAA3m Content-Type: text/plain; charset="utf-8" The compression flag is the hint indicates that the inode can be compressed, when the inode is using large folio, we expected it keeps using the large folio read/write paths and its data stays uncompressed on disk until the inode is evicted and re-read Let f2fs_write_begin() skip the compression overwrite preparation for such inodes and remove the compressed-file gate in f2fs_read_data_large_folio() so the data is simply read/written as regular blocks. Signed-off-by: Nanzhe Zhao --- fs/f2fs/data.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 2e20833c6417..befcdc3742f6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2955,12 +2955,6 @@ static int f2fs_read_data_large_folio(struct inode *= inode, int ret =3D 0; bool folio_in_bio =3D false; =20 - if (f2fs_compressed_file(inode)) { - if (folio) - folio_unlock(folio); - return -EOPNOTSUPP; - } - map.m_seg_type =3D NO_CHECK_TYPE; =20 if (rac) @@ -4935,7 +4929,8 @@ static int f2fs_write_begin(const struct kiocb *iocb, } =20 #ifdef CONFIG_F2FS_FS_COMPRESSION - if (f2fs_compressed_file(inode)) { + if (f2fs_compressed_file(inode) && + !mapping_large_folio_support(inode->i_mapping)) { int ret; struct page *page; =20 --=20 2.43.0