Skip to contentSkip to conversation

FAILED: patch "[PATCH] f2fs: fix to migrate all curseg types during" failed to apply to 6.6-stable tree

PATCH New [email protected] 2 participants 5 messages Sep 9 – Sep 11

Subsystems (2)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 8ec06f50ddd8d201bd7e55b896ae28ed9d4cb7d1
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090946-clumsily-stuck-0ea2@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'

Possible dependencies:

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From 8ec06f50ddd8d201bd7e55b896ae28ed9d4cb7d1 Mon Sep 17 00:00:00 2001
From: Daeho Jeong <[email protected]>
Date: Wed, 19 Aug 2026 11:06:35 -0700
Subject: [PATCH] f2fs: fix to migrate all curseg types during
free_segment_range

In free_segment_range(), the curseg evacuation loop only iterates up to NR_CURSEG_PERSIST_TYPE (0..5), missing non-persistent in-memory curseg types such as CURSEG_COLD_DATA_PINNED and CURSEG_ALL_DATA_ATGC.

Even though these in-memory curseg types are not saved in the on-disk checkpoint header, they still occupy active physical segments at runtime. If an active in-memory curseg happens to be allocated within the segment range being truncated during filesystem shrink, failing to evacuate it will cause subsequent writes to the curseg attempting out-of-bounds I/O on the truncated storage range.

Fix this by expanding the curseg evacuation loop upper bound to NR_CURSEG_TYPE to ensure all active curseg types are safely migrated out of the target range.

Fixes d0b9e42ab615 ("f2fs: introduce inmem curseg")
fs/f2fs/gc.c +1 -1
index 3215630c353d..5917ee917d87 100644
@@ -2240,7 +2240,7 @@ static int free_segment_range(struct f2fs_sb_info *sbi,
 	f2fs_reset_gc_victim_resource(sbi, start, end);
 
 	/* Move out cursegs from the target range */
-	for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++) {
+	for (type = CURSEG_HOT_DATA; type < NR_CURSEG_TYPE; type++) {
 		err = f2fs_allocate_segment_for_resize(sbi, type, start, end);
 		if (err)
 			goto out;
maintainer
#1 lore raw

PATCH 1/4 f2fs: delete f2fs_get_new_segment() declaration

From: Chao Yu <[email protected]>

[ Upstream commit e24e8333d0f3110473c6e6a20afd51911984e901 ]

Commit 093749e296e2 ("f2fs: support age threshold based garbage collection") added this declaration, but w/ definition, delete it.

Stable-dep-of: 8ec06f50ddd8 ("f2fs: fix to migrate all curseg types during free_segment_range")

---
 fs/f2fs/f2fs.h | 2 --
 1 file changed, 2 deletions(-)
fs/f2fs/f2fs.h +0 -2
index 63d3450f895ee..7ad3c976a773f 100644
@@ -3737,8 +3737,6 @@ bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno);
 void f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi);
 void f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi);
 void f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi);
-void f2fs_get_new_segment(struct f2fs_sb_info *sbi,
-			unsigned int *newseg, bool new_sec, int dir);
 void f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type,
 					unsigned int start, unsigned int end);
 int f2fs_allocate_new_section(struct f2fs_sb_info *sbi, int type, bool force);
Signature · 1 line
2.53.0
maintainer
#2 lore raw

PATCH 2/4 f2fs: fix to handle segment allocation failure correctly

From: Chao Yu <[email protected]>

[ Upstream commit 7d009e048d7cfcc21d400f2aba4c8bacbdebbd47 ]

If CONFIG_F2FS_CHECK_FS is off, and for very rare corner case that we run out of free segment, we should not panic kernel, instead, let's handle such error correctly in its caller.

Stable-dep-of: 8ec06f50ddd8 ("f2fs: fix to migrate all curseg types during free_segment_range")

---
 fs/f2fs/data.c    |  7 +++++--
 fs/f2fs/f2fs.h    |  2 +-
 fs/f2fs/file.c    |  7 ++++++-
 fs/f2fs/gc.c      |  7 ++++++-
 fs/f2fs/segment.c | 46 +++++++++++++++++++++++++++++++++++++++-------
 5 files changed, 57 insertions(+), 12 deletions(-)
fs/f2fs/data.c +5 -2
index d068111436d79..51b31dcf9ec6b 100644
@@ -1453,8 +1453,11 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type)
 
 	set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
 	old_blkaddr = dn->data_blkaddr;
-	f2fs_allocate_data_block(sbi, NULL, old_blkaddr, &dn->data_blkaddr,
-				&sum, seg_type, NULL);
+	err = f2fs_allocate_data_block(sbi, NULL, old_blkaddr,
+				&dn->data_blkaddr, &sum, seg_type, NULL);
+	if (err)
+		return err;
+
 	if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
 		f2fs_invalidate_internal_cache(sbi, old_blkaddr);
 
fs/f2fs/f2fs.h +1 -1
index 7ad3c976a773f..629b1e5d2f6ca 100644
@@ -3762,7 +3762,7 @@ void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
 			block_t old_addr, block_t new_addr,
 			unsigned char version, bool recover_curseg,
 			bool recover_newaddr);
-void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
+int f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
 			block_t old_blkaddr, block_t *new_blkaddr,
 			struct f2fs_summary *sum, int type,
 			struct f2fs_io_info *fio);
fs/f2fs/file.c +6 -1
index d321a42f97c89..41f50236a058f 100644
@@ -2363,8 +2363,11 @@ int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
 	case F2FS_GOING_DOWN_METASYNC:
 		/* do checkpoint only */
 		ret = f2fs_sync_fs(sb, 1);
-		if (ret)
+		if (ret) {
+			if (ret == -EIO)
+				ret = 0;
 			goto out;
+		}
 		f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
 		break;
 	case F2FS_GOING_DOWN_NOSYNC:
@@ -2380,6 +2383,8 @@ int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
 		set_sbi_flag(sbi, SBI_IS_DIRTY);
 		/* do checkpoint only */
 		ret = f2fs_sync_fs(sb, 1);
+		if (ret == -EIO)
+			ret = 0;
 		goto out;
 	default:
 		ret = -EINVAL;
fs/f2fs/gc.c +6 -1
index ac21950f53cd2..ec68bc87ef008 100644
@@ -1397,8 +1397,13 @@ static int move_data_block(struct inode *inode, block_t bidx,
 	set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
 
 	/* allocate block address */
-	f2fs_allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
+	err = f2fs_allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
 				&sum, type, NULL);
+	if (err) {
+		f2fs_put_page(mpage, 1);
+		/* filesystem should shutdown, no need to recovery block */
+		goto up_out;
+	}
 
 	fio.encrypted_page = f2fs_pagecache_get_page(META_MAPPING(fio.sbi),
 				newaddr, FGP_LOCK | FGP_CREAT, GFP_NOFS);
fs/f2fs/segment.c +39 -7

113 lines not on this page · raw message

Signature · 1 line
2.53.0
maintainer
#3 lore raw

PATCH 3/4 f2fs: fix to handle error paths of {new,change}_curseg()

In reply to Sasha Levin

From: Zhiguo Niu <[email protected]>

[ Upstream commit 245930617c9bd85330c78e1a70775e1f61b12f7a ]

{new,change}_curseg() may return error in some special cases, error handling should be did in their callers, and this will also facilitate subsequent error path expansion in {new,change}_curseg().

For the 6.6 backport, retain reset_checkpoint at its existing location so recovery-skipping paths still initialize the in-memory cursegs. Check any outstanding recovery error before assigning the initializer's return value; otherwise a successful initialization could mask that error. Route initialization failures through the existing sync_free_meta cleanup.

Keep the resize allocation error propagation and loop structure needed by 8ec06f50ddd8 ("f2fs: fix to migrate all curseg types during free_segment_range"). No functions are added.

Stable-dep-of: 8ec06f50ddd8 ("f2fs: fix to migrate all curseg types during free_segment_range")

---
 fs/f2fs/f2fs.h    |  4 ++--
 fs/f2fs/gc.c      |  7 ++++--
 fs/f2fs/segment.c | 57 +++++++++++++++++++++++++++++------------------
 fs/f2fs/super.c   |  6 ++++-
 4 files changed, 47 insertions(+), 27 deletions(-)
fs/f2fs/f2fs.h +2 -2
index 629b1e5d2f6ca..298332e26d031 100644
@@ -3734,10 +3734,10 @@ int f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable);
 void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi);
 int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra);
 bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno);
-void f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi);
+int f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi);
 void f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi);
 void f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi);
-void f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type,
+int f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type,
 					unsigned int start, unsigned int end);
 int f2fs_allocate_new_section(struct f2fs_sb_info *sbi, int type, bool force);
 int f2fs_allocate_pinning_section(struct f2fs_sb_info *sbi);
fs/f2fs/gc.c +5 -2
index ec68bc87ef008..115b6d6c6bc7a 100644
@@ -2103,8 +2103,11 @@ static int free_segment_range(struct f2fs_sb_info *sbi,
 	mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
 
 	/* Move out cursegs from the target range */
-	for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++)
-		f2fs_allocate_segment_for_resize(sbi, type, start, end);
+	for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++) {
+		err = f2fs_allocate_segment_for_resize(sbi, type, start, end);
+		if (err)
+			goto out;
+	}
 
 	/* do GC to move out valid blocks in the range */
 	err = f2fs_gc_range(sbi, start, end, dry_run, 0);
fs/f2fs/segment.c +35 -22

184 lines not on this page · raw message

fs/f2fs/super.c +5 -1
index 95adc2c4c3c08..3d7e987bf5f1a 100644
@@ -4729,7 +4729,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	}
 
 reset_checkpoint:
-	f2fs_init_inmem_curseg(sbi);
+	if (err)
+		goto free_meta;
+	err = f2fs_init_inmem_curseg(sbi);
+	if (err)
+		goto sync_free_meta;
 
 	/* f2fs_recover_fsync_data() cleared this already */
 	clear_sbi_flag(sbi, SBI_POR_DOING);
Signature · 1 line
2.53.0
maintainer
#4 lore raw

PATCH 4/4 f2fs: fix to migrate all curseg types during free_segment_range

In reply to Sasha Levin

From: Daeho Jeong <[email protected]>

[ Upstream commit 8ec06f50ddd8d201bd7e55b896ae28ed9d4cb7d1 ]

In free_segment_range(), the curseg evacuation loop only iterates up to NR_CURSEG_PERSIST_TYPE (0..5), missing non-persistent in-memory curseg types such as CURSEG_COLD_DATA_PINNED and CURSEG_ALL_DATA_ATGC.

Even though these in-memory curseg types are not saved in the on-disk checkpoint header, they still occupy active physical segments at runtime. If an active in-memory curseg happens to be allocated within the segment range being truncated during filesystem shrink, failing to evacuate it will cause subsequent writes to the curseg attempting out-of-bounds I/O on the truncated storage range.

Fix this by expanding the curseg evacuation loop upper bound to NR_CURSEG_TYPE to ensure all active curseg types are safely migrated out of the target range.

Fixes d0b9e42ab615 ("f2fs: introduce inmem curseg")
---
 fs/f2fs/gc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/gc.c +1 -1
index 115b6d6c6bc7a..edeb111c47569 100644
@@ -2103,7 +2103,7 @@ static int free_segment_range(struct f2fs_sb_info *sbi,
 	mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
 
 	/* Move out cursegs from the target range */
-	for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++) {
+	for (type = CURSEG_HOT_DATA; type < NR_CURSEG_TYPE; type++) {
 		err = f2fs_allocate_segment_for_resize(sbi, type, start, end);
 		if (err)
 			goto out;
Signature · 1 line
2.53.0