* FAILED: patch "[PATCH] f2fs: fix to avoid potential deadloop in" failed to apply to 6.6-stable tree
@ 2026-09-09 11:26 gregkh
2026-09-12 11:17 ` [PATCH 6.6.y 1/6] f2fs: Add f2fs_new_node_folio() Sasha Levin
0 siblings, 1 reply; 7+ messages in thread
From: gregkh @ 2026-09-09 11:26 UTC (permalink / raw)
To: chao, jaegeuk; +Cc: stable
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 <stable@vger.kernel.org>.
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 ce366bfa821ec81dd45bde547ee31e659306cc61
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026090957-spectacle-juniper-fbdf@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From ce366bfa821ec81dd45bde547ee31e659306cc61 Mon Sep 17 00:00:00 2001
From: Chao Yu <chao@kernel.org>
Date: Mon, 24 Aug 2026 21:17:29 +0800
Subject: [PATCH] f2fs: fix to avoid potential deadloop in
f2fs_fsync_node_pages()
There is potential deadloop in race condition:
Thread A Thread B
- fsync
- f2fs_do_sync_file
- f2fs_fsync_node_pages
- last_fsync_dnode
- folio_get(last_folio)
- f2fs_setattr
- f2fs_truncate
- f2fs_truncate_blocks
- f2fs_do_truncate_blocks
- f2fs_truncate_inode_blocks
- truncate_dnode
- truncate_node
- invalidate_mapping_pages
- folio->mapping = NULL
- is_node_folio alwasy return false
- atomic && !marked is always true,
then goto retry
Cc: stable@kernel.org
Fixes: 608514deba38 ("f2fs: set fsync mark only for the last dnode")
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 968e5ed38816..86c2e67e43b6 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -2016,6 +2016,11 @@ int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
f2fs_debug(sbi, "Retry to write fsync mark: ino=%u, idx=%lx",
ino, last_folio->index);
folio_lock(last_folio);
+ if (unlikely(!is_node_folio(last_folio))) {
+ f2fs_folio_put(last_folio, true);
+ ret = -EAGAIN;
+ goto out;
+ }
f2fs_folio_wait_writeback(last_folio, NODE, true, true);
folio_mark_dirty(last_folio);
folio_unlock(last_folio);
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 6.6.y 1/6] f2fs: Add f2fs_new_node_folio()
2026-09-09 11:26 FAILED: patch "[PATCH] f2fs: fix to avoid potential deadloop in" failed to apply to 6.6-stable tree gregkh
@ 2026-09-12 11:17 ` Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 2/6] f2fs: Add f2fs_get_node_folio() Sasha Levin
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Sasha Levin @ 2026-09-12 11:17 UTC (permalink / raw)
To: stable; +Cc: Matthew Wilcox (Oracle), Chao Yu, Jaegeuk Kim, Sasha Levin
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
[ Upstream commit cfa809146f8223c2953e7553f830c0877921f9de ]
Convert f2fs_new_node_page() to f2fs_new_node_folio() and add
a compatibility wrapper. Removes five hidden calls to compound_head().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Stable-dep-of: ce366bfa821e ("f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/f2fs.h | 8 +++++++-
fs/f2fs/node.c | 28 ++++++++++++++--------------
2 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 63d3450f895ee..6bcc60b02bd00 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3680,7 +3680,7 @@ int f2fs_wait_on_node_pages_writeback(struct f2fs_sb_info *sbi,
unsigned int seq_id);
int f2fs_remove_inode_page(struct inode *inode);
struct page *f2fs_new_inode_page(struct inode *inode);
-struct page *f2fs_new_node_page(struct dnode_of_data *dn, unsigned int ofs);
+struct folio *f2fs_new_node_folio(struct dnode_of_data *dn, unsigned int ofs);
void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid);
struct page *f2fs_get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid);
struct page *f2fs_get_node_page_ra(struct page *parent, int start);
@@ -3708,6 +3708,12 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi);
int __init f2fs_create_node_manager_caches(void);
void f2fs_destroy_node_manager_caches(void);
+static inline
+struct page *f2fs_new_node_page(struct dnode_of_data *dn, unsigned int ofs)
+{
+ return &f2fs_new_node_folio(dn, ofs)->page;
+}
+
/*
* segment.c
*/
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 586a90643ddd2..bf4bf31b73416 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1326,19 +1326,19 @@ struct page *f2fs_new_inode_page(struct inode *inode)
return f2fs_new_node_page(&dn, 0);
}
-struct page *f2fs_new_node_page(struct dnode_of_data *dn, unsigned int ofs)
+struct folio *f2fs_new_node_folio(struct dnode_of_data *dn, unsigned int ofs)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
struct node_info new_ni;
- struct page *page;
+ struct folio *folio;
int err;
if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC)))
return ERR_PTR(-EPERM);
- page = f2fs_grab_cache_page(NODE_MAPPING(sbi), dn->nid, false);
- if (!page)
- return ERR_PTR(-ENOMEM);
+ folio = f2fs_grab_cache_folio(NODE_MAPPING(sbi), dn->nid, false);
+ if (IS_ERR(folio))
+ return folio;
if (unlikely((err = inc_valid_node_count(sbi, dn->inode, !ofs))))
goto fail;
@@ -1369,12 +1369,12 @@ struct page *f2fs_new_node_page(struct dnode_of_data *dn, unsigned int ofs)
new_ni.version = 0;
set_node_addr(sbi, &new_ni, NEW_ADDR, false);
- f2fs_wait_on_page_writeback(page, NODE, true, true);
- fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true);
- set_cold_node(page, S_ISDIR(dn->inode->i_mode));
- if (!PageUptodate(page))
- SetPageUptodate(page);
- if (set_page_dirty(page))
+ f2fs_folio_wait_writeback(folio, NODE, true, true);
+ fill_node_footer(&folio->page, dn->nid, dn->inode->i_ino, ofs, true);
+ set_cold_node(&folio->page, S_ISDIR(dn->inode->i_mode));
+ if (!folio_test_uptodate(folio))
+ folio_mark_uptodate(folio);
+ if (folio_mark_dirty(folio))
dn->node_changed = true;
if (f2fs_has_xattr_block(ofs))
@@ -1382,10 +1382,10 @@ struct page *f2fs_new_node_page(struct dnode_of_data *dn, unsigned int ofs)
if (ofs == 0)
inc_valid_inode_count(sbi);
- return page;
+ return folio;
fail:
- clear_node_page_dirty(page);
- f2fs_put_page(page, 1);
+ clear_node_page_dirty(&folio->page);
+ f2fs_folio_put(folio, true);
return ERR_PTR(err);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 6.6.y 2/6] f2fs: Add f2fs_get_node_folio()
2026-09-12 11:17 ` [PATCH 6.6.y 1/6] f2fs: Add f2fs_new_node_folio() Sasha Levin
@ 2026-09-12 11:17 ` Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 3/6] f2fs: Use a folio in truncate_nodes() Sasha Levin
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-09-12 11:17 UTC (permalink / raw)
To: stable; +Cc: Matthew Wilcox (Oracle), Chao Yu, Jaegeuk Kim, Sasha Levin
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
[ Upstream commit aa220cede54ed671f7be413d8dacb91263a9d218 ]
The folio equivalent of f2fs_get_node_page().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Stable-dep-of: ce366bfa821e ("f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/f2fs.h | 1 +
fs/f2fs/node.c | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 6bcc60b02bd00..8f6c1079d48f0 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3682,6 +3682,7 @@ int f2fs_remove_inode_page(struct inode *inode);
struct page *f2fs_new_inode_page(struct inode *inode);
struct folio *f2fs_new_node_folio(struct dnode_of_data *dn, unsigned int ofs);
void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid);
+struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid);
struct page *f2fs_get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid);
struct page *f2fs_get_node_page_ra(struct page *parent, int start);
int f2fs_move_node_page(struct page *node_page, int gc_type);
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index bf4bf31b73416..0aff16c5e1b5d 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1524,6 +1524,11 @@ static struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid,
return ERR_PTR(err);
}
+struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid)
+{
+ return __get_node_folio(sbi, nid, NULL, 0, NODE_TYPE_REGULAR);
+}
+
struct page *f2fs_get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
{
return __get_node_page(sbi, nid, NULL, 0);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 6.6.y 3/6] f2fs: Use a folio in truncate_nodes()
2026-09-12 11:17 ` [PATCH 6.6.y 1/6] f2fs: Add f2fs_new_node_folio() Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 2/6] f2fs: Add f2fs_get_node_folio() Sasha Levin
@ 2026-09-12 11:17 ` Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 4/6] f2fs: Use folios in truncate_partial_nodes() Sasha Levin
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-09-12 11:17 UTC (permalink / raw)
To: stable; +Cc: Matthew Wilcox (Oracle), Chao Yu, Jaegeuk Kim, Sasha Levin
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
[ Upstream commit 878a05b09c0c578b679956431cd2be5cc3689403 ]
Remove two calls to compound_head().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Stable-dep-of: ce366bfa821e ("f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/node.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 0aff16c5e1b5d..c0de044fac304 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -998,7 +998,7 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
int ofs, int depth)
{
struct dnode_of_data rdn = *dn;
- struct page *page;
+ struct folio *folio;
struct f2fs_node *rn;
nid_t child_nid;
unsigned int child_nofs;
@@ -1010,15 +1010,15 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
trace_f2fs_truncate_nodes_enter(dn->inode, dn->nid, dn->data_blkaddr);
- page = f2fs_get_node_page(F2FS_I_SB(dn->inode), dn->nid);
- if (IS_ERR(page)) {
- trace_f2fs_truncate_nodes_exit(dn->inode, PTR_ERR(page));
- return PTR_ERR(page);
+ folio = f2fs_get_node_folio(F2FS_I_SB(dn->inode), dn->nid);
+ if (IS_ERR(folio)) {
+ trace_f2fs_truncate_nodes_exit(dn->inode, PTR_ERR(folio));
+ return PTR_ERR(folio);
}
- f2fs_ra_node_pages(page, ofs, NIDS_PER_BLOCK);
+ f2fs_ra_node_pages(&folio->page, ofs, NIDS_PER_BLOCK);
- rn = F2FS_NODE(page);
+ rn = F2FS_NODE(&folio->page);
if (depth < 3) {
for (i = ofs; i < NIDS_PER_BLOCK; i++, freed++) {
child_nid = le32_to_cpu(rn->in.nid[i]);
@@ -1028,7 +1028,7 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
ret = truncate_dnode(&rdn);
if (ret < 0)
goto out_err;
- if (set_nid(page, i, 0, false))
+ if (set_nid(&folio->page, i, 0, false))
dn->node_changed = true;
}
} else {
@@ -1042,7 +1042,7 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
rdn.nid = child_nid;
ret = truncate_nodes(&rdn, child_nofs, 0, depth - 1);
if (ret == (NIDS_PER_BLOCK + 1)) {
- if (set_nid(page, i, 0, false))
+ if (set_nid(&folio->page, i, 0, false))
dn->node_changed = true;
child_nofs += ret;
} else if (ret < 0 && ret != -ENOENT) {
@@ -1054,19 +1054,19 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
if (!ofs) {
/* remove current indirect node */
- dn->node_page = page;
+ dn->node_page = &folio->page;
ret = truncate_node(dn);
if (ret)
goto out_err;
freed++;
} else {
- f2fs_put_page(page, 1);
+ f2fs_folio_put(folio, true);
}
trace_f2fs_truncate_nodes_exit(dn->inode, freed);
return freed;
out_err:
- f2fs_put_page(page, 1);
+ f2fs_folio_put(folio, true);
trace_f2fs_truncate_nodes_exit(dn->inode, ret);
return ret;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 6.6.y 4/6] f2fs: Use folios in truncate_partial_nodes()
2026-09-12 11:17 ` [PATCH 6.6.y 1/6] f2fs: Add f2fs_new_node_folio() Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 2/6] f2fs: Add f2fs_get_node_folio() Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 3/6] f2fs: Use a folio in truncate_nodes() Sasha Levin
@ 2026-09-12 11:17 ` Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 5/6] f2fs: introduce is_{meta,node}_folio Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 6/6] f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages() Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-09-12 11:17 UTC (permalink / raw)
To: stable; +Cc: Matthew Wilcox (Oracle), Chao Yu, Jaegeuk Kim, Sasha Levin
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
[ Upstream commit d68833a0b74e46edc678340cdff5ad22782c6609 ]
Removes two calls to compound_head().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Stable-dep-of: ce366bfa821e ("f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/node.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index c0de044fac304..b6a57d3706722 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1074,7 +1074,7 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
static int truncate_partial_nodes(struct dnode_of_data *dn,
struct f2fs_inode *ri, int *offset, int depth)
{
- struct page *pages[2];
+ struct folio *folios[2];
nid_t nid[3];
nid_t child_nid;
int err = 0;
@@ -1088,45 +1088,45 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
/* get indirect nodes in the path */
for (i = 0; i < idx + 1; i++) {
/* reference count'll be increased */
- pages[i] = f2fs_get_node_page(F2FS_I_SB(dn->inode), nid[i]);
- if (IS_ERR(pages[i])) {
- err = PTR_ERR(pages[i]);
+ folios[i] = f2fs_get_node_folio(F2FS_I_SB(dn->inode), nid[i]);
+ if (IS_ERR(folios[i])) {
+ err = PTR_ERR(folios[i]);
idx = i - 1;
goto fail;
}
- nid[i + 1] = get_nid(pages[i], offset[i + 1], false);
+ nid[i + 1] = get_nid(&folios[i]->page, offset[i + 1], false);
}
- f2fs_ra_node_pages(pages[idx], offset[idx + 1], NIDS_PER_BLOCK);
+ f2fs_ra_node_pages(&folios[idx]->page, offset[idx + 1], NIDS_PER_BLOCK);
/* free direct nodes linked to a partial indirect node */
for (i = offset[idx + 1]; i < NIDS_PER_BLOCK; i++) {
- child_nid = get_nid(pages[idx], i, false);
+ child_nid = get_nid(&folios[idx]->page, i, false);
if (!child_nid)
continue;
dn->nid = child_nid;
err = truncate_dnode(dn);
if (err < 0)
goto fail;
- if (set_nid(pages[idx], i, 0, false))
+ if (set_nid(&folios[idx]->page, i, 0, false))
dn->node_changed = true;
}
if (offset[idx + 1] == 0) {
- dn->node_page = pages[idx];
+ dn->node_page = &folios[idx]->page;
dn->nid = nid[idx];
err = truncate_node(dn);
if (err)
goto fail;
} else {
- f2fs_put_page(pages[idx], 1);
+ f2fs_folio_put(folios[idx], true);
}
offset[idx]++;
offset[idx + 1] = 0;
idx--;
fail:
for (i = idx; i >= 0; i--)
- f2fs_put_page(pages[i], 1);
+ f2fs_folio_put(folios[i], true);
trace_f2fs_truncate_partial_nodes(dn->inode, nid, depth, err);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 6.6.y 5/6] f2fs: introduce is_{meta,node}_folio
2026-09-12 11:17 ` [PATCH 6.6.y 1/6] f2fs: Add f2fs_new_node_folio() Sasha Levin
` (2 preceding siblings ...)
2026-09-12 11:17 ` [PATCH 6.6.y 4/6] f2fs: Use folios in truncate_partial_nodes() Sasha Levin
@ 2026-09-12 11:17 ` Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 6/6] f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages() Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-09-12 11:17 UTC (permalink / raw)
To: stable; +Cc: Chao Yu, Jaegeuk Kim, Sasha Levin
From: Chao Yu <chao@kernel.org>
[ Upstream commit 019a8912425e231511042a4eae48b7a9e459453c ]
Just cleanup, no changes.
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Adapt this dependency to the page-based stable tree so that the unchanged
ce366bfa821e fsync retry fix applies cleanly.
Keep the node mapping checks in last_fsync_dnode() and
f2fs_fsync_node_pages(). Implement is_node_folio() as a macro which checks
for a NULL mapping before looking up the superblock. The upstream helper
would dereference a detached folio's NULL mapping in the race that the
target addresses. Drop the unrelated metadata, data I/O, GC and other
node cleanup hunks instead of importing their missing folio conversions.
Use a local last_folio in the fsync retry block and provide macro adapters
for folio put and writeback wait operations using the stable page APIs.
Normalize existing node-write failures to -EIO at their source, then
return ret unchanged so the target's new -EAGAIN result is preserved.
Existing error behavior is unchanged before the target is applied.
Earlier dependencies already converted node allocation and truncation to
folios without supplying all prerequisites. Adapt the existing
f2fs_new_node_folio() and f2fs_get_node_folio() implementations to the
available page allocation and lookup functions, retaining allocation
failure and error-pointer handling. The put/writeback adapters also
resolve the missing APIs in those earlier conversions.
No functions are added by this backport; the adaptation uses macros and
changes existing functions only. Leave the retry mapping guard itself
to the target commit.
[ sashal: Reduced backport -- upstream 019a8912425e2 touches 5 file(s), this
backport carries 2. Not backported here:
fs/f2fs/checkpoint.c
fs/f2fs/data.c
fs/f2fs/gc.c
This note is generated from the file lists only; see the resolution record
for the reasoning. ]
Stable-dep-of: ce366bfa821e ("f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/f2fs.h | 14 ++++++++++++++
fs/f2fs/node.c | 33 +++++++++++++++++++++------------
2 files changed, 35 insertions(+), 12 deletions(-)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 8f6c1079d48f0..50df2954b6795 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2054,6 +2054,12 @@ static inline struct address_space *NODE_MAPPING(struct f2fs_sb_info *sbi)
return sbi->node_inode->i_mapping;
}
+/* The caller must stabilize folio->mapping before testing it. */
+#define is_node_folio(folio) ({ \
+ struct address_space *__mapping = (folio)->mapping; \
+ __mapping && __mapping == NODE_MAPPING(F2FS_M_SB(__mapping)); \
+})
+
static inline bool is_sbi_flag_set(struct f2fs_sb_info *sbi, unsigned int type)
{
return test_bit(type, &sbi->s_flag);
@@ -2844,6 +2850,12 @@ static inline void f2fs_put_page(struct page *page, int unlock)
put_page(page);
}
+#define f2fs_folio_put(_folio, unlock) do { \
+ struct folio *__folio = (_folio); \
+ if (!IS_ERR_OR_NULL(__folio)) \
+ f2fs_put_page(&__folio->page, (unlock)); \
+} while (0)
+
static inline void f2fs_put_dnode(struct dnode_of_data *dn)
{
if (dn->node_page)
@@ -3779,6 +3791,8 @@ void f2fs_update_device_state(struct f2fs_sb_info *sbi, nid_t ino,
block_t blkaddr, unsigned int blkcnt);
void f2fs_wait_on_page_writeback(struct page *page,
enum page_type type, bool ordered, bool locked);
+#define f2fs_folio_wait_writeback(folio, type, ordered, locked) \
+ f2fs_wait_on_page_writeback(&(folio)->page, type, ordered, locked)
void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr);
void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr,
block_t len);
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index b6a57d3706722..48064c9c8d8c7 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1331,14 +1331,16 @@ struct folio *f2fs_new_node_folio(struct dnode_of_data *dn, unsigned int ofs)
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
struct node_info new_ni;
struct folio *folio;
+ struct page *page;
int err;
if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC)))
return ERR_PTR(-EPERM);
- folio = f2fs_grab_cache_folio(NODE_MAPPING(sbi), dn->nid, false);
- if (IS_ERR(folio))
- return folio;
+ page = f2fs_grab_cache_page(NODE_MAPPING(sbi), dn->nid, false);
+ if (!page)
+ return ERR_PTR(-ENOMEM);
+ folio = page_folio(page);
if (unlikely((err = inc_valid_node_count(sbi, dn->inode, !ofs))))
goto fail;
@@ -1526,7 +1528,11 @@ static struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid,
struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid)
{
- return __get_node_folio(sbi, nid, NULL, 0, NODE_TYPE_REGULAR);
+ struct page *page = __get_node_page(sbi, nid, NULL, 0);
+
+ if (IS_ERR(page))
+ return ERR_CAST(page);
+ return page_folio(page);
}
struct page *f2fs_get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
@@ -1609,7 +1615,7 @@ static struct page *last_fsync_dnode(struct f2fs_sb_info *sbi, nid_t ino)
lock_page(page);
- if (unlikely(page->mapping != NODE_MAPPING(sbi))) {
+ if (unlikely(!is_node_folio(page_folio(page)))) {
continue_unlock:
unlock_page(page);
continue;
@@ -1842,7 +1848,7 @@ int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
lock_page(page);
- if (unlikely(page->mapping != NODE_MAPPING(sbi))) {
+ if (unlikely(!is_node_folio(page_folio(page)))) {
continue_unlock:
unlock_page(page);
continue;
@@ -1886,6 +1892,7 @@ int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
if (ret) {
unlock_page(page);
f2fs_put_page(last_page, 0);
+ ret = -EIO;
break;
} else if (submitted) {
nwritten++;
@@ -1904,18 +1911,20 @@ int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
break;
}
if (!ret && atomic && !marked) {
+ struct folio *last_folio = page_folio(last_page);
+
f2fs_debug(sbi, "Retry to write fsync mark: ino=%u, idx=%lx",
- ino, last_page->index);
- lock_page(last_page);
- f2fs_wait_on_page_writeback(last_page, NODE, true, true);
- set_page_dirty(last_page);
- unlock_page(last_page);
+ ino, last_folio->index);
+ folio_lock(last_folio);
+ f2fs_folio_wait_writeback(last_folio, NODE, true, true);
+ folio_mark_dirty(last_folio);
+ folio_unlock(last_folio);
goto retry;
}
out:
if (nwritten)
f2fs_submit_merged_write_cond(sbi, NULL, NULL, ino, NODE);
- return ret ? -EIO : 0;
+ return ret;
}
static int f2fs_match_ino(struct inode *inode, unsigned long ino, void *data)
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 6.6.y 6/6] f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages()
2026-09-12 11:17 ` [PATCH 6.6.y 1/6] f2fs: Add f2fs_new_node_folio() Sasha Levin
` (3 preceding siblings ...)
2026-09-12 11:17 ` [PATCH 6.6.y 5/6] f2fs: introduce is_{meta,node}_folio Sasha Levin
@ 2026-09-12 11:17 ` Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-09-12 11:17 UTC (permalink / raw)
To: stable; +Cc: Chao Yu, stable, Jaegeuk Kim, Sasha Levin
From: Chao Yu <chao@kernel.org>
[ Upstream commit ce366bfa821ec81dd45bde547ee31e659306cc61 ]
There is potential deadloop in race condition:
Thread A Thread B
- fsync
- f2fs_do_sync_file
- f2fs_fsync_node_pages
- last_fsync_dnode
- folio_get(last_folio)
- f2fs_setattr
- f2fs_truncate
- f2fs_truncate_blocks
- f2fs_do_truncate_blocks
- f2fs_truncate_inode_blocks
- truncate_dnode
- truncate_node
- invalidate_mapping_pages
- folio->mapping = NULL
- is_node_folio alwasy return false
- atomic && !marked is always true,
then goto retry
Cc: stable@kernel.org
Fixes: 608514deba38 ("f2fs: set fsync mark only for the last dnode")
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/node.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 48064c9c8d8c7..3fa81132f18f9 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1916,6 +1916,11 @@ int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
f2fs_debug(sbi, "Retry to write fsync mark: ino=%u, idx=%lx",
ino, last_folio->index);
folio_lock(last_folio);
+ if (unlikely(!is_node_folio(last_folio))) {
+ f2fs_folio_put(last_folio, true);
+ ret = -EAGAIN;
+ goto out;
+ }
f2fs_folio_wait_writeback(last_folio, NODE, true, true);
folio_mark_dirty(last_folio);
folio_unlock(last_folio);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-12 11:17 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-09 11:26 FAILED: patch "[PATCH] f2fs: fix to avoid potential deadloop in" failed to apply to 6.6-stable tree gregkh
2026-09-12 11:17 ` [PATCH 6.6.y 1/6] f2fs: Add f2fs_new_node_folio() Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 2/6] f2fs: Add f2fs_get_node_folio() Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 3/6] f2fs: Use a folio in truncate_nodes() Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 4/6] f2fs: Use folios in truncate_partial_nodes() Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 5/6] f2fs: introduce is_{meta,node}_folio Sasha Levin
2026-09-12 11:17 ` [PATCH 6.6.y 6/6] f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages() Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox