FAILED: patch "[PATCH] f2fs: dirty directory inodes on mtime/ctime update" failed to apply to 6.6-stable tree
Subsystems (2)
[email protected]
maintainerThe 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 9ec09d5f4b317a417c8655c14056f70cbe71eb6c
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090949-surpass-emphases-308c@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 9ec09d5f4b317a417c8655c14056f70cbe71eb6c Mon Sep 17 00:00:00 2001
From: Joanne Chang <[email protected]>
Date: Tue, 23 Jun 2026 06:34:28 +0000
Subject: [PATCH] f2fs: dirty directory inodes on mtime/ctime update
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.
fs/f2fs/dir.c +3 -3
@@ -461,7 +461,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); }
@@ -616,7 +616,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);
@@ -928,7 +928,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);
fs/f2fs/inline.c +1 -1
@@ -732,7 +732,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry,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);
fs/f2fs/namei.c +3 -3
@@ -1076,7 +1076,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 ;
@@ -1246,7 +1246,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);
@@ -1265,7 +1265,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 );
Sasha Levin
maintainerPATCH 1/4 f2fs: Use a folio in find_in_level()
From: "Matthew Wilcox (Oracle)" <[email protected]>
[ Upstream commit e4ca8ff450c39dd58a6a0d7167e32e80af457507 ]
Get a folio instead of a page and use it throughout. Removes a call to compound_head().
Stable-dep-of: 9ec09d5f4b31 ("f2fs: dirty directory inodes on mtime/ctime update")
--- fs/f2fs/dir.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
fs/f2fs/dir.c +10 -10
@@ -164,7 +164,7 @@ static unsigned long dir_block_index(unsigned int level, }static struct f2fs_dir_entry *find_in_block (struct inode *dir, -struct page *dentry_page, +struct folio *dentry_folio,const struct f2fs_filename *fname,int *max_slots,bool use_hash)
@@ -172,7 +172,7 @@ static struct f2fs_dir_entry *find_in_block(struct inode *dir,struct f2fs_dentry_block *dentry_blk;struct f2fs_dentry_ptr d; - dentry_blk = (struct f2fs_dentry_block *)page_address (dentry_page); + dentry_blk =folio_address (dentry_folio);make_dentry_ptr_block (dir, &d, dentry_blk);return f2fs_find_target_dentry (&d, fname, max_slots, use_hash);
@@ -309,7 +309,6 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,int s =GET_DENTRY_SLOTS (fname->disk_name.len);unsigned int nbucket, nblock;unsigned int bidx, end_block, bucket_no; -struct page *dentry_page;struct f2fs_dir_entry *de =NULL ;pgoff_t next_pgofs;bool room = false;
@@ -327,31 +326,32 @@ 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 */ - dentry_page =f2fs_find_data_page (dir, bidx, &next_pgofs); -if (IS_ERR (dentry_page)) { -if (PTR_ERR (dentry_page) == -ENOENT ) { +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 ) { room = true; bidx = next_pgofs;continue ; }else { - *res_page = dentry_page; + *res_page = &dentry_folio->page;break ; } } - de =find_in_block (dir, dentry_page, fname, &max_slots, use_hash); + de =find_in_block (dir, dentry_folio, fname, &max_slots, use_hash);if (IS_ERR (de)) { *res_page =ERR_CAST (de); de =NULL ;break ; }else if (de) { - *res_page = dentry_page; + *res_page = &dentry_folio->page;break ; }if (max_slots >= s) room = true; -f2fs_put_page (dentry_page,0 ); +f2fs_folio_put (dentry_folio, false); bidx++; }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 f2fs: Pass a folio to __f2fs_find_entry()
From: "Matthew Wilcox (Oracle)" <[email protected]>
[ Upstream commit c190a13d71e7d9c3ccad1d17e00285888857b974 ]
Also pass a folio to f2fs_find_in_inline_dir() and find_in_level(). Remove three calls to compound_head().
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")
--- 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(-)
fs/f2fs/dir.c +30 -24
@@ -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)
fs/f2fs/f2fs.h +10 -5
@@ -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,
fs/f2fs/inline.c +5 -5
@@ -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)
fs/f2fs/namei.c +5 -5
@@ -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)) {
fs/f2fs/recovery.c +6 -6
@@ -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>" ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 f2fs: Pass a folio to f2fs_find_entry()
In reply to Sasha LevinFrom: "Matthew Wilcox (Oracle)" <[email protected]>
[ 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().
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")
--- 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(-)
fs/f2fs/dir.c +20 -18
@@ -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);
fs/f2fs/f2fs.h +5 -5
@@ -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,
fs/f2fs/inline.c +3 -2
@@ -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);
fs/f2fs/namei.c +40 -38
202 lines not on this page · raw message
fs/f2fs/recovery.c +1 -1
@@ -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)) {
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 f2fs: dirty directory inodes on mtime/ctime update
In reply to Sasha LevinFrom: Joanne Chang <[email protected]>
[ 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.
--- fs/f2fs/dir.c | 6 +++--- fs/f2fs/inline.c | 2 +- fs/f2fs/namei.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-)
fs/f2fs/dir.c +3 -3
@@ -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);
fs/f2fs/inline.c +1 -1
@@ -722,7 +722,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct folio *foliof2fs_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);
fs/f2fs/namei.c +3 -3
@@ -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 );
Signature · 1 line
2.53.0