* [PATCH 6.6.y 2/4] f2fs: Pass a folio to __f2fs_find_entry()
2026-09-12 1:32 ` [PATCH 6.6.y 1/4] f2fs: Use a folio in find_in_level() Sasha Levin
@ 2026-09-12 1:32 ` Sasha Levin
2026-09-12 1:32 ` [PATCH 6.6.y 3/4] f2fs: Pass a folio to f2fs_find_entry() Sasha Levin
2026-09-12 1:32 ` [PATCH 6.6.y 4/4] f2fs: dirty directory inodes on mtime/ctime update Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-12 1:32 UTC (permalink / raw)
To: stable; +Cc: Matthew Wilcox (Oracle), Chao Yu, Jaegeuk Kim, Sasha Levin
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
[ Upstream commit c190a13d71e7d9c3ccad1d17e00285888857b974 ]
Also pass a folio to f2fs_find_in_inline_dir() and find_in_level().
Remove three 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>
Adapt the folio lookup interface to the 6.6 page-based implementation.
Keep f2fs_get_node_page() for inline directories, preserve error pointers
with ERR_CAST(), and convert successful results with page_folio(). Use
f2fs_find_data_page() in find_in_level(), since the preceding dependency
introduced a call to f2fs_find_data_folio() which is absent from this tree.
Preserve NULL and error results when returning pages to existing callers.
Provide f2fs_folio_put() as a macro around the existing f2fs_put_page(),
retaining its NULL handling, lock assertion, unlock and reference release
without adding a function. This keeps the page-based callers intact.
Use the existing mtime setter at the four directory update sites and use
a folio internally in f2fs_set_link(). These equivalent API adaptations
provide the context required for 9ec09d5f4b31 to cherry-pick cleanly; all
seven changes to synchronous inode dirtying remain in the target commit.
Stable-dep-of: 9ec09d5f4b31 ("f2fs: dirty directory inodes on mtime/ctime update")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/dir.c | 54 +++++++++++++++++++++++++---------------------
fs/f2fs/f2fs.h | 15 ++++++++-----
fs/f2fs/inline.c | 10 ++++-----
fs/f2fs/namei.c | 10 ++++-----
fs/f2fs/recovery.c | 12 +++++------
5 files changed, 56 insertions(+), 45 deletions(-)
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 48073550089fe..c8e7d48c4b752 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -303,7 +303,7 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,
static struct f2fs_dir_entry *find_in_level(struct inode *dir,
unsigned int level,
const struct f2fs_filename *fname,
- struct page **res_page,
+ struct folio **res_folio,
bool use_hash)
{
int s = GET_DENTRY_SLOTS(fname->disk_name.len);
@@ -326,26 +326,29 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
while (bidx < end_block) {
/* no need to allocate new dentry pages to all the indices */
+ struct page *dentry_page;
struct folio *dentry_folio;
- dentry_folio = f2fs_find_data_folio(dir, bidx, &next_pgofs);
- if (IS_ERR(dentry_folio)) {
- if (PTR_ERR(dentry_folio) == -ENOENT) {
+
+ dentry_page = f2fs_find_data_page(dir, bidx, &next_pgofs);
+ if (IS_ERR(dentry_page)) {
+ if (PTR_ERR(dentry_page) == -ENOENT) {
room = true;
bidx = next_pgofs;
continue;
} else {
- *res_page = &dentry_folio->page;
+ *res_folio = ERR_CAST(dentry_page);
break;
}
}
+ dentry_folio = page_folio(dentry_page);
de = find_in_block(dir, dentry_folio, fname, &max_slots, use_hash);
if (IS_ERR(de)) {
- *res_page = ERR_CAST(de);
+ *res_folio = ERR_CAST(de);
de = NULL;
break;
} else if (de) {
- *res_page = &dentry_folio->page;
+ *res_folio = dentry_folio;
break;
}
@@ -372,7 +375,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
const struct f2fs_filename *fname,
- struct page **res_page)
+ struct folio **res_folio)
{
unsigned long npages = dir_blocks(dir);
struct f2fs_dir_entry *de = NULL;
@@ -380,13 +383,13 @@ struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
unsigned int level;
bool use_hash = true;
- *res_page = NULL;
+ *res_folio = NULL;
#if IS_ENABLED(CONFIG_UNICODE)
start_find_entry:
#endif
if (f2fs_has_inline_dentry(dir)) {
- de = f2fs_find_in_inline_dir(dir, fname, res_page, use_hash);
+ de = f2fs_find_in_inline_dir(dir, fname, res_folio, use_hash);
goto out;
}
@@ -402,8 +405,8 @@ struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
}
for (level = 0; level < max_depth; level++) {
- de = find_in_level(dir, level, fname, res_page, use_hash);
- if (de || IS_ERR(*res_page))
+ de = find_in_level(dir, level, fname, res_folio, use_hash);
+ if (de || IS_ERR(*res_folio))
break;
}
@@ -431,6 +434,7 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
{
struct f2fs_dir_entry *de = NULL;
struct f2fs_filename fname;
+ struct folio *rfolio;
int err;
err = f2fs_setup_filename(dir, child, 1, &fname);
@@ -442,7 +446,8 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
return NULL;
}
- de = __f2fs_find_entry(dir, &fname, res_page);
+ de = __f2fs_find_entry(dir, &fname, &rfolio);
+ *res_page = IS_ERR_OR_NULL(rfolio) ? ERR_CAST(rfolio) : &rfolio->page;
f2fs_free_filename(&fname);
return de;
@@ -472,16 +477,17 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
struct page *page, struct inode *inode)
{
enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA;
+ struct folio *folio = page_folio(page);
- lock_page(page);
+ folio_lock(folio);
f2fs_wait_on_page_writeback(page, type, true, true);
de->ino = cpu_to_le32(inode->i_ino);
de->file_type = fs_umode_to_ftype(inode->i_mode);
- set_page_dirty(page);
+ folio_mark_dirty(folio);
- dir->i_mtime = inode_set_ctime_current(dir);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
- f2fs_put_page(page, 1);
+ f2fs_folio_put(folio, true);
}
static void init_dent_inode(struct inode *dir, struct inode *inode,
@@ -633,7 +639,7 @@ void f2fs_update_parent_metadata(struct inode *dir, struct inode *inode,
f2fs_i_links_write(dir, true);
clear_inode_flag(inode, FI_NEW_INODE);
}
- dir->i_mtime = inode_set_ctime_current(dir);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
if (F2FS_I(dir)->i_current_depth != current_depth)
@@ -823,7 +829,7 @@ int f2fs_do_add_link(struct inode *dir, const struct qstr *name,
struct inode *inode, nid_t ino, umode_t mode)
{
struct f2fs_filename fname;
- struct page *page = NULL;
+ struct folio *folio = NULL;
struct f2fs_dir_entry *de = NULL;
int err;
@@ -839,14 +845,14 @@ int f2fs_do_add_link(struct inode *dir, const struct qstr *name,
* consistency more.
*/
if (current != F2FS_I(dir)->task) {
- de = __f2fs_find_entry(dir, &fname, &page);
+ de = __f2fs_find_entry(dir, &fname, &folio);
F2FS_I(dir)->task = NULL;
}
if (de) {
- f2fs_put_page(page, 0);
+ f2fs_folio_put(folio, false);
err = -EEXIST;
- } else if (IS_ERR(page)) {
- err = PTR_ERR(page);
+ } else if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
} else {
err = f2fs_add_dentry(dir, &fname, inode, ino, mode);
}
@@ -944,7 +950,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
}
f2fs_put_page(page, 1);
- dir->i_mtime = inode_set_ctime_current(dir);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
if (inode)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 63d3450f895ee..311d3b82b60a6 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2844,6 +2844,13 @@ static inline void f2fs_put_page(struct page *page, int unlock)
put_page(page);
}
+/* Keep folio callers on the existing page release path in this tree. */
+#define f2fs_folio_put(_folio, _unlock) \
+do { \
+ struct folio *__folio = (_folio); \
+ f2fs_put_page(__folio ? &__folio->page : NULL, (_unlock)); \
+} while (0)
+
static inline void f2fs_put_dnode(struct dnode_of_data *dn)
{
if (dn->node_page)
@@ -3600,8 +3607,7 @@ void f2fs_update_parent_metadata(struct inode *dir, struct inode *inode,
int f2fs_room_for_filename(const void *bitmap, int slots, int max_slots);
void f2fs_drop_nlink(struct inode *dir, struct inode *inode);
struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
- const struct f2fs_filename *fname,
- struct page **res_page);
+ const struct f2fs_filename *fname, struct folio **res_folio);
struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
const struct qstr *child, struct page **res_page);
struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p);
@@ -4203,9 +4209,8 @@ int f2fs_try_convert_inline_dir(struct inode *dir, struct dentry *dentry);
int f2fs_write_inline_data(struct inode *inode, struct page *page);
int f2fs_recover_inline_data(struct inode *inode, struct page *npage);
struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
- const struct f2fs_filename *fname,
- struct page **res_page,
- bool use_hash);
+ const struct f2fs_filename *fname, struct folio **res_folio,
+ bool use_hash);
int f2fs_make_empty_inline_dir(struct inode *inode, struct inode *parent,
struct page *ipage);
int f2fs_add_inline_entry(struct inode *dir, const struct f2fs_filename *fname,
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 92bbdb272a6d9..d9e7f581de780 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -353,7 +353,7 @@ int f2fs_recover_inline_data(struct inode *inode, struct page *npage)
struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
const struct f2fs_filename *fname,
- struct page **res_page,
+ struct folio **res_folio,
bool use_hash)
{
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
@@ -364,7 +364,7 @@ struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
ipage = f2fs_get_node_page(sbi, dir->i_ino);
if (IS_ERR(ipage)) {
- *res_page = ipage;
+ *res_folio = ERR_CAST(ipage);
return NULL;
}
@@ -374,11 +374,11 @@ struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
de = f2fs_find_target_dentry(&d, fname, NULL, use_hash);
unlock_page(ipage);
if (IS_ERR(de)) {
- *res_page = ERR_CAST(de);
+ *res_folio = ERR_CAST(de);
de = NULL;
}
if (de)
- *res_page = ipage;
+ *res_folio = page_folio(ipage);
else
f2fs_put_page(ipage, 0);
@@ -720,7 +720,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
set_page_dirty(page);
f2fs_put_page(page, 1);
- dir->i_mtime = inode_set_ctime_current(dir);
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
if (inode)
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index e55d0ee3e2947..e009b145c3863 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -460,7 +460,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
{
struct inode *inode = NULL;
struct f2fs_dir_entry *de;
- struct page *page;
+ struct folio *folio;
struct dentry *new;
nid_t ino = -1;
int err = 0;
@@ -479,12 +479,12 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
goto out_splice;
if (err)
goto out;
- de = __f2fs_find_entry(dir, &fname, &page);
+ de = __f2fs_find_entry(dir, &fname, &folio);
f2fs_free_filename(&fname);
if (!de) {
- if (IS_ERR(page)) {
- err = PTR_ERR(page);
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
goto out;
}
err = -ENOENT;
@@ -492,7 +492,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
}
ino = le32_to_cpu(de->ino);
- f2fs_put_page(page, 0);
+ f2fs_folio_put(folio, false);
inode = f2fs_iget(dir->i_sb, ino);
if (IS_ERR(inode)) {
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index 332996ff5cce6..1f37594defe0c 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -172,7 +172,7 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
struct f2fs_dir_entry *de;
struct f2fs_filename fname;
struct qstr usr_fname;
- struct page *page;
+ struct folio *folio;
struct inode *dir, *einode;
struct fsync_inode_entry *entry;
int err = 0;
@@ -194,7 +194,7 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
if (err)
goto out;
retry:
- de = __f2fs_find_entry(dir, &fname, &page);
+ de = __f2fs_find_entry(dir, &fname, &folio);
if (de && inode->i_ino == le32_to_cpu(de->ino))
goto out_put;
@@ -219,11 +219,11 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
iput(einode);
goto out_put;
}
- f2fs_delete_entry(de, page, dir, einode);
+ f2fs_delete_entry(de, &folio->page, dir, einode);
iput(einode);
goto retry;
- } else if (IS_ERR(page)) {
- err = PTR_ERR(page);
+ } else if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
} else {
err = f2fs_add_dentry(dir, &fname, inode,
inode->i_ino, inode->i_mode);
@@ -233,7 +233,7 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
goto out;
out_put:
- f2fs_put_page(page, 0);
+ f2fs_folio_put(folio, false);
out:
if (file_enc_name(inode))
name = "<encrypted>";
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 6.6.y 3/4] f2fs: Pass a folio to f2fs_find_entry()
2026-09-12 1:32 ` [PATCH 6.6.y 1/4] f2fs: Use a folio in find_in_level() Sasha Levin
2026-09-12 1:32 ` [PATCH 6.6.y 2/4] f2fs: Pass a folio to __f2fs_find_entry() Sasha Levin
@ 2026-09-12 1:32 ` Sasha Levin
2026-09-12 1:32 ` [PATCH 6.6.y 4/4] f2fs: dirty directory inodes on mtime/ctime update Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-12 1:32 UTC (permalink / raw)
To: stable; +Cc: Matthew Wilcox (Oracle), Chao Yu, Jaegeuk Kim, Sasha Levin
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
[ Upstream commit 0bd84d2d8912e83c4c6771ca12c647f4e37419cd ]
Convert all callers to pass in a pointer to a folio instead of a page.
Also convert f2fs_inode_by_name() to take a folio pointer. Removes
six 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>
Adapt this dependency to the stable page-based implementation. Return the
folio directly from f2fs_find_entry(), and preserve NULL and error pointers
when f2fs_parent_dir() converts its result back to a page.
Pass folios to the existing f2fs_set_link(), f2fs_delete_entry() and
f2fs_delete_inline_entry() functions, updating all callers, including
recovery. Keep the stable page-based writeback and cache helpers through
local page pointers, and use the existing f2fs_folio_put() compatibility
macro to preserve locking and reference-release behavior.
These interface and release-call adaptations supply the context needed for
9ec09d5f4b31 ("f2fs: dirty directory inodes on mtime/ctime update") to apply
without conflicts. Leave all seven synchronous inode-dirtying changes to
the target commit. No new functions are introduced.
Stable-dep-of: 9ec09d5f4b31 ("f2fs: dirty directory inodes on mtime/ctime update")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/dir.c | 38 +++++++++++-----------
fs/f2fs/f2fs.h | 10 +++---
fs/f2fs/inline.c | 5 +--
fs/f2fs/namei.c | 78 ++++++++++++++++++++++++----------------------
fs/f2fs/recovery.c | 2 +-
5 files changed, 69 insertions(+), 64 deletions(-)
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index c8e7d48c4b752..29005eeb283ca 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -425,29 +425,27 @@ struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
/*
* Find an entry in the specified directory with the wanted name.
- * It returns the page where the entry was found (as a parameter - res_page),
- * and the entry itself. Page is returned mapped and unlocked.
+ * It returns the folio where the entry was found (as a parameter - res_folio),
+ * and the entry itself. Folio is returned mapped and unlocked.
* Entry is guaranteed to be valid.
*/
struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
- const struct qstr *child, struct page **res_page)
+ const struct qstr *child, struct folio **res_folio)
{
struct f2fs_dir_entry *de = NULL;
struct f2fs_filename fname;
- struct folio *rfolio;
int err;
err = f2fs_setup_filename(dir, child, 1, &fname);
if (err) {
if (err == -ENOENT)
- *res_page = NULL;
+ *res_folio = NULL;
else
- *res_page = ERR_PTR(err);
+ *res_folio = ERR_PTR(err);
return NULL;
}
- de = __f2fs_find_entry(dir, &fname, &rfolio);
- *res_page = IS_ERR_OR_NULL(rfolio) ? ERR_CAST(rfolio) : &rfolio->page;
+ de = __f2fs_find_entry(dir, &fname, res_folio);
f2fs_free_filename(&fname);
return de;
@@ -455,32 +453,35 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p)
{
- return f2fs_find_entry(dir, &dotdot_name, p);
+ struct folio *folio;
+ struct f2fs_dir_entry *r = f2fs_find_entry(dir, &dotdot_name, &folio);
+
+ *p = IS_ERR_OR_NULL(folio) ? ERR_CAST(folio) : &folio->page;
+ return r;
}
ino_t f2fs_inode_by_name(struct inode *dir, const struct qstr *qstr,
- struct page **page)
+ struct folio **folio)
{
ino_t res = 0;
struct f2fs_dir_entry *de;
- de = f2fs_find_entry(dir, qstr, page);
+ de = f2fs_find_entry(dir, qstr, folio);
if (de) {
res = le32_to_cpu(de->ino);
- f2fs_put_page(*page, 0);
+ f2fs_folio_put(*folio, false);
}
return res;
}
void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
- struct page *page, struct inode *inode)
+ struct folio *folio, struct inode *inode)
{
enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA;
- struct folio *folio = page_folio(page);
folio_lock(folio);
- f2fs_wait_on_page_writeback(page, type, true, true);
+ f2fs_wait_on_page_writeback(&folio->page, type, true, true);
de->ino = cpu_to_le32(inode->i_ino);
de->file_type = fs_umode_to_ftype(inode->i_mode);
folio_mark_dirty(folio);
@@ -908,9 +909,10 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode)
* It only removes the dentry from the dentry page, corresponding name
* entry in name page does not need to be touched during deletion.
*/
-void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
+void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct folio *folio,
struct inode *dir, struct inode *inode)
{
+ struct page *page = &folio->page;
struct f2fs_dentry_block *dentry_blk;
unsigned int bit_pos;
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
@@ -922,7 +924,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
f2fs_add_ino_entry(F2FS_I_SB(dir), dir->i_ino, TRANS_DIR_INO);
if (f2fs_has_inline_dentry(dir))
- return f2fs_delete_inline_entry(dentry, page, dir, inode);
+ return f2fs_delete_inline_entry(dentry, folio, dir, inode);
lock_page(page);
f2fs_wait_on_page_writeback(page, DATA, true, true);
@@ -948,7 +950,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
inode_dec_dirty_pages(dir);
f2fs_remove_dirty_inode(dir);
}
- f2fs_put_page(page, 1);
+ f2fs_folio_put(folio, true);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 311d3b82b60a6..f93c226f710c3 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3609,12 +3609,12 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode);
struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,
const struct f2fs_filename *fname, struct folio **res_folio);
struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
- const struct qstr *child, struct page **res_page);
+ const struct qstr *child, struct folio **res_folio);
struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p);
ino_t f2fs_inode_by_name(struct inode *dir, const struct qstr *qstr,
- struct page **page);
+ struct folio **folio);
void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
- struct page *page, struct inode *inode);
+ struct folio *folio, struct inode *inode);
bool f2fs_has_enough_room(struct inode *dir, struct page *ipage,
const struct f2fs_filename *fname);
void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d,
@@ -3626,7 +3626,7 @@ int f2fs_add_dentry(struct inode *dir, const struct f2fs_filename *fname,
struct inode *inode, nid_t ino, umode_t mode);
int f2fs_do_add_link(struct inode *dir, const struct qstr *name,
struct inode *inode, nid_t ino, umode_t mode);
-void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
+void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct folio *folio,
struct inode *dir, struct inode *inode);
int f2fs_do_tmpfile(struct inode *inode, struct inode *dir,
struct f2fs_filename *fname);
@@ -4216,7 +4216,7 @@ int f2fs_make_empty_inline_dir(struct inode *inode, struct inode *parent,
int f2fs_add_inline_entry(struct inode *dir, const struct f2fs_filename *fname,
struct inode *inode, nid_t ino, umode_t mode);
void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry,
- struct page *page, struct inode *dir,
+ struct folio *folio, struct inode *dir,
struct inode *inode);
bool f2fs_empty_inline_dir(struct inode *dir);
int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx,
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index d9e7f581de780..68e5a77bb515d 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -698,9 +698,10 @@ int f2fs_add_inline_entry(struct inode *dir, const struct f2fs_filename *fname,
return err;
}
-void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
+void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct folio *folio,
struct inode *dir, struct inode *inode)
{
+ struct page *page = &folio->page;
struct f2fs_dentry_ptr d;
void *inline_dentry;
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
@@ -718,7 +719,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
__clear_bit_le(bit_pos + i, d.bitmap);
set_page_dirty(page);
- f2fs_put_page(page, 1);
+ f2fs_folio_put(folio, true);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index e009b145c3863..96ed0ab996b0d 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -444,12 +444,12 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *f2fs_get_parent(struct dentry *child)
{
- struct page *page;
- unsigned long ino = f2fs_inode_by_name(d_inode(child), &dotdot_name, &page);
+ struct folio *folio;
+ unsigned long ino = f2fs_inode_by_name(d_inode(child), &dotdot_name, &folio);
if (!ino) {
- if (IS_ERR(page))
- return ERR_CAST(page);
+ if (IS_ERR(folio))
+ return ERR_CAST(folio);
return ERR_PTR(-ENOENT);
}
return d_obtain_alias(f2fs_iget(child->d_sb, ino));
@@ -536,7 +536,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct inode *inode = d_inode(dentry);
struct f2fs_dir_entry *de;
- struct page *page;
+ struct folio *folio;
int err;
trace_f2fs_unlink_enter(dir, dentry);
@@ -553,10 +553,10 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
if (err)
goto fail;
- de = f2fs_find_entry(dir, &dentry->d_name, &page);
+ de = f2fs_find_entry(dir, &dentry->d_name, &folio);
if (!de) {
- if (IS_ERR(page))
- err = PTR_ERR(page);
+ if (IS_ERR(folio))
+ err = PTR_ERR(folio);
goto fail;
}
@@ -565,7 +565,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
__func__, inode->i_ino);
err = -EFSCORRUPTED;
set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
- f2fs_put_page(page, 0);
+ f2fs_folio_put(folio, false);
goto fail;
}
@@ -575,10 +575,10 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
err = f2fs_acquire_orphan_inode(sbi);
if (err) {
f2fs_unlock_op(sbi);
- f2fs_put_page(page, 0);
+ f2fs_folio_put(folio, false);
goto fail;
}
- f2fs_delete_entry(de, page, dir, inode);
+ f2fs_delete_entry(de, folio, dir, inode);
f2fs_unlock_op(sbi);
#if IS_ENABLED(CONFIG_UNICODE)
@@ -901,7 +901,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct inode *new_inode = d_inode(new_dentry);
struct inode *whiteout = NULL;
struct page *old_dir_page = NULL;
- struct page *old_page, *new_page = NULL;
+ struct folio *old_folio, *new_folio = NULL;
struct f2fs_dir_entry *old_dir_entry = NULL;
struct f2fs_dir_entry *old_entry;
struct f2fs_dir_entry *new_entry;
@@ -960,10 +960,10 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
}
err = -ENOENT;
- old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
+ old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_folio);
if (!old_entry) {
- if (IS_ERR(old_page))
- err = PTR_ERR(old_page);
+ if (IS_ERR(old_folio))
+ err = PTR_ERR(old_folio);
goto out;
}
@@ -984,10 +984,10 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
err = -ENOENT;
new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name,
- &new_page);
+ &new_folio);
if (!new_entry) {
- if (IS_ERR(new_page))
- err = PTR_ERR(new_page);
+ if (IS_ERR(new_folio))
+ err = PTR_ERR(new_folio);
goto out_dir;
}
@@ -999,8 +999,8 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
if (err)
goto put_out_dir;
- f2fs_set_link(new_dir, new_entry, new_page, old_inode);
- new_page = NULL;
+ f2fs_set_link(new_dir, new_entry, new_folio, old_inode);
+ new_folio = NULL;
inode_set_ctime_current(new_inode);
f2fs_down_write(&F2FS_I(new_inode)->i_sem);
@@ -1039,8 +1039,8 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
inode_set_ctime_current(old_inode);
f2fs_mark_inode_dirty_sync(old_inode, false);
- f2fs_delete_entry(old_entry, old_page, old_dir, NULL);
- old_page = NULL;
+ f2fs_delete_entry(old_entry, old_folio, old_dir, NULL);
+ old_folio = NULL;
if (whiteout) {
set_inode_flag(whiteout, FI_INC_LINK);
@@ -1060,7 +1060,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
if (old_dir_entry) {
if (old_dir != new_dir)
f2fs_set_link(old_inode, old_dir_entry,
- old_dir_page, new_dir);
+ page_folio(old_dir_page), new_dir);
else
f2fs_put_page(old_dir_page, 0);
f2fs_i_links_write(old_dir, false);
@@ -1082,12 +1082,12 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
put_out_dir:
f2fs_unlock_op(sbi);
- f2fs_put_page(new_page, 0);
+ f2fs_folio_put(new_folio, false);
out_dir:
if (old_dir_entry)
f2fs_put_page(old_dir_page, 0);
out_old:
- f2fs_put_page(old_page, 0);
+ f2fs_folio_put(old_folio, false);
out:
iput(whiteout);
return err;
@@ -1100,7 +1100,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry);
struct page *old_dir_page, *new_dir_page;
- struct page *old_page, *new_page;
+ struct folio *old_folio, *new_folio;
struct f2fs_dir_entry *old_dir_entry = NULL, *new_dir_entry = NULL;
struct f2fs_dir_entry *old_entry, *new_entry;
int old_nlink = 0, new_nlink = 0;
@@ -1128,17 +1128,17 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
goto out;
err = -ENOENT;
- old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
+ old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_folio);
if (!old_entry) {
- if (IS_ERR(old_page))
- err = PTR_ERR(old_page);
+ if (IS_ERR(old_folio))
+ err = PTR_ERR(old_folio);
goto out;
}
- new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, &new_page);
+ new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, &new_folio);
if (!new_entry) {
- if (IS_ERR(new_page))
- err = PTR_ERR(new_page);
+ if (IS_ERR(new_folio))
+ err = PTR_ERR(new_folio);
goto out_old;
}
@@ -1186,14 +1186,16 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
/* update ".." directory entry info of old dentry */
if (old_dir_entry)
- f2fs_set_link(old_inode, old_dir_entry, old_dir_page, new_dir);
+ f2fs_set_link(old_inode, old_dir_entry,
+ page_folio(old_dir_page), new_dir);
/* update ".." directory entry info of new dentry */
if (new_dir_entry)
- f2fs_set_link(new_inode, new_dir_entry, new_dir_page, old_dir);
+ f2fs_set_link(new_inode, new_dir_entry,
+ page_folio(new_dir_page), old_dir);
/* update directory entry info of old dir inode */
- f2fs_set_link(old_dir, old_entry, old_page, new_inode);
+ f2fs_set_link(old_dir, old_entry, old_folio, new_inode);
f2fs_down_write(&F2FS_I(old_inode)->i_sem);
if (!old_dir_entry)
@@ -1212,7 +1214,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_mark_inode_dirty_sync(old_dir, false);
/* update directory entry info of new dir inode */
- f2fs_set_link(new_dir, new_entry, new_page, old_inode);
+ f2fs_set_link(new_dir, new_entry, new_folio, old_inode);
f2fs_down_write(&F2FS_I(new_inode)->i_sem);
if (!new_dir_entry)
@@ -1251,9 +1253,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_put_page(old_dir_page, 0);
}
out_new:
- f2fs_put_page(new_page, 0);
+ f2fs_folio_put(new_folio, false);
out_old:
- f2fs_put_page(old_page, 0);
+ f2fs_folio_put(old_folio, false);
out:
return err;
}
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index 1f37594defe0c..fc51ee800e3f5 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -219,7 +219,7 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
iput(einode);
goto out_put;
}
- f2fs_delete_entry(de, &folio->page, dir, einode);
+ f2fs_delete_entry(de, folio, dir, einode);
iput(einode);
goto retry;
} else if (IS_ERR(folio)) {
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 6.6.y 4/4] f2fs: dirty directory inodes on mtime/ctime update
2026-09-12 1:32 ` [PATCH 6.6.y 1/4] f2fs: Use a folio in find_in_level() Sasha Levin
2026-09-12 1:32 ` [PATCH 6.6.y 2/4] f2fs: Pass a folio to __f2fs_find_entry() Sasha Levin
2026-09-12 1:32 ` [PATCH 6.6.y 3/4] f2fs: Pass a folio to f2fs_find_entry() Sasha Levin
@ 2026-09-12 1:32 ` Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-12 1:32 UTC (permalink / raw)
To: stable; +Cc: Joanne Chang, Chao Yu, Jaegeuk Kim, Sasha Levin
From: Joanne Chang <joannechien@google.com>
[ Upstream commit 9ec09d5f4b317a417c8655c14056f70cbe71eb6c ]
Xfstests generic/547 sometimes fail with mismatched directory metadata
before and after a power failure. This happens because when a directory
entry is added, renamed, or deleted, its mtime and ctime are updated and
the inode is marked dirty via
f2fs_mark_inode_dirty_sync(dir, sync=false). The sync=false flag means
the dirty inode is not added to the global DIRTY_META list. Therefore,
subsequent checkpoints skip flushing these updated directory blocks,
causing directory timestamps to revert to stale values after a sudden
power failure.
Address this by changing the dirtying parameter to sync=true during
directory entry mutations and renames. This forces F2FS to immediately
queue the updated directory blocks on the global DIRTY_META list,
ensuring timestamps are committed to checkpoints.
Fixes: 7c45729a4d6d ("f2fs: keep dirty inodes selectively for checkpoint")
Cc: stable@vger.kernel.org
Signed-off-by: Joanne Chang <joannechien@google.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/f2fs/dir.c | 6 +++---
fs/f2fs/inline.c | 2 +-
fs/f2fs/namei.c | 6 +++---
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 29005eeb283ca..11d2d27c9a703 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -487,7 +487,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
folio_mark_dirty(folio);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
- f2fs_mark_inode_dirty_sync(dir, false);
+ f2fs_mark_inode_dirty_sync(dir, true);
f2fs_folio_put(folio, true);
}
@@ -641,7 +641,7 @@ void f2fs_update_parent_metadata(struct inode *dir, struct inode *inode,
clear_inode_flag(inode, FI_NEW_INODE);
}
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
- f2fs_mark_inode_dirty_sync(dir, false);
+ f2fs_mark_inode_dirty_sync(dir, true);
if (F2FS_I(dir)->i_current_depth != current_depth)
f2fs_i_depth_write(dir, current_depth);
@@ -953,7 +953,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct folio *folio,
f2fs_folio_put(folio, true);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
- f2fs_mark_inode_dirty_sync(dir, false);
+ f2fs_mark_inode_dirty_sync(dir, true);
if (inode)
f2fs_drop_nlink(dir, inode);
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 68e5a77bb515d..1a03fac43f997 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -722,7 +722,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct folio *folio
f2fs_folio_put(folio, true);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
- f2fs_mark_inode_dirty_sync(dir, false);
+ f2fs_mark_inode_dirty_sync(dir, true);
if (inode)
f2fs_drop_nlink(dir, inode);
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 96ed0ab996b0d..018ef38e2e535 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -1037,7 +1037,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
f2fs_up_write(&F2FS_I(old_inode)->i_sem);
inode_set_ctime_current(old_inode);
- f2fs_mark_inode_dirty_sync(old_inode, false);
+ f2fs_mark_inode_dirty_sync(old_inode, true);
f2fs_delete_entry(old_entry, old_folio, old_dir, NULL);
old_folio = NULL;
@@ -1211,7 +1211,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_i_links_write(old_dir, old_nlink > 0);
f2fs_up_write(&F2FS_I(old_dir)->i_sem);
}
- f2fs_mark_inode_dirty_sync(old_dir, false);
+ f2fs_mark_inode_dirty_sync(old_dir, true);
/* update directory entry info of new dir inode */
f2fs_set_link(new_dir, new_entry, new_folio, old_inode);
@@ -1230,7 +1230,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_i_links_write(new_dir, new_nlink > 0);
f2fs_up_write(&F2FS_I(new_dir)->i_sem);
}
- f2fs_mark_inode_dirty_sync(new_dir, false);
+ f2fs_mark_inode_dirty_sync(new_dir, true);
if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT) {
f2fs_add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread