| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/16 20:32 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"EXT4_FS",
"EXT2_FS",
"MSDOS_FS",
"VFAT_FS",
"MINIX_FS",
"UDF_FS",
"BFS_FS",
"AFFS_FS"
],
"FocusSymbols": [
"__writeback_single_inode",
"writeback_single_inode",
"ext4_sync_inode_metadata",
"ext2_sync_inode_metadata",
"fat_sync_inode_metadata",
"minix_sync_inode_metadata",
"udf_sync_inode_metadata",
"bfs_sync_inode_metadata",
"simple_fsync_noflush"
],
"Reasoning": "The patch introduces a new `sync_inode_metadata` callback in `super_operations` and modifies the VFS writeback logic to handle a new `I_METADATA_WRITEBACK` flag. It implements this callback for several filesystems (ext4, ext2, fat, minix, udf, bfs) and refactors their metadata synchronization logic, replacing `mmb_fsync`. This is a significant functional change to core VFS writeback and filesystem metadata handling, making it highly relevant for fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/16 20:32 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 9cb53a8d5963fd1e65bce9ea2f5a63adc83329bd\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Jul 16 20:32:01 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/fs/affs/affs.h b/fs/affs/affs.h\nindex 44a3f69d275f96..a7faa91deed5ac 100644\n--- a/fs/affs/affs.h\n+++ b/fs/affs/affs.h\n@@ -44,7 +44,6 @@ struct affs_inode_info {\n \tstruct mutex i_link_lock;\t\t/* Protects internal inode access. */\n \tstruct mutex i_ext_lock;\t\t/* Protects internal inode access. */\n #define i_hash_lock i_ext_lock\n-\tstruct mapping_metadata_bhs i_metadata_bhs;\n \tu32\t i_blkcnt;\t\t\t/* block count */\n \tu32\t i_extcnt;\t\t\t/* extended block count */\n \tu32\t*i_lc;\t\t\t\t/* linear cache of extended blocks */\n@@ -152,7 +151,6 @@ extern bool\taffs_nofilenametruncate(const struct dentry *dentry);\n extern int\taffs_check_name(const unsigned char *name, int len,\n \t\t\t\tbool notruncate);\n extern int\taffs_copy_name(unsigned char *bstr, struct dentry *dentry);\n-struct mapping_metadata_bhs *affs_get_metadata_bhs(struct inode *inode);\n \n /* bitmap. c */\n \ndiff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c\nindex bed4fc805e8e66..6cc0fc9a4cbf92 100644\n--- a/fs/affs/amigaffs.c\n+++ b/fs/affs/amigaffs.c\n@@ -57,7 +57,7 @@ affs_insert_hash(struct inode *dir, struct buffer_head *bh)\n \t\tAFFS_TAIL(sb, dir_bh)-\u003ehash_chain = cpu_to_be32(ino);\n \n \taffs_adjust_checksum(dir_bh, ino);\n-\tmmb_mark_buffer_dirty(dir_bh, \u0026AFFS_I(dir)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(dir_bh);\n \taffs_brelse(dir_bh);\n \n \tinode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));\n@@ -100,7 +100,7 @@ affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)\n \t\t\telse\n \t\t\t\tAFFS_TAIL(sb, bh)-\u003ehash_chain = ino;\n \t\t\taffs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);\n-\t\t\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(dir)-\u003ei_metadata_bhs);\n+\t\t\tmark_buffer_dirty(bh);\n \t\t\tAFFS_TAIL(sb, rem_bh)-\u003eparent = 0;\n \t\t\tretval = 0;\n \t\t\tbreak;\n@@ -180,7 +180,7 @@ affs_remove_link(struct dentry *dentry)\n \t\t\taffs_unlock_dir(dir);\n \t\t\tgoto done;\n \t\t}\n-\t\tmmb_mark_buffer_dirty(link_bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\tmark_buffer_dirty(link_bh);\n \n \t\tmemcpy(AFFS_TAIL(sb, bh)-\u003ename, AFFS_TAIL(sb, link_bh)-\u003ename, 32);\n \t\tretval = affs_insert_hash(dir, bh);\n@@ -188,7 +188,7 @@ affs_remove_link(struct dentry *dentry)\n \t\t\taffs_unlock_dir(dir);\n \t\t\tgoto done;\n \t\t}\n-\t\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\tmark_buffer_dirty(bh);\n \n \t\taffs_unlock_dir(dir);\n \t\tiput(dir);\n@@ -203,7 +203,7 @@ affs_remove_link(struct dentry *dentry)\n \t\t\t__be32 ino2 = AFFS_TAIL(sb, link_bh)-\u003elink_chain;\n \t\t\tAFFS_TAIL(sb, bh)-\u003elink_chain = ino2;\n \t\t\taffs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino);\n-\t\t\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\t\tmark_buffer_dirty(bh);\n \t\t\tretval = 0;\n \t\t\t/* Fix the link count, if bh is a normal header block without links */\n \t\t\tswitch (be32_to_cpu(AFFS_TAIL(sb, bh)-\u003estype)) {\n@@ -306,7 +306,7 @@ affs_remove_header(struct dentry *dentry)\n \tretval = affs_remove_hash(dir, bh);\n \tif (retval)\n \t\tgoto done_unlock;\n-\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh);\n \n \taffs_unlock_dir(dir);\n \ndiff --git a/fs/affs/file.c b/fs/affs/file.c\nindex 144b17482d12da..23e088a7ed4fb3 100644\n--- a/fs/affs/file.c\n+++ b/fs/affs/file.c\n@@ -140,14 +140,14 @@ affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)\n \tAFFS_TAIL(sb, new_bh)-\u003eparent = cpu_to_be32(inode-\u003ei_ino);\n \taffs_fix_checksum(sb, new_bh);\n \n-\tmmb_mark_buffer_dirty(new_bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(new_bh);\n \n \ttmp = be32_to_cpu(AFFS_TAIL(sb, bh)-\u003eextension);\n \tif (tmp)\n \t\taffs_warning(sb, \"alloc_ext\", \"previous extension set (%x)\", tmp);\n \tAFFS_TAIL(sb, bh)-\u003eextension = cpu_to_be32(blocknr);\n \taffs_adjust_checksum(bh, blocknr - tmp);\n-\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh);\n \n \tAFFS_I(inode)-\u003ei_extcnt++;\n \tmark_inode_dirty(inode);\n@@ -581,7 +581,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)\n \t\tmemset(AFFS_DATA(bh) + boff, 0, tmp);\n \t\tbe32_add_cpu(\u0026AFFS_DATA_HEAD(bh)-\u003esize, tmp);\n \t\taffs_fix_checksum(sb, bh);\n-\t\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\tmark_buffer_dirty(bh);\n \t\tsize += tmp;\n \t\tbidx++;\n \t} else if (bidx) {\n@@ -603,7 +603,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)\n \t\tAFFS_DATA_HEAD(bh)-\u003esize = cpu_to_be32(tmp);\n \t\taffs_fix_checksum(sb, bh);\n \t\tbh-\u003eb_state \u0026= ~(1UL \u003c\u003c BH_New);\n-\t\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\tmark_buffer_dirty(bh);\n \t\tif (prev_bh) {\n \t\t\tu32 tmp_next = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)-\u003enext);\n \n@@ -613,8 +613,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)\n \t\t\t\t\t bidx, tmp_next);\n \t\t\tAFFS_DATA_HEAD(prev_bh)-\u003enext = cpu_to_be32(bh-\u003eb_blocknr);\n \t\t\taffs_adjust_checksum(prev_bh, bh-\u003eb_blocknr - tmp_next);\n-\t\t\tmmb_mark_buffer_dirty(prev_bh,\n-\t\t\t\t\t \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\t\tmark_buffer_dirty(prev_bh);\n \t\t\taffs_brelse(prev_bh);\n \t\t}\n \t\tsize += bsize;\n@@ -733,7 +732,7 @@ static int affs_write_end_ofs(const struct kiocb *iocb,\n \t\tAFFS_DATA_HEAD(bh)-\u003esize = cpu_to_be32(\n \t\t\tmax(boff + tmp, be32_to_cpu(AFFS_DATA_HEAD(bh)-\u003esize)));\n \t\taffs_fix_checksum(sb, bh);\n-\t\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\tmark_buffer_dirty(bh);\n \t\twritten += tmp;\n \t\tfrom += tmp;\n \t\tbidx++;\n@@ -766,13 +765,12 @@ static int affs_write_end_ofs(const struct kiocb *iocb,\n \t\t\t\t\t\t bidx, tmp_next);\n \t\t\t\tAFFS_DATA_HEAD(prev_bh)-\u003enext = cpu_to_be32(bh-\u003eb_blocknr);\n \t\t\t\taffs_adjust_checksum(prev_bh, bh-\u003eb_blocknr - tmp_next);\n-\t\t\t\tmmb_mark_buffer_dirty(prev_bh,\n-\t\t\t\t\t\u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\t\t\tmark_buffer_dirty(prev_bh);\n \t\t\t}\n \t\t}\n \t\taffs_brelse(prev_bh);\n \t\taffs_fix_checksum(sb, bh);\n-\t\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\tmark_buffer_dirty(bh);\n \t\twritten += bsize;\n \t\tfrom += bsize;\n \t\tbidx++;\n@@ -801,14 +799,13 @@ static int affs_write_end_ofs(const struct kiocb *iocb,\n \t\t\t\t\t\t bidx, tmp_next);\n \t\t\t\tAFFS_DATA_HEAD(prev_bh)-\u003enext = cpu_to_be32(bh-\u003eb_blocknr);\n \t\t\t\taffs_adjust_checksum(prev_bh, bh-\u003eb_blocknr - tmp_next);\n-\t\t\t\tmmb_mark_buffer_dirty(prev_bh,\n-\t\t\t\t\t\t\u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\t\t\tmark_buffer_dirty(prev_bh);\n \t\t\t}\n \t\t} else if (be32_to_cpu(AFFS_DATA_HEAD(bh)-\u003esize) \u003c tmp)\n \t\t\tAFFS_DATA_HEAD(bh)-\u003esize = cpu_to_be32(tmp);\n \t\taffs_brelse(prev_bh);\n \t\taffs_fix_checksum(sb, bh);\n-\t\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\tmark_buffer_dirty(bh);\n \t\twritten += tmp;\n \t\tfrom += tmp;\n \t\tbidx++;\n@@ -945,7 +942,7 @@ affs_truncate(struct inode *inode)\n \t}\n \tAFFS_TAIL(sb, ext_bh)-\u003eextension = 0;\n \taffs_fix_checksum(sb, ext_bh);\n-\tmmb_mark_buffer_dirty(ext_bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(ext_bh);\n \taffs_brelse(ext_bh);\n \n \tif (inode-\u003ei_size) {\ndiff --git a/fs/affs/inode.c b/fs/affs/inode.c\nindex 5dd1b016bcb00a..d4a3f381c4bcff 100644\n--- a/fs/affs/inode.c\n+++ b/fs/affs/inode.c\n@@ -206,7 +206,7 @@ affs_write_inode(struct inode *inode, struct writeback_control *wbc)\n \t\t}\n \t}\n \taffs_fix_checksum(sb, bh);\n-\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh);\n \taffs_brelse(bh);\n \taffs_free_prealloc(inode);\n \treturn 0;\n@@ -266,11 +266,8 @@ affs_evict_inode(struct inode *inode)\n \tif (!inode-\u003ei_nlink) {\n \t\tinode-\u003ei_size = 0;\n \t\taffs_truncate(inode);\n-\t} else {\n-\t\tmmb_sync(\u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n \t}\n \n-\tmmb_invalidate(\u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n \tclear_inode(inode);\n \taffs_free_prealloc(inode);\n \tcache_page = (unsigned long)AFFS_I(inode)-\u003ei_lc;\n@@ -305,7 +302,7 @@ affs_new_inode(struct inode *dir)\n \tbh = affs_getzeroblk(sb, block);\n \tif (!bh)\n \t\tgoto err_bh;\n-\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh);\n \taffs_brelse(bh);\n \n \tinode-\u003ei_uid = current_fsuid();\n@@ -393,17 +390,17 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3\n \t\tAFFS_TAIL(sb, bh)-\u003elink_chain = chain;\n \t\tAFFS_TAIL(sb, inode_bh)-\u003elink_chain = cpu_to_be32(block);\n \t\taffs_adjust_checksum(inode_bh, block - be32_to_cpu(chain));\n-\t\tmmb_mark_buffer_dirty(inode_bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\t\tmark_buffer_dirty(inode_bh);\n \t\tset_nlink(inode, 2);\n \t\tihold(inode);\n \t}\n \taffs_fix_checksum(sb, bh);\n-\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh);\n \tdentry-\u003ed_fsdata = (void *)(long)bh-\u003eb_blocknr;\n \n \taffs_lock_dir(dir);\n \tretval = affs_insert_hash(dir, bh);\n-\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh);\n \taffs_unlock_dir(dir);\n \taffs_unlock_link(inode);\n \ndiff --git a/fs/affs/namei.c b/fs/affs/namei.c\nindex c3c6532da4b0da..57d8d755aada05 100644\n--- a/fs/affs/namei.c\n+++ b/fs/affs/namei.c\n@@ -373,7 +373,7 @@ affs_symlink(struct mnt_idmap *idmap, struct inode *dir,\n \t}\n \t*p = 0;\n \tinode-\u003ei_size = i + 1;\n-\tmmb_mark_buffer_dirty(bh, \u0026AFFS_I(inode)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh);\n \taffs_brelse(bh);\n \tmark_inode_dirty(inode);\n \n@@ -443,8 +443,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,\n \t/* TODO: move it back to old_dir, if error? */\n \n done:\n-\tmmb_mark_buffer_dirty(bh,\n-\t\t\t\u0026AFFS_I(retval ? old_dir : new_dir)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh);\n \taffs_brelse(bh);\n \treturn retval;\n }\n@@ -497,8 +496,8 @@ affs_xrename(struct inode *old_dir, struct dentry *old_dentry,\n \tretval = affs_insert_hash(old_dir, bh_new);\n \taffs_unlock_dir(old_dir);\n done:\n-\tmmb_mark_buffer_dirty(bh_old, \u0026AFFS_I(new_dir)-\u003ei_metadata_bhs);\n-\tmmb_mark_buffer_dirty(bh_new, \u0026AFFS_I(old_dir)-\u003ei_metadata_bhs);\n+\tmark_buffer_dirty(bh_old);\n+\tmark_buffer_dirty(bh_new);\n \taffs_brelse(bh_old);\n \taffs_brelse(bh_new);\n \treturn retval;\ndiff --git a/fs/affs/super.c b/fs/affs/super.c\nindex b232251aa7bbd0..0ad5127e9fb8ee 100644\n--- a/fs/affs/super.c\n+++ b/fs/affs/super.c\n@@ -108,7 +108,6 @@ static struct inode *affs_alloc_inode(struct super_block *sb)\n \ti-\u003ei_lc = NULL;\n \ti-\u003ei_ext_bh = NULL;\n \ti-\u003ei_pa_cnt = 0;\n-\tmmb_init(\u0026i-\u003ei_metadata_bhs, \u0026i-\u003evfs_inode.i_data);\n \n \treturn \u0026i-\u003evfs_inode;\n }\ndiff --git a/fs/bfs/dir.c b/fs/bfs/dir.c\nindex 5b40ab09a796de..9b37ec4bd89a8c 100644\n--- a/fs/bfs/dir.c\n+++ b/fs/bfs/dir.c\n@@ -68,17 +68,10 @@ static int bfs_readdir(struct file *f, struct dir_context *ctx)\n \treturn 0;\n }\n \n-static int bfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)\n-{\n-\treturn mmb_fsync(file,\n-\t\t\t\u0026BFS_I(file-\u003ef_mapping-\u003ehost)-\u003ei_metadata_bhs,\n-\t\t\tstart, end, datasync);\n-}\n-\n const struct file_operations bfs_dir_operations = {\n \t.read\t\t= generic_read_dir,\n \t.iterate_shared\t= bfs_readdir,\n-\t.fsync\t\t= bfs_fsync,\n+\t.fsync\t\t= simple_fsync,\n \t.llseek\t\t= generic_file_llseek,\n };\n \ndiff --git a/fs/bfs/inode.c b/fs/bfs/inode.c\nindex e41efdd35db9ef..cacc7eff9c33ce 100644\n--- a/fs/bfs/inode.c\n+++ b/fs/bfs/inode.c\n@@ -136,7 +136,6 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc)\n \tunsigned long i_sblock;\n \tstruct bfs_inode *di;\n \tstruct buffer_head *bh;\n-\tint err = 0;\n \n \tdprintf(\"ino=%08x\\n\", ino);\n \n@@ -165,13 +164,31 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc)\n \tdi-\u003ei_eoffset = cpu_to_le32(i_sblock * BFS_BSIZE + inode-\u003ei_size - 1);\n \n \tmark_buffer_dirty(bh);\n-\tif (wbc-\u003esync_mode == WB_SYNC_ALL) {\n-\t\tsync_dirty_buffer(bh);\n-\t\tif (buffer_req(bh) \u0026\u0026 !buffer_uptodate(bh))\n-\t\t\terr = -EIO;\n-\t}\n \tbrelse(bh);\n \tmutex_unlock(\u0026info-\u003ebfs_lock);\n+\tset_inode_metadata_writeback(inode);\n+\treturn 0;\n+}\n+\n+static int bfs_sync_inode_metadata(struct inode *inode,\n+\t\t\t\t struct writeback_control *wbc)\n+{\n+\tint err = 0;\n+\tstruct bfs_inode *di;\n+\tstruct buffer_head *bh;\n+\n+\tdi = find_inode(inode-\u003ei_sb, (u16)inode-\u003eino, \u0026bh);\n+\tif (IS_ERR(di))\n+\t\treturn PTR_ERR(di);\n+\n+\tsync_dirty_buffer(bh);\n+\tif (buffer_write_io_error(bh)) {\n+\t\terr = -EIO;\n+\t\tgoto out;\n+\t}\n+\terr = mmb_sync(\u0026BFS_I(inode)-\u003ei_metadata_bhs);\n+out:\n+\tbrelse(bh);\n \treturn err;\n }\n \n@@ -302,6 +319,7 @@ static const struct super_operations bfs_sops = {\n \t.alloc_inode\t= bfs_alloc_inode,\n \t.free_inode\t= bfs_free_inode,\n \t.write_inode\t= bfs_write_inode,\n+\t.sync_inode_metadata = bfs_sync_inode_metadata,\n \t.evict_inode\t= bfs_evict_inode,\n \t.put_super\t= bfs_put_super,\n \t.statfs\t\t= bfs_statfs,\ndiff --git a/fs/buffer.c b/fs/buffer.c\nindex 9af5f061a1f85a..be8b57a635cd82 100644\n--- a/fs/buffer.c\n+++ b/fs/buffer.c\n@@ -628,79 +628,6 @@ int mmb_sync(struct mapping_metadata_bhs *mmb)\n }\n EXPORT_SYMBOL(mmb_sync);\n \n-/**\n- * mmb_fsync_noflush - fsync implementation for simple filesystems with\n- * \t\t metadata buffers list\n- *\n- * @file:\tfile to synchronize\n- * @mmb:\tlist of metadata bhs to flush\n- * @start:\tstart offset in bytes\n- * @end:\tend offset in bytes (inclusive)\n- * @datasync:\tonly synchronize essential metadata if true\n- *\n- * This is an implementation of the fsync method for simple filesystems which\n- * track all non-inode metadata in the buffers list hanging off the @mmb\n- * structure.\n- */\n-int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,\n-\t\t loff_t start, loff_t end, bool datasync)\n-{\n-\tstruct inode *inode = file-\u003ef_mapping-\u003ehost;\n-\tint err;\n-\tint ret = 0;\n-\n-\terr = file_write_and_wait_range(file, start, end);\n-\tif (err)\n-\t\treturn err;\n-\n-\tif (mmb)\n-\t\tret = mmb_sync(mmb);\n-\tif (!(inode_state_read_once(inode) \u0026 I_DIRTY_ALL))\n-\t\tgoto out;\n-\tif (datasync \u0026\u0026 !(inode_state_read_once(inode) \u0026 I_DIRTY_DATASYNC))\n-\t\tgoto out;\n-\n-\terr = sync_inode_metadata(inode, 1);\n-\tif (ret == 0)\n-\t\tret = err;\n-\n-out:\n-\t/* check and advance again to catch errors after syncing out buffers */\n-\terr = file_check_and_advance_wb_err(file);\n-\tif (ret == 0)\n-\t\tret = err;\n-\treturn ret;\n-}\n-EXPORT_SYMBOL(mmb_fsync_noflush);\n-\n-/**\n- * mmb_fsync - fsync implementation for simple filesystems with metadata\n- * \t buffers list\n- *\n- * @file:\tfile to synchronize\n- * @mmb:\tlist of metadata bhs to flush\n- * @start:\tstart offset in bytes\n- * @end:\tend offset in bytes (inclusive)\n- * @datasync:\tonly synchronize essential metadata if true\n- *\n- * This is an implementation of the fsync method for simple filesystems which\n- * track all non-inode metadata in the buffers list hanging off the @mmb\n- * structure. This also makes sure that a device cache flush operation is\n- * called at the end.\n- */\n-int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,\n-\t loff_t start, loff_t end, bool datasync)\n-{\n-\tstruct inode *inode = file-\u003ef_mapping-\u003ehost;\n-\tint ret;\n-\n-\tret = mmb_fsync_noflush(file, mmb, start, end, datasync);\n-\tif (!ret)\n-\t\tret = blkdev_issue_flush(inode-\u003ei_sb-\u003es_bdev);\n-\treturn ret;\n-}\n-EXPORT_SYMBOL(mmb_fsync);\n-\n /*\n * Called when we've recently written block `bblock', and it is known that\n * `bblock' was for a buffer_boundary() buffer. This means that the block at\n@@ -1123,12 +1050,18 @@ EXPORT_SYMBOL(mark_buffer_dirty);\n \n void mark_buffer_write_io_error(struct buffer_head *bh)\n {\n+\tstruct mapping_metadata_bhs *mmb;\n+\n \tset_buffer_write_io_error(bh);\n \t/* FIXME: do we need to set this in both places? */\n \tif (bh-\u003eb_folio \u0026\u0026 bh-\u003eb_folio-\u003emapping)\n \t\tmapping_set_error(bh-\u003eb_folio-\u003emapping, -EIO);\n-\tif (bh-\u003eb_mmb)\n-\t\tmapping_set_error(bh-\u003eb_mmb-\u003emapping, -EIO);\n+\t/* Protect us from mmb \u0026 inode getting freed while we work on it */\n+\trcu_read_lock();\n+\tmmb = READ_ONCE(bh-\u003eb_mmb);\n+\tif (mmb)\n+\t\tmapping_set_error(mmb-\u003emapping, -EIO);\n+\trcu_read_unlock();\n }\n EXPORT_SYMBOL(mark_buffer_write_io_error);\n \ndiff --git a/fs/ext2/dir.c b/fs/ext2/dir.c\nindex 278d4be8ecbe77..e17bbc7598c143 100644\n--- a/fs/ext2/dir.c\n+++ b/fs/ext2/dir.c\n@@ -734,6 +734,6 @@ const struct file_operations ext2_dir_operations = {\n #ifdef CONFIG_COMPAT\n \t.compat_ioctl\t= ext2_compat_ioctl,\n #endif\n-\t.fsync\t\t= ext2_fsync,\n+\t.fsync\t\t= simple_fsync,\n \t.setlease\t= generic_setlease,\n };\ndiff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h\nindex 79f7b395258c2a..5642451bf19173 100644\n--- a/fs/ext2/ext2.h\n+++ b/fs/ext2/ext2.h\n@@ -735,6 +735,7 @@ extern unsigned long ext2_count_free (struct buffer_head *, unsigned);\n /* inode.c */\n extern struct inode *ext2_iget (struct super_block *, unsigned long);\n extern int ext2_write_inode (struct inode *, struct writeback_control *);\n+extern int ext2_sync_inode_metadata(struct inode *, struct writeback_control *);\n extern void ext2_evict_inode(struct inode *);\n void ext2_write_failed(struct address_space *mapping, loff_t to);\n extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);\n@@ -772,8 +773,6 @@ extern void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es,\n extern const struct file_operations ext2_dir_operations;\n \n /* file.c */\n-extern int ext2_fsync(struct file *file, loff_t start, loff_t end,\n-\t\t int datasync);\n extern const struct inode_operations ext2_file_inode_operations;\n extern const struct file_operations ext2_file_operations;\n \ndiff --git a/fs/ext2/file.c b/fs/ext2/file.c\nindex 8dca9ec4cacd9a..b9020df7d89e03 100644\n--- a/fs/ext2/file.c\n+++ b/fs/ext2/file.c\n@@ -47,21 +47,6 @@ static int ext2_release_file (struct inode * inode, struct file * filp)\n \treturn 0;\n }\n \n-int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)\n-{\n-\tint ret;\n-\tstruct inode *inode = file-\u003ef_mapping-\u003ehost;\n-\tstruct super_block *sb = inode-\u003ei_sb;\n-\n-\tret = mmb_fsync(file, \u0026EXT2_I(inode)-\u003ei_metadata_bhs,\n-\t\t\tstart, end, datasync);\n-\tif (ret == -EIO)\n-\t\t/* We don't really know where the IO error happened... */\n-\t\text2_error(sb, __func__,\n-\t\t\t \"detected IO error when writing metadata buffers\");\n-\treturn ret;\n-}\n-\n static ssize_t ext2_dio_read_iter(struct kiocb *iocb, struct iov_iter *to)\n {\n \tstruct file *file = iocb-\u003eki_filp;\n@@ -213,7 +198,7 @@ const struct file_operations ext2_file_operations = {\n \t.mmap_prepare\t= generic_file_mmap_prepare,\n \t.open\t\t= ext2_file_open,\n \t.release\t= ext2_release_file,\n-\t.fsync\t\t= ext2_fsync,\n+\t.fsync\t\t= simple_fsync,\n \t.get_unmapped_area = thp_get_unmapped_area,\n \t.splice_read\t= filemap_splice_read,\n \t.splice_write\t= iter_file_splice_write,\ndiff --git a/fs/ext2/inode.c b/fs/ext2/inode.c\nindex 29808629cce56f..99acd79a4af272 100644\n--- a/fs/ext2/inode.c\n+++ b/fs/ext2/inode.c\n@@ -39,8 +39,6 @@\n #include \"acl.h\"\n #include \"xattr.h\"\n \n-static int __ext2_write_inode(struct inode *inode, int do_sync);\n-\n /*\n * Test whether an inode is a fast symlink.\n */\n@@ -87,7 +85,7 @@ void ext2_evict_inode(struct inode * inode)\n \t\t/* set dtime */\n \t\tEXT2_I(inode)-\u003ei_dtime\t= ktime_get_real_seconds();\n \t\tmark_inode_dirty(inode);\n-\t\t__ext2_write_inode(inode, inode_needs_sync(inode));\n+\t\tsync_inode_metadata(inode, inode_needs_sync(inode));\n \t\t/* truncate to 0 */\n \t\tinode-\u003ei_size = 0;\n \t\tif (inode-\u003ei_blocks)\n@@ -1258,12 +1256,9 @@ static int ext2_setsize(struct inode *inode, loff_t newsize)\n \tfilemap_invalidate_unlock(inode-\u003ei_mapping);\n \n \tinode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));\n-\tif (inode_needs_sync(inode)) {\n-\t\tmmb_sync(\u0026EXT2_I(inode)-\u003ei_metadata_bhs);\n+\tmark_inode_dirty(inode);\n+\tif (inode_needs_sync(inode))\n \t\tsync_inode_metadata(inode, 1);\n-\t} else {\n-\t\tmark_inode_dirty(inode);\n-\t}\n \n \treturn 0;\n }\n@@ -1469,7 +1464,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)\n \treturn ERR_PTR(ret);\n }\n \n-static int __ext2_write_inode(struct inode *inode, int do_sync)\n+int ext2_write_inode(struct inode *inode, struct writeback_control *wbc)\n {\n \tstruct ext2_inode_info *ei = EXT2_I(inode);\n \tstruct super_block *sb = inode-\u003ei_sb;\n@@ -1560,22 +1555,33 @@ static int __ext2_write_inode(struct inode *inode, int do_sync)\n \t} else for (n = 0; n \u003c EXT2_N_BLOCKS; n++)\n \t\traw_inode-\u003ei_block[n] = ei-\u003ei_data[n];\n \tmark_buffer_dirty(bh);\n-\tif (do_sync) {\n-\t\tsync_dirty_buffer(bh);\n-\t\tif (buffer_req(bh) \u0026\u0026 !buffer_uptodate(bh)) {\n-\t\t\tprintk (\"IO error syncing ext2 inode [%s:%08lx]\\n\",\n-\t\t\t\tsb-\u003es_id, (unsigned long) ino);\n-\t\t\terr = -EIO;\n-\t\t}\n-\t}\n \tei-\u003ei_state \u0026= ~EXT2_STATE_NEW;\n \tbrelse (bh);\n+\tset_inode_metadata_writeback(inode);\n \treturn err;\n }\n \n-int ext2_write_inode(struct inode *inode, struct writeback_control *wbc)\n+int ext2_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)\n {\n-\treturn __ext2_write_inode(inode, wbc-\u003esync_mode == WB_SYNC_ALL);\n+\tstruct buffer_head *bh;\n+\tstruct ext2_inode *raw_inode = ext2_get_inode(inode-\u003ei_sb, inode-\u003ei_ino,\n+\t\t\t\t\t\t \u0026bh);\n+\tint err = 0;\n+\n+\tif (IS_ERR(raw_inode))\n+\t\treturn -EIO;\n+\terr = mmb_sync(\u0026EXT2_I(inode)-\u003ei_metadata_bhs);\n+\tif (err)\n+\t\tgoto out;\n+\tsync_dirty_buffer(bh);\n+\tif (buffer_write_io_error(bh)) {\n+\t\tprintk(\"IO error syncing ext2 inode [%s:%08lx]\\n\",\n+\t\t inode-\u003ei_sb-\u003es_id, (unsigned long)inode-\u003ei_ino);\n+\t\terr = -EIO;\n+\t}\n+out:\n+\tbrelse(bh);\n+\treturn err;\n }\n \n int ext2_getattr(struct mnt_idmap *idmap, const struct path *path,\ndiff --git a/fs/ext2/super.c b/fs/ext2/super.c\nindex 3999f8f3b156e3..a40f530872a407 100644\n--- a/fs/ext2/super.c\n+++ b/fs/ext2/super.c\n@@ -362,6 +362,7 @@ static const struct super_operations ext2_sops = {\n \t.alloc_inode\t= ext2_alloc_inode,\n \t.free_inode\t= ext2_free_in_core_inode,\n \t.write_inode\t= ext2_write_inode,\n+\t.sync_inode_metadata = ext2_sync_inode_metadata,\n \t.evict_inode\t= ext2_evict_inode,\n \t.put_super\t= ext2_put_super,\n \t.sync_fs\t= ext2_sync_fs,\ndiff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c\nindex e55d16abf422f4..be63f89402a381 100644\n--- a/fs/ext2/xattr.c\n+++ b/fs/ext2/xattr.c\n@@ -777,6 +777,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,\n \t/* Update the inode. */\n \tEXT2_I(inode)-\u003ei_file_acl = new_bh ? new_bh-\u003eb_blocknr : 0;\n \tinode_set_ctime_current(inode);\n+\tmark_inode_dirty(inode);\n \tif (IS_SYNC(inode)) {\n \t\terror = sync_inode_metadata(inode, 1);\n \t\t/* In case sync failed due to ENOSPC the inode was actually\n@@ -789,8 +790,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,\n \t\t\t}\n \t\t\tgoto cleanup;\n \t\t}\n-\t} else\n-\t\tmark_inode_dirty(inode);\n+\t}\n \n \terror = 0;\n \tif (old_bh \u0026\u0026 old_bh != new_bh) {\ndiff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h\nindex b37c136ea3ab33..0f06155a35a694 100644\n--- a/fs/ext4/ext4.h\n+++ b/fs/ext4/ext4.h\n@@ -1151,7 +1151,7 @@ struct ext4_inode_info {\n \tstruct rw_semaphore i_data_sem;\n \tstruct inode vfs_inode;\n \tstruct jbd2_inode *jinode;\n-\tstruct mapping_metadata_bhs i_metadata_bhs;\n+\tstruct mapping_metadata_bhs *i_metadata_bhs;\n \n \t/*\n \t * File creation time. Its function is same as that of\n@@ -2126,6 +2126,17 @@ static inline bool ext4_inode_orphan_tracked(struct inode *inode)\n \t\t!list_empty(\u0026EXT4_I(inode)-\u003ei_orphan);\n }\n \n+static inline struct mapping_metadata_bhs *ext4_i_metadata_bhs(\n+\t\t\t\t\t\t\tstruct inode *inode)\n+{\n+\t/*\n+\t * i_metadata_bhs is set in ext4_inode_attach_mmb() using cmpxchg().\n+\t * We use READ_ONCE when accessing i_metadata_bhs to make sure we get\n+\t * consistent view for all accesses.\n+\t */\n+\treturn READ_ONCE(EXT4_I(inode)-\u003ei_metadata_bhs);\n+}\n+\n /*\n * Codes for operating systems\n */\n@@ -3155,6 +3166,7 @@ extern struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,\n \t__ext4_iget((sb), (ino), (flags), __func__, __LINE__)\n \n extern int ext4_write_inode(struct inode *, struct writeback_control *);\n+extern int ext4_sync_inode_metadata(struct inode *, struct writeback_control *);\n extern int ext4_setattr(struct mnt_idmap *, struct dentry *,\n \t\t\t struct iattr *);\n extern u32 ext4_dio_alignment(struct inode *inode);\ndiff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c\nindex 9a8c225f275309..3b2f8ac20bf85b 100644\n--- a/fs/ext4/ext4_jbd2.c\n+++ b/fs/ext4/ext4_jbd2.c\n@@ -350,6 +350,21 @@ int __ext4_journal_get_create_access(const char *where, unsigned int line,\n \treturn 0;\n }\n \n+static void ext4_inode_attach_mmb(struct inode *inode)\n+{\n+\tstruct mapping_metadata_bhs *mmb;\n+\n+\t/*\n+\t * It's difficult to handle failure when marking buffer dirty without\n+\t * leaving filesystem corrupted\n+\t */\n+\tmmb = kmalloc_obj(*mmb, GFP_NOFS | __GFP_NOFAIL);\n+\tmmb_init(mmb, \u0026inode-\u003ei_data);\n+\t/* Someone swapped another mmb before us? */\n+\tif (cmpxchg(\u0026EXT4_I(inode)-\u003ei_metadata_bhs, NULL, mmb))\n+\t\tkfree(mmb);\n+}\n+\n int __ext4_handle_dirty_metadata(const char *where, unsigned int line,\n \t\t\t\t handle_t *handle, struct inode *inode,\n \t\t\t\t struct buffer_head *bh)\n@@ -389,11 +404,13 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,\n \t\t\t\t\t err);\n \t\t}\n \t} else {\n-\t\tif (inode)\n-\t\t\tmmb_mark_buffer_dirty(bh,\n-\t\t\t\t\t \u0026EXT4_I(inode)-\u003ei_metadata_bhs);\n-\t\telse\n+\t\tif (inode) {\n+\t\t\tif (!ext4_i_metadata_bhs(inode))\n+\t\t\t\text4_inode_attach_mmb(inode);\n+\t\t\tmmb_mark_buffer_dirty(bh, ext4_i_metadata_bhs(inode));\n+\t\t} else {\n \t\t\tmark_buffer_dirty(bh);\n+\t\t}\n \t\tif (inode \u0026\u0026 inode_needs_sync(inode)) {\n \t\t\tsync_dirty_buffer(bh);\n \t\t\tif (buffer_req(bh) \u0026\u0026 !buffer_uptodate(bh)) {\ndiff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c\nindex 924726dcc85ff5..2999c2cc8fcfa0 100644\n--- a/fs/ext4/fsync.c\n+++ b/fs/ext4/fsync.c\n@@ -68,9 +68,6 @@ static int ext4_sync_parent(struct inode *inode)\n \t\t * through ext4_evict_inode()) and so we are safe to flush\n \t\t * metadata blocks and the inode.\n \t\t */\n-\t\tret = mmb_sync(\u0026EXT4_I(inode)-\u003ei_metadata_bhs);\n-\t\tif (ret)\n-\t\t\tbreak;\n \t\tret = sync_inode_metadata(inode, 1);\n \t\tif (ret)\n \t\t\tbreak;\n@@ -83,22 +80,11 @@ static int ext4_fsync_nojournal(struct file *file, loff_t start, loff_t end,\n \t\t\t\tint datasync, bool *needs_barrier)\n {\n \tstruct inode *inode = file-\u003ef_inode;\n-\tstruct writeback_control wbc = {\n-\t\t.sync_mode = WB_SYNC_ALL,\n-\t\t.nr_to_write = 0,\n-\t};\n \tint ret;\n \n-\tret = mmb_fsync_noflush(file, \u0026EXT4_I(inode)-\u003ei_metadata_bhs,\n-\t\t\t\tstart, end, datasync);\n+\tret = sync_inode_metadata(inode, 1);\n \tif (ret)\n \t\treturn ret;\n-\n-\t/* Force writeout of inode table buffer to disk */\n-\tret = ext4_write_inode(inode, \u0026wbc);\n-\tif (ret)\n-\t\treturn ret;\n-\n \tret = ext4_sync_parent(inode);\n \n \tif (test_opt(inode-\u003ei_sb, BARRIER))\n@@ -156,6 +142,10 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)\n \tif (sb_rdonly(inode-\u003ei_sb))\n \t\tgoto out;\n \n+\tret = file_write_and_wait_range(file, start, end);\n+\tif (ret)\n+\t\tgoto out;\n+\n \tif (!EXT4_SB(inode-\u003ei_sb)-\u003es_journal) {\n \t\tret = ext4_fsync_nojournal(file, start, end, datasync,\n \t\t\t\t\t \u0026needs_barrier);\n@@ -164,10 +154,6 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)\n \t\tgoto out;\n \t}\n \n-\tret = file_write_and_wait_range(file, start, end);\n-\tif (ret)\n-\t\tgoto out;\n-\n \t/*\n \t * The caller's filemap_fdatawrite()/wait will sync the data.\n \t * Metadata is in the journal, we wait for proper transaction to\ndiff --git a/fs/ext4/inode.c b/fs/ext4/inode.c\nindex ce99807c5f5b2c..0c220b535590f4 100644\n--- a/fs/ext4/inode.c\n+++ b/fs/ext4/inode.c\n@@ -186,6 +186,8 @@ void ext4_evict_inode(struct inode *inode)\n \tif (EXT4_I(inode)-\u003ei_flags \u0026 EXT4_EA_INODE_FL)\n \t\text4_evict_ea_inode(inode);\n \tif (inode-\u003ei_nlink) {\n+\t\tstruct mapping_metadata_bhs *mmb;\n+\n \t\t/*\n \t\t * If there's dirty page will lead to data loss, user\n \t\t * could see stale data.\n@@ -195,9 +197,9 @@ void ext4_evict_inode(struct inode *inode)\n \t\t\text4_warning_inode(inode, \"data will be lost\");\n \n \t\ttruncate_inode_pages_final(\u0026inode-\u003ei_data);\n-\t\t/* Avoid mballoc special inode which has no proper iops */\n-\t\tif (!EXT4_SB(inode-\u003ei_sb)-\u003es_journal)\n-\t\t\tmmb_sync(\u0026EXT4_I(inode)-\u003ei_metadata_bhs);\n+\t\tmmb = ext4_i_metadata_bhs(inode);\n+\t\tif (mmb)\n+\t\t\tmmb_sync(mmb);\n \t\tgoto no_delete;\n \t}\n \n@@ -3452,6 +3454,7 @@ static bool ext4_release_folio(struct folio *folio, gfp_t wait)\n static bool ext4_inode_datasync_dirty(struct inode *inode)\n {\n \tjournal_t *journal = EXT4_SB(inode-\u003ei_sb)-\u003es_journal;\n+\tstruct mapping_metadata_bhs *mmb;\n \n \tif (journal) {\n \t\tif (jbd2_transaction_committed(journal,\n@@ -3462,8 +3465,9 @@ static bool ext4_inode_datasync_dirty(struct inode *inode)\n \t\treturn true;\n \t}\n \n+\tmmb = ext4_i_metadata_bhs(inode);\n \t/* Any metadata buffers to write? */\n-\tif (mmb_has_buffers(\u0026EXT4_I(inode)-\u003ei_metadata_bhs))\n+\tif (mmb \u0026\u0026 mmb_has_buffers(mmb))\n \t\treturn true;\n \treturn inode_state_read_once(inode) \u0026 I_DIRTY_DATASYNC;\n }\n@@ -5838,24 +5842,38 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)\n \t\terr = ext4_fc_commit(EXT4_SB(inode-\u003ei_sb)-\u003es_journal,\n \t\t\t\t\t\tEXT4_I(inode)-\u003ei_sync_tid);\n \t} else {\n-\t\tstruct ext4_iloc iloc;\n+\t\tset_inode_metadata_writeback(inode);\n+\t}\n+\treturn err;\n+}\n \n-\t\terr = __ext4_get_inode_loc_noinmem(inode, \u0026iloc);\n+int ext4_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)\n+{\n+\tstruct ext4_iloc iloc;\n+\tstruct mapping_metadata_bhs *mmb;\n+\tint err;\n+\n+\t/* We should only get here in nojournal mode */\n+\tif (WARN_ON_ONCE(EXT4_SB(inode-\u003ei_sb)-\u003es_journal))\n+\t\treturn -EFSCORRUPTED;\n+\n+\terr = __ext4_get_inode_loc_noinmem(inode, \u0026iloc);\n+\tif (err)\n+\t\treturn err;\n+\tmmb = READ_ONCE(EXT4_I(inode)-\u003ei_metadata_bhs);\n+\tif (mmb) {\n+\t\terr = mmb_sync(mmb);\n \t\tif (err)\n-\t\t\treturn err;\n-\t\t/*\n-\t\t * sync(2) will flush the whole buffer cache. No need to do\n-\t\t * it here separately for each inode.\n-\t\t */\n-\t\tif (wbc-\u003esync_mode == WB_SYNC_ALL \u0026\u0026 !wbc-\u003efor_sync)\n-\t\t\tsync_dirty_buffer(iloc.bh);\n-\t\tif (buffer_req(iloc.bh) \u0026\u0026 !buffer_uptodate(iloc.bh)) {\n-\t\t\text4_error_inode_block(inode, iloc.bh-\u003eb_blocknr, EIO,\n-\t\t\t\t\t \"IO error syncing inode\");\n-\t\t\terr = -EIO;\n-\t\t}\n-\t\tbrelse(iloc.bh);\n+\t\t\tgoto out;\n+\t}\n+\tsync_dirty_buffer(iloc.bh);\n+\tif (buffer_write_io_error(iloc.bh)) {\n+\t\text4_error_inode_block(inode, iloc.bh-\u003eb_blocknr, EIO,\n+\t\t\t\t \"IO error syncing inode\");\n+\t\terr = -EIO;\n \t}\n+out:\n+\tbrelse(iloc.bh);\n \treturn err;\n }\n \ndiff --git a/fs/ext4/super.c b/fs/ext4/super.c\nindex 245f67d10ded34..ae33f5bcb13314 100644\n--- a/fs/ext4/super.c\n+++ b/fs/ext4/super.c\n@@ -1430,7 +1430,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)\n \tINIT_WORK(\u0026ei-\u003ei_rsv_conversion_work, ext4_end_io_rsv_work);\n \text4_fc_init_inode(\u0026ei-\u003evfs_inode);\n \tspin_lock_init(\u0026ei-\u003ei_fc_lock);\n-\tmmb_init(\u0026ei-\u003ei_metadata_bhs, \u0026ei-\u003evfs_inode.i_data);\n+\tei-\u003ei_metadata_bhs = NULL;\n #ifdef CONFIG_LOCKDEP\n \tlockdep_set_subclass(\u0026ei-\u003ei_data_sem, I_DATA_SEM_NORMAL);\n #endif\n@@ -1451,6 +1451,7 @@ static int ext4_drop_inode(struct inode *inode)\n static void ext4_free_in_core_inode(struct inode *inode)\n {\n \tfscrypt_free_inode(inode);\n+\tkfree(ext4_i_metadata_bhs(inode));\n \tif (!list_empty(\u0026(EXT4_I(inode)-\u003ei_fc_list))) {\n \t\tpr_warn(\"%s: inode %llu still in fc list\",\n \t\t\t__func__, inode-\u003ei_ino);\n@@ -1529,9 +1530,11 @@ static void destroy_inodecache(void)\n \n void ext4_clear_inode(struct inode *inode)\n {\n+\tstruct mapping_metadata_bhs *mmb = ext4_i_metadata_bhs(inode);\n+\n \text4_fc_del(inode);\n-\tif (!EXT4_SB(inode-\u003ei_sb)-\u003es_journal)\n-\t\tmmb_invalidate(\u0026EXT4_I(inode)-\u003ei_metadata_bhs);\n+\tif (mmb)\n+\t\tmmb_invalidate(mmb);\n \tclear_inode(inode);\n \text4_discard_preallocations(inode);\n \t/*\n@@ -1605,9 +1608,13 @@ static int ext4_nfs_commit_metadata(struct inode *inode)\n \tstruct writeback_control wbc = {\n \t\t.sync_mode = WB_SYNC_ALL\n \t};\n+\tint ret;\n \n \ttrace_ext4_nfs_commit_metadata(inode);\n-\treturn ext4_write_inode(inode, \u0026wbc);\n+\tret = ext4_write_inode(inode, \u0026wbc);\n+\tif (!ret \u0026\u0026 inode_state_read_once(inode) \u0026 I_METADATA_WRITEBACK)\n+\t\tret = ext4_sync_inode_metadata(inode, \u0026wbc);\n+\treturn ret;\n }\n \n #ifdef CONFIG_QUOTA\n@@ -1664,6 +1671,7 @@ static const struct super_operations ext4_sops = {\n \t.free_inode\t= ext4_free_in_core_inode,\n \t.destroy_inode\t= ext4_destroy_inode,\n \t.write_inode\t= ext4_write_inode,\n+\t.sync_inode_metadata = ext4_sync_inode_metadata,\n \t.dirty_inode\t= ext4_dirty_inode,\n \t.drop_inode\t= ext4_drop_inode,\n \t.evict_inode\t= ext4_evict_inode,\ndiff --git a/fs/fat/dir.c b/fs/fat/dir.c\nindex c6cca5d00ffd7e..916f5c4cdcf390 100644\n--- a/fs/fat/dir.c\n+++ b/fs/fat/dir.c\n@@ -1053,11 +1053,7 @@ static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots)\n \t\t\tnr_slots--;\n \t\t}\n \t\tmmb_mark_buffer_dirty(bh, \u0026MSDOS_I(dir)-\u003ei_metadata_bhs);\n-\t\tif (IS_DIRSYNC(dir))\n-\t\t\terr = sync_dirty_buffer(bh);\n \t\tbrelse(bh);\n-\t\tif (err)\n-\t\t\tbreak;\n \n \t\t/* pos is *next* de's position, so this does `- sizeof(de)' */\n \t\tpos += ((orig_slots - nr_slots) * sizeof(*de)) - sizeof(*de);\n@@ -1088,11 +1084,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)\n \t\tnr_slots--;\n \t}\n \tmmb_mark_buffer_dirty(bh, \u0026MSDOS_I(dir)-\u003ei_metadata_bhs);\n-\tif (IS_DIRSYNC(dir))\n-\t\terr = sync_dirty_buffer(bh);\n \tbrelse(bh);\n-\tif (err)\n-\t\treturn err;\n \tinode_inc_iversion(dir);\n \n \tif (nr_slots) {\n@@ -1109,12 +1101,12 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)\n \t}\n \n \tfat_truncate_time(dir, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);\n+\terr = 0;\n+\tmark_inode_dirty(dir);\n \tif (IS_DIRSYNC(dir))\n-\t\t(void)fat_sync_inode(dir);\n-\telse\n-\t\tmark_inode_dirty(dir);\n+\t\terr = sync_inode_metadata(dir, 1);\n \n-\treturn 0;\n+\treturn err;\n }\n EXPORT_SYMBOL_GPL(fat_remove_entries);\n \n@@ -1144,21 +1136,11 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,\n \t\tn++;\n \t\tblknr++;\n \t\tif (n == nr_bhs) {\n-\t\t\tif (IS_DIRSYNC(dir)) {\n-\t\t\t\terr = fat_sync_bhs(bhs, n);\n-\t\t\t\tif (err)\n-\t\t\t\t\tgoto error;\n-\t\t\t}\n \t\t\tfor (i = 0; i \u003c n; i++)\n \t\t\t\tbrelse(bhs[i]);\n \t\t\tn = 0;\n \t\t}\n \t}\n-\tif (IS_DIRSYNC(dir)) {\n-\t\terr = fat_sync_bhs(bhs, n);\n-\t\tif (err)\n-\t\t\tgoto error;\n-\t}\n \tfor (i = 0; i \u003c n; i++)\n \t\tbrelse(bhs[i]);\n \ndiff --git a/fs/fat/fat.h b/fs/fat/fat.h\nindex 99ed9228a677be..dcb5ba757073ed 100644\n--- a/fs/fat/fat.h\n+++ b/fs/fat/fat.h\n@@ -421,7 +421,6 @@ extern void fat_detach(struct inode *inode);\n extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos);\n extern struct inode *fat_build_inode(struct super_block *sb,\n \t\t\tstruct msdos_dir_entry *de, loff_t i_pos);\n-extern int fat_sync_inode(struct inode *inode);\n extern int fat_fill_super(struct super_block *sb, struct fs_context *fc,\n \t\t\t void (*setup)(struct super_block *));\n extern int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);\ndiff --git a/fs/fat/file.c b/fs/fat/file.c\nindex 37e7049b4c8c44..1c835ca5f21a51 100644\n--- a/fs/fat/file.c\n+++ b/fs/fat/file.c\n@@ -190,8 +190,7 @@ int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)\n \tstruct inode *fat_inode = MSDOS_SB(inode-\u003ei_sb)-\u003efat_inode;\n \tint err;\n \n-\terr = mmb_fsync_noflush(filp, \u0026MSDOS_I(inode)-\u003ei_metadata_bhs,\n-\t\t\t\tstart, end, datasync);\n+\terr = simple_fsync_noflush(filp, start, end, datasync);\n \tif (err)\n \t\treturn err;\n \n@@ -332,15 +331,15 @@ static int fat_free(struct inode *inode, int skip)\n \t}\n \tMSDOS_I(inode)-\u003ei_attrs |= ATTR_ARCH;\n \tfat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME);\n+\tmark_inode_dirty(inode);\n \tif (wait) {\n-\t\terr = fat_sync_inode(inode);\n+\t\terr = sync_inode_metadata(inode, 1);\n \t\tif (err) {\n \t\t\tMSDOS_I(inode)-\u003ei_start = i_start;\n \t\t\tMSDOS_I(inode)-\u003ei_logstart = i_logstart;\n \t\t\treturn err;\n \t\t}\n-\t} else\n-\t\tmark_inode_dirty(inode);\n+\t}\n \n \t/* Write a new EOF, and get the remaining cluster chain for freeing. */\n \tif (skip) {\ndiff --git a/fs/fat/inode.c b/fs/fat/inode.c\nindex 3aa52481ad5cf3..e3bb7b4713f2ac 100644\n--- a/fs/fat/inode.c\n+++ b/fs/fat/inode.c\n@@ -623,7 +623,34 @@ struct inode *fat_build_inode(struct super_block *sb,\n \n EXPORT_SYMBOL_GPL(fat_build_inode);\n \n-static int __fat_write_inode(struct inode *inode, int wait);\n+static int __fat_write_inode(struct inode *inode);\n+\n+static int fat_sync_inode_metadata(struct inode *inode,\n+\t\t\t\t struct writeback_control *wbc)\n+{\n+\tstruct msdos_sb_info *sbi = MSDOS_SB(inode-\u003ei_sb);\n+\tstruct buffer_head *bh;\n+\tloff_t i_pos;\n+\tsector_t blocknr;\n+\tint offset;\n+\n+\tif (inode-\u003ei_ino == MSDOS_ROOT_INO)\n+\t\treturn 0;\n+\ti_pos = fat_i_pos_read(sbi, inode);\n+\tif (!i_pos)\n+\t\treturn 0;\n+\n+\tfat_get_blknr_offset(sbi, i_pos, \u0026blocknr, \u0026offset);\n+\tbh = sb_find_get_block_nonatomic(inode-\u003ei_sb, blocknr);\n+\t/*\n+\t * Buffer present? We leave buffer_dirty check for sync_dirty_buffer()\n+\t * for proper synchronization with ongoing IO.\n+\t */\n+\tif (bh \u0026\u0026 buffer_uptodate(bh))\n+\t\tsync_dirty_buffer(bh);\n+\tbrelse(bh);\n+\treturn mmb_sync(\u0026MSDOS_I(inode)-\u003ei_metadata_bhs);\n+}\n \n static void fat_free_eofblocks(struct inode *inode)\n {\n@@ -640,7 +667,7 @@ static void fat_free_eofblocks(struct inode *inode)\n \t\t * any corruption on the next access to the cluster\n \t\t * chain for the file.\n \t\t */\n-\t\terr = __fat_write_inode(inode, inode_needs_sync(inode));\n+\t\terr = sync_inode_metadata(inode, inode_needs_sync(inode));\n \t\tif (err) {\n \t\t\tfat_msg(inode-\u003ei_sb, KERN_WARNING, \"Failed to \"\n \t\t\t\t\t\"update on disk inode for unused \"\n@@ -854,7 +881,7 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)\n \treturn 0;\n }\n \n-static int __fat_write_inode(struct inode *inode, int wait)\n+static int __fat_write_inode(struct inode *inode)\n {\n \tstruct super_block *sb = inode-\u003ei_sb;\n \tstruct msdos_sb_info *sbi = MSDOS_SB(sb);\n@@ -863,7 +890,7 @@ static int __fat_write_inode(struct inode *inode, int wait)\n \tstruct timespec64 mtime;\n \tloff_t i_pos;\n \tsector_t blocknr;\n-\tint err, offset;\n+\tint offset;\n \n \tif (inode-\u003ei_ino == MSDOS_ROOT_INO)\n \t\treturn 0;\n@@ -907,11 +934,9 @@ static int __fat_write_inode(struct inode *inode, int wait)\n \t}\n \tspin_unlock(\u0026sbi-\u003einode_hash_lock);\n \tmark_buffer_dirty(bh);\n-\terr = 0;\n-\tif (wait)\n-\t\terr = sync_dirty_buffer(bh);\n \tbrelse(bh);\n-\treturn err;\n+\tset_inode_metadata_writeback(inode);\n+\treturn 0;\n }\n \n static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)\n@@ -925,23 +950,17 @@ static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)\n \t\terr = fat_clusters_flush(sb);\n \t\tmutex_unlock(\u0026MSDOS_SB(sb)-\u003es_lock);\n \t} else\n-\t\terr = __fat_write_inode(inode, wbc-\u003esync_mode == WB_SYNC_ALL);\n+\t\terr = __fat_write_inode(inode);\n \n \treturn err;\n }\n \n-int fat_sync_inode(struct inode *inode)\n-{\n-\treturn __fat_write_inode(inode, 1);\n-}\n-\n-EXPORT_SYMBOL_GPL(fat_sync_inode);\n-\n static int fat_show_options(struct seq_file *m, struct dentry *root);\n static const struct super_operations fat_sops = {\n \t.alloc_inode\t= fat_alloc_inode,\n \t.free_inode\t= fat_free_inode,\n \t.write_inode\t= fat_write_inode,\n+\t.sync_inode_metadata = fat_sync_inode_metadata,\n \t.evict_inode\t= fat_evict_inode,\n \t.put_super\t= fat_put_super,\n \t.statfs\t\t= fat_statfs,\ndiff --git a/fs/fat/misc.c b/fs/fat/misc.c\nindex 3027ef53af214c..be18f6b5819ba7 100644\n--- a/fs/fat/misc.c\n+++ b/fs/fat/misc.c\n@@ -146,16 +146,17 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)\n \t} else {\n \t\tMSDOS_I(inode)-\u003ei_start = new_dclus;\n \t\tMSDOS_I(inode)-\u003ei_logstart = new_dclus;\n+\t\tmark_inode_dirty(inode);\n \t\t/*\n \t\t * Since generic_write_sync() synchronizes regular files later,\n \t\t * we sync here only directories.\n \t\t */\n \t\tif (S_ISDIR(inode-\u003ei_mode) \u0026\u0026 IS_DIRSYNC(inode)) {\n-\t\t\tret = fat_sync_inode(inode);\n+\t\t\tret = sync_inode_metadata(inode, 1);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n-\t\t} else\n-\t\t\tmark_inode_dirty(inode);\n+\t\t}\n+\n \t}\n \tif (new_fclus != (inode-\u003ei_blocks \u003e\u003e (sbi-\u003ecluster_bits - 9))) {\n \t\tfat_fs_error_ratelimit(\ndiff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c\nindex 0fd2971ad4b131..868ef091048e40 100644\n--- a/fs/fat/namei_msdos.c\n+++ b/fs/fat/namei_msdos.c\n@@ -252,12 +252,11 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name,\n \t\treturn err;\n \n \tfat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);\n+\tmark_inode_dirty(dir);\n \tif (IS_DIRSYNC(dir))\n-\t\t(void)fat_sync_inode(dir);\n-\telse\n-\t\tmark_inode_dirty(dir);\n+\t\terr = sync_inode_metadata(dir, 1);\n \n-\treturn 0;\n+\treturn err;\n }\n \n /***** Create a file */\n@@ -473,21 +472,20 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,\n \t\t\t\tMSDOS_I(old_inode)-\u003ei_attrs |= ATTR_HIDDEN;\n \t\t\telse\n \t\t\t\tMSDOS_I(old_inode)-\u003ei_attrs \u0026= ~ATTR_HIDDEN;\n+\t\t\tmark_inode_dirty(old_inode);\n \t\t\tif (IS_DIRSYNC(old_dir)) {\n-\t\t\t\terr = fat_sync_inode(old_inode);\n+\t\t\t\terr = sync_inode_metadata(old_inode, 1);\n \t\t\t\tif (err) {\n \t\t\t\t\tMSDOS_I(old_inode)-\u003ei_attrs = old_attrs;\n \t\t\t\t\tgoto out;\n \t\t\t\t}\n-\t\t\t} else\n-\t\t\t\tmark_inode_dirty(old_inode);\n+\t\t\t}\n \n \t\t\tinode_inc_iversion(old_dir);\n \t\t\tfat_truncate_time(old_dir, NULL, FAT_UPDATE_CMTIME);\n+\t\t\tmark_inode_dirty(old_dir);\n \t\t\tif (IS_DIRSYNC(old_dir))\n-\t\t\t\t(void)fat_sync_inode(old_dir);\n-\t\t\telse\n-\t\t\t\tmark_inode_dirty(old_dir);\n+\t\t\t\terr = sync_inode_metadata(old_dir, 1);\n \t\t\tgoto out;\n \t\t}\n \t}\n@@ -518,26 +516,21 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,\n \t\tMSDOS_I(old_inode)-\u003ei_attrs |= ATTR_HIDDEN;\n \telse\n \t\tMSDOS_I(old_inode)-\u003ei_attrs \u0026= ~ATTR_HIDDEN;\n-\tif (IS_DIRSYNC(new_dir)) {\n-\t\terr = fat_sync_inode(old_inode);\n-\t\tif (err)\n-\t\t\tgoto error_inode;\n-\t} else\n-\t\tmark_inode_dirty(old_inode);\n-\n \tif (update_dotdot) {\n \t\tfat_set_start(dotdot_de, MSDOS_I(new_dir)-\u003ei_logstart);\n \t\tmmb_mark_buffer_dirty(dotdot_bh,\n \t\t\t\t \u0026MSDOS_I(old_inode)-\u003ei_metadata_bhs);\n-\t\tif (IS_DIRSYNC(new_dir)) {\n-\t\t\terr = sync_dirty_buffer(dotdot_bh);\n-\t\t\tif (err)\n-\t\t\t\tgoto error_dotdot;\n-\t\t}\n \t\tdrop_nlink(old_dir);\n \t\tif (!new_inode)\n \t\t\tinc_nlink(new_dir);\n \t}\n+\tmark_inode_dirty(old_inode);\n+\tif (IS_DIRSYNC(new_dir)) {\n+\t\terr = sync_inode_metadata(old_inode, 1);\n+\t\tif (err)\n+\t\t\tgoto error_dotdot;\n+\t}\n+\n \n \terr = fat_remove_entries(old_dir, \u0026old_sinfo);\t/* and releases bh */\n \told_sinfo.bh = NULL;\n@@ -545,10 +538,9 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,\n \t\tgoto error_dotdot;\n \tinode_inc_iversion(old_dir);\n \tfat_truncate_time(old_dir, \u0026ts, FAT_UPDATE_CMTIME);\n+\tmark_inode_dirty(old_dir);\n \tif (IS_DIRSYNC(old_dir))\n-\t\t(void)fat_sync_inode(old_dir);\n-\telse\n-\t\tmark_inode_dirty(old_dir);\n+\t\terr = sync_inode_metadata(old_dir, 1);\n \n \tif (new_inode) {\n \t\tdrop_nlink(new_inode);\n@@ -571,14 +563,15 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,\n \t\t\t\t \u0026MSDOS_I(old_inode)-\u003ei_metadata_bhs);\n \t\tcorrupt |= sync_dirty_buffer(dotdot_bh);\n \t}\n-error_inode:\n \tfat_detach(old_inode);\n \tfat_attach(old_inode, old_sinfo.i_pos);\n \tMSDOS_I(old_inode)-\u003ei_attrs = old_attrs;\n \tif (new_inode) {\n \t\tfat_attach(new_inode, new_i_pos);\n-\t\tif (corrupt)\n-\t\t\tcorrupt |= fat_sync_inode(new_inode);\n+\t\tif (corrupt) {\n+\t\t\tmark_inode_dirty(new_inode);\n+\t\t\tcorrupt |= sync_inode_metadata(new_inode, 1);\n+\t\t}\n \t} else {\n \t\t/*\n \t\t * If new entry was not sharing the data cluster, it\ndiff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c\nindex e909447873e364..308158895b1d8a 100644\n--- a/fs/fat/namei_vfat.c\n+++ b/fs/fat/namei_vfat.c\n@@ -678,10 +678,9 @@ static int vfat_add_entry(struct inode *dir, const struct qstr *qname,\n \n \t/* update timestamp */\n \tfat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);\n+\tmark_inode_dirty(dir);\n \tif (IS_DIRSYNC(dir))\n-\t\t(void)fat_sync_inode(dir);\n-\telse\n-\t\tmark_inode_dirty(dir);\n+\t\terr = sync_inode_metadata(dir, 1);\n cleanup:\n \tkfree(slots);\n \treturn err;\n@@ -904,9 +903,9 @@ static int vfat_get_dotdot_de(struct inode *inode, struct buffer_head **bh,\n \n static int vfat_sync_ipos(struct inode *dir, struct inode *inode)\n {\n-\tif (IS_DIRSYNC(dir))\n-\t\treturn fat_sync_inode(inode);\n \tmark_inode_dirty(inode);\n+\tif (IS_DIRSYNC(dir))\n+\t\treturn sync_inode_metadata(inode, 1);\n \treturn 0;\n }\n \n@@ -921,14 +920,14 @@ static int vfat_update_dotdot_de(struct inode *dir, struct inode *inode,\n \treturn 0;\n }\n \n-static void vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts)\n+static int vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts)\n {\n \tinode_inc_iversion(dir);\n \tfat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);\n+\tmark_inode_dirty(dir);\n \tif (IS_DIRSYNC(dir))\n-\t\t(void)fat_sync_inode(dir);\n-\telse\n-\t\tmark_inode_dirty(dir);\n+\t\treturn sync_inode_metadata(dir, 1);\n+\treturn 0;\n }\n \n static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,\n@@ -996,7 +995,7 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,\n \told_sinfo.bh = NULL;\n \tif (err)\n \t\tgoto error_dotdot;\n-\tvfat_update_dir_metadata(old_dir, \u0026ts);\n+\terr = vfat_update_dir_metadata(old_dir, \u0026ts);\n \n \tif (new_inode) {\n \t\tdrop_nlink(new_inode);\n@@ -1024,8 +1023,10 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,\n \tfat_attach(old_inode, old_sinfo.i_pos);\n \tif (new_inode) {\n \t\tfat_attach(new_inode, new_i_pos);\n-\t\tif (corrupt)\n-\t\t\tcorrupt |= fat_sync_inode(new_inode);\n+\t\tif (corrupt) {\n+\t\t\tmark_inode_dirty(new_inode);\n+\t\t\tcorrupt |= sync_inode_metadata(new_inode, 1);\n+\t\t}\n \t} else {\n \t\t/*\n \t\t * If new entry was not sharing the data cluster, it\n@@ -1123,10 +1124,13 @@ static int vfat_rename_exchange(struct inode *old_dir, struct dentry *old_dentry\n \t\t\tvfat_move_nlink(new_dir, old_dir);\n \t}\n \n-\tvfat_update_dir_metadata(old_dir, \u0026ts);\n+\terr = vfat_update_dir_metadata(old_dir, \u0026ts);\n \t/* if directories are not the same, update new_dir as well */\n-\tif (old_dir != new_dir)\n-\t\tvfat_update_dir_metadata(new_dir, \u0026ts);\n+\tif (old_dir != new_dir) {\n+\t\tint err2 = vfat_update_dir_metadata(new_dir, \u0026ts);\n+\t\tif (!err)\n+\t\t\terr = err2;\n+\t}\n \n out:\n \tbrelse(old_dotdot_bh);\ndiff --git a/fs/fs-writeback.c b/fs/fs-writeback.c\nindex fdb8766d275a17..9d96357731a3d4 100644\n--- a/fs/fs-writeback.c\n+++ b/fs/fs-writeback.c\n@@ -1851,6 +1851,22 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)\n \t\tif (ret == 0)\n \t\t\tret = err;\n \t}\n+\n+\t/*\n+\t * Do we need to wait for inode metadata IO possibly submitted\n+\t * by previous WB_SYNC_NONE writeback?\n+\t */\n+\tif (wbc-\u003esync_mode == WB_SYNC_ALL \u0026\u0026 !wbc-\u003efor_sync \u0026\u0026\n+\t inode_state_read_once(inode) \u0026 I_METADATA_WRITEBACK) {\n+\t\tint err;\n+\n+\t\tspin_lock(\u0026inode-\u003ei_lock);\n+\t\tinode_state_clear(inode, I_METADATA_WRITEBACK);\n+\t\tspin_unlock(\u0026inode-\u003ei_lock);\n+\t\terr = inode-\u003ei_sb-\u003es_op-\u003esync_inode_metadata(inode, wbc);\n+\t\tif (ret == 0)\n+\t\t\tret = err;\n+\t}\n \twbc-\u003eunpinned_netfs_wb = false;\n \ttrace_writeback_single_inode(inode, wbc, nr_to_write);\n \treturn ret;\n@@ -1892,14 +1908,17 @@ static int writeback_single_inode(struct inode *inode,\n \t/*\n \t * If the inode is already fully clean, then there's nothing to do.\n \t *\n-\t * For data-integrity syncs we also need to check whether any pages are\n-\t * still under writeback, e.g. due to prior WB_SYNC_NONE writeback. If\n-\t * there are any such pages, we'll need to wait for them.\n+\t * For data-integrity syncs we also need to check whether any folios or\n+\t * metadata are still under writeback, e.g. due to prior WB_SYNC_NONE\n+\t * writeback. If there, we'll need to wait for them.\n \t */\n-\tif (!(inode_state_read(inode) \u0026 I_DIRTY_ALL) \u0026\u0026\n-\t (wbc-\u003esync_mode != WB_SYNC_ALL ||\n-\t !mapping_tagged(inode-\u003ei_mapping, PAGECACHE_TAG_WRITEBACK)))\n-\t\tgoto out;\n+\tif (!(inode_state_read(inode) \u0026 I_DIRTY_ALL)) {\n+\t\tif (wbc-\u003esync_mode != WB_SYNC_ALL)\n+\t\t\tgoto out;\n+\t\tif (!mapping_tagged(inode-\u003ei_mapping, PAGECACHE_TAG_WRITEBACK) \u0026\u0026\n+\t\t !(inode_state_read(inode) \u0026 I_METADATA_WRITEBACK))\n+\t\t\tgoto out;\n+\t}\n \tinode_state_set(inode, I_SYNC);\n \twbc_attach_and_unlock_inode(wbc, inode);\n \ndiff --git a/fs/libfs.c b/fs/libfs.c\nindex 5a0d276379d13b..27d7dc16fcb0a0 100644\n--- a/fs/libfs.c\n+++ b/fs/libfs.c\n@@ -1559,9 +1559,12 @@ int simple_fsync_noflush(struct file *file, loff_t start, loff_t end,\n \tif (err)\n \t\treturn err;\n \n-\tif (!(inode_state_read_once(inode) \u0026 I_DIRTY_ALL))\n+\tif (!(inode_state_read_once(inode) \u0026\n+\t\t\t(I_DIRTY_ALL | I_SYNC | I_METADATA_WRITEBACK)))\n \t\tgoto out;\n-\tif (datasync \u0026\u0026 !(inode_state_read_once(inode) \u0026 I_DIRTY_DATASYNC))\n+\tif (datasync \u0026\u0026\n+\t !(inode_state_read_once(inode) \u0026\n+\t\t\t(I_DIRTY_DATASYNC | I_SYNC | I_METADATA_WRITEBACK)))\n \t\tgoto out;\n \n \tret = sync_inode_metadata(inode, 1);\ndiff --git a/fs/minix/dir.c b/fs/minix/dir.c\nindex 361d26d87d2e2d..2ca16f849d5ac0 100644\n--- a/fs/minix/dir.c\n+++ b/fs/minix/dir.c\n@@ -23,7 +23,7 @@ const struct file_operations minix_dir_operations = {\n \t.llseek\t\t= generic_file_llseek,\n \t.read\t\t= generic_read_dir,\n \t.iterate_shared\t= minix_readdir,\n-\t.fsync\t\t= minix_fsync,\n+\t.fsync\t\t= simple_fsync,\n };\n \n /*\ndiff --git a/fs/minix/file.c b/fs/minix/file.c\nindex 86e5943cd2ff74..02aabbdb5deaa0 100644\n--- a/fs/minix/file.c\n+++ b/fs/minix/file.c\n@@ -10,13 +10,6 @@\n #include \u003clinux/buffer_head.h\u003e\n #include \"minix.h\"\n \n-int minix_fsync(struct file *file, loff_t start, loff_t end, int datasync)\n-{\n-\treturn mmb_fsync(file,\n-\t\t\t\u0026minix_i(file-\u003ef_mapping-\u003ehost)-\u003ei_metadata_bhs,\n-\t\t\tstart, end, datasync);\n-}\n-\n /*\n * We have mostly NULLs here: the current defaults are OK for\n * the minix filesystem.\n@@ -26,7 +19,7 @@ const struct file_operations minix_file_operations = {\n \t.read_iter\t= generic_file_read_iter,\n \t.write_iter\t= generic_file_write_iter,\n \t.mmap_prepare\t= generic_file_mmap_prepare,\n-\t.fsync\t\t= minix_fsync,\n+\t.fsync\t\t= simple_fsync,\n \t.splice_read\t= filemap_splice_read,\n };\n \ndiff --git a/fs/minix/inode.c b/fs/minix/inode.c\nindex c30cc590698d6f..daf83e4ff25c81 100644\n--- a/fs/minix/inode.c\n+++ b/fs/minix/inode.c\n@@ -24,6 +24,8 @@\n \n static int minix_write_inode(struct inode *inode,\n \t\tstruct writeback_control *wbc);\n+static int minix_sync_inode_metadata(struct inode *inode,\n+\t\t\t\t struct writeback_control *wbc);\n static int minix_statfs(struct dentry *dentry, struct kstatfs *buf);\n \n void __minix_error_inode(struct inode *inode, const char *function,\n@@ -128,6 +130,7 @@ static const struct super_operations minix_sops = {\n \t.alloc_inode\t= minix_alloc_inode,\n \t.free_inode\t= minix_free_in_core_inode,\n \t.write_inode\t= minix_write_inode,\n+\t.sync_inode_metadata = minix_sync_inode_metadata,\n \t.evict_inode\t= minix_evict_inode,\n \t.put_super\t= minix_put_super,\n \t.statfs\t\t= minix_statfs,\n@@ -630,7 +633,7 @@ struct inode *minix_iget(struct super_block *sb, unsigned long ino)\n /*\n * The minix V1 function to synchronize an inode.\n */\n-static struct buffer_head * V1_minix_update_inode(struct inode * inode)\n+static int V1_minix_update_inode(struct inode * inode)\n {\n \tstruct buffer_head * bh;\n \tstruct minix_inode * raw_inode;\n@@ -639,7 +642,7 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode)\n \n \traw_inode = minix_V1_raw_inode(inode-\u003ei_sb, inode-\u003ei_ino, \u0026bh);\n \tif (!raw_inode)\n-\t\treturn NULL;\n+\t\treturn -EIO;\n \traw_inode-\u003ei_mode = inode-\u003ei_mode;\n \traw_inode-\u003ei_uid = fs_high2lowuid(i_uid_read(inode));\n \traw_inode-\u003ei_gid = fs_high2lowgid(i_gid_read(inode));\n@@ -651,13 +654,15 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode)\n \telse for (i = 0; i \u003c 9; i++)\n \t\traw_inode-\u003ei_zone[i] = minix_inode-\u003eu.i1_data[i];\n \tmark_buffer_dirty(bh);\n-\treturn bh;\n+\tbrelse(bh);\n+\tset_inode_metadata_writeback(inode);\n+\treturn 0;\n }\n \n /*\n * The minix V2 function to synchronize an inode.\n */\n-static struct buffer_head * V2_minix_update_inode(struct inode * inode)\n+static int V2_minix_update_inode(struct inode * inode)\n {\n \tstruct buffer_head * bh;\n \tstruct minix2_inode * raw_inode;\n@@ -666,7 +671,7 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)\n \n \traw_inode = minix_V2_raw_inode(inode-\u003ei_sb, inode-\u003ei_ino, \u0026bh);\n \tif (!raw_inode)\n-\t\treturn NULL;\n+\t\treturn -EIO;\n \traw_inode-\u003ei_mode = inode-\u003ei_mode;\n \traw_inode-\u003ei_uid = fs_high2lowuid(i_uid_read(inode));\n \traw_inode-\u003ei_gid = fs_high2lowgid(i_gid_read(inode));\n@@ -680,29 +685,42 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)\n \telse for (i = 0; i \u003c 10; i++)\n \t\traw_inode-\u003ei_zone[i] = minix_inode-\u003eu.i2_data[i];\n \tmark_buffer_dirty(bh);\n-\treturn bh;\n+\tbrelse(bh);\n+\tset_inode_metadata_writeback(inode);\n+\treturn 0;\n }\n \n static int minix_write_inode(struct inode *inode, struct writeback_control *wbc)\n+{\n+\tif (INODE_VERSION(inode) == MINIX_V1)\n+\t\treturn V1_minix_update_inode(inode);\n+\treturn V2_minix_update_inode(inode);\n+}\n+\n+static int minix_sync_inode_metadata(struct inode *inode,\n+\t\t\t\t struct writeback_control *wbc)\n {\n \tint err = 0;\n \tstruct buffer_head *bh;\n+\tvoid *raw_inode;\n \n \tif (INODE_VERSION(inode) == MINIX_V1)\n-\t\tbh = V1_minix_update_inode(inode);\n+\t\traw_inode = minix_V1_raw_inode(inode-\u003ei_sb, inode-\u003ei_ino, \u0026bh);\n \telse\n-\t\tbh = V2_minix_update_inode(inode);\n-\tif (!bh)\n+\t\traw_inode = minix_V2_raw_inode(inode-\u003ei_sb, inode-\u003ei_ino, \u0026bh);\n+\tif (!raw_inode)\n \t\treturn -EIO;\n-\tif (wbc-\u003esync_mode == WB_SYNC_ALL \u0026\u0026 buffer_dirty(bh)) {\n-\t\tsync_dirty_buffer(bh);\n-\t\tif (buffer_req(bh) \u0026\u0026 !buffer_uptodate(bh)) {\n-\t\t\tprintk(\"IO error syncing minix inode [%s:%08llx]\\n\",\n-\t\t\t\tinode-\u003ei_sb-\u003es_id, inode-\u003ei_ino);\n-\t\t\terr = -EIO;\n-\t\t}\n+\terr = mmb_sync(\u0026minix_i(inode)-\u003ei_metadata_bhs);\n+\tif (err)\n+\t\tgoto out;\n+\tsync_dirty_buffer(bh);\n+\tif (buffer_write_io_error(bh)) {\n+\t\tprintk(\"IO error syncing minix inode [%s:%08llx]\\n\",\n+\t\t\tinode-\u003ei_sb-\u003es_id, inode-\u003ei_ino);\n+\t\terr = -EIO;\n \t}\n-\tbrelse (bh);\n+out:\n+\tbrelse(bh);\n \treturn err;\n }\n \ndiff --git a/fs/udf/dir.c b/fs/udf/dir.c\nindex ebc9f6a379fe3a..425e3c16293504 100644\n--- a/fs/udf/dir.c\n+++ b/fs/udf/dir.c\n@@ -157,6 +157,6 @@ const struct file_operations udf_dir_operations = {\n \t.read\t\t\t= generic_read_dir,\n \t.iterate_shared\t\t= udf_readdir,\n \t.unlocked_ioctl\t\t= udf_ioctl,\n-\t.fsync\t\t\t= udf_fsync,\n+\t.fsync\t\t\t= simple_fsync,\n \t.setlease\t\t= generic_setlease,\n };\ndiff --git a/fs/udf/file.c b/fs/udf/file.c\nindex f7f1422de30fa4..57d11606a2a776 100644\n--- a/fs/udf/file.c\n+++ b/fs/udf/file.c\n@@ -198,13 +198,6 @@ static int udf_file_mmap(struct file *file, struct vm_area_struct *vma)\n \treturn 0;\n }\n \n-int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync)\n-{\n-\treturn mmb_fsync(file,\n-\t\t\t\u0026UDF_I(file-\u003ef_mapping-\u003ehost)-\u003ei_metadata_bhs,\n-\t\t\tstart, end, datasync);\n-}\n-\n const struct file_operations udf_file_operations = {\n \t.read_iter\t\t= generic_file_read_iter,\n \t.unlocked_ioctl\t\t= udf_ioctl,\n@@ -212,7 +205,7 @@ const struct file_operations udf_file_operations = {\n \t.mmap\t\t\t= udf_file_mmap,\n \t.write_iter\t\t= udf_file_write_iter,\n \t.release\t\t= udf_release_file,\n-\t.fsync\t\t\t= udf_fsync,\n+\t.fsync\t\t\t= simple_fsync,\n \t.splice_read\t\t= filemap_splice_read,\n \t.splice_write\t\t= iter_file_splice_write,\n \t.llseek\t\t\t= generic_file_llseek,\n@@ -253,6 +246,8 @@ static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry,\n \n \tsetattr_copy(\u0026nop_mnt_idmap, inode, attr);\n \tmark_inode_dirty(inode);\n+\tif (IS_SYNC(inode))\n+\t\tsync_inode_metadata(inode, 1);\n \treturn 0;\n }\n \ndiff --git a/fs/udf/inode.c b/fs/udf/inode.c\nindex 67bcf83758c8fb..68c6c2ba8ed1a2 100644\n--- a/fs/udf/inode.c\n+++ b/fs/udf/inode.c\n@@ -51,8 +51,6 @@\n struct udf_map_rq;\n \n static umode_t udf_convert_permissions(struct fileEntry *);\n-static int udf_update_inode(struct inode *, int);\n-static int udf_sync_inode(struct inode *inode);\n static int udf_alloc_i_data(struct inode *inode, size_t size);\n static int inode_getblk(struct inode *inode, struct udf_map_rq *map);\n static int udf_insert_aext(struct inode *, struct extent_position,\n@@ -142,7 +140,7 @@ void udf_evict_inode(struct inode *inode)\n \t\tif (!inode-\u003ei_nlink) {\n \t\t\twant_delete = 1;\n \t\t\tudf_setsize(inode, 0);\n-\t\t\tudf_update_inode(inode, IS_SYNC(inode));\n+\t\t\tsync_inode_metadata(inode, IS_SYNC(inode));\n \t\t}\n \t\tif (iinfo-\u003ei_alloc_type != ICBTAG_FLAG_AD_IN_ICB \u0026\u0026\n \t\t inode-\u003ei_size != iinfo-\u003ei_lenExtents) {\n@@ -936,10 +934,7 @@ static int inode_getblk(struct inode *inode, struct udf_map_rq *map)\n \tiinfo-\u003ei_next_alloc_goal = newblocknum + 1;\n \tinode_set_ctime_current(inode);\n \n-\tif (IS_SYNC(inode))\n-\t\tudf_sync_inode(inode);\n-\telse\n-\t\tmark_inode_dirty(inode);\n+\tmark_inode_dirty(inode);\n \tret = 0;\n out_free:\n \tbrelse(prev_epos.bh);\n@@ -1326,10 +1321,7 @@ int udf_setsize(struct inode *inode, loff_t newsize)\n \t}\n update_time:\n \tinode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));\n-\tif (IS_SYNC(inode))\n-\t\tudf_sync_inode(inode);\n-\telse\n-\t\tmark_inode_dirty(inode);\n+\tmark_inode_dirty(inode);\n \treturn err;\n }\n \n@@ -1705,14 +1697,28 @@ void udf_update_extra_perms(struct inode *inode, umode_t mode)\n \t\tiinfo-\u003ei_extraPerms |= FE_PERM_O_DELETE;\n }\n \n-int udf_write_inode(struct inode *inode, struct writeback_control *wbc)\n+int udf_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)\n {\n-\treturn udf_update_inode(inode, wbc-\u003esync_mode == WB_SYNC_ALL);\n-}\n+\tstruct buffer_head *bh;\n+\tint err = 0;\n \n-static int udf_sync_inode(struct inode *inode)\n-{\n-\treturn udf_update_inode(inode, 1);\n+\tbh = sb_getblk(inode-\u003ei_sb,\n+\t\t\tudf_get_lb_pblock(inode-\u003ei_sb,\n+\t\t\t\t\t \u0026UDF_I(inode)-\u003ei_location, 0));\n+\tif (!bh)\n+\t\treturn -EIO;\n+\n+\tsync_dirty_buffer(bh);\n+\tif (buffer_write_io_error(bh)) {\n+\t\tudf_warn(inode-\u003ei_sb, \"IO error syncing udf inode [%08llx]\\n\",\n+\t\t\t inode-\u003ei_ino);\n+\t\terr = -EIO;\n+\t\tgoto out;\n+\t}\n+\terr = mmb_sync(\u0026UDF_I(inode)-\u003ei_metadata_bhs);\n+out:\n+\tbrelse(bh);\n+\treturn err;\n }\n \n static void udf_adjust_time(struct udf_inode_info *iinfo, struct timespec64 time)\n@@ -1723,7 +1729,7 @@ static void udf_adjust_time(struct udf_inode_info *iinfo, struct timespec64 time\n \t\tiinfo-\u003ei_crtime = time;\n }\n \n-static int udf_update_inode(struct inode *inode, int do_sync)\n+int udf_write_inode(struct inode *inode, struct writeback_control *wbc)\n {\n \tstruct buffer_head *bh = NULL;\n \tstruct fileEntry *fe;\n@@ -1732,7 +1738,6 @@ static int udf_update_inode(struct inode *inode, int do_sync)\n \tuint32_t udfperms;\n \tuint16_t icbflags;\n \tuint16_t crclen;\n-\tint err = 0;\n \tstruct udf_sb_info *sbi = UDF_SB(inode-\u003ei_sb);\n \tunsigned char blocksize_bits = inode-\u003ei_sb-\u003es_blocksize_bits;\n \tstruct udf_inode_info *iinfo = UDF_I(inode);\n@@ -1937,17 +1942,10 @@ static int udf_update_inode(struct inode *inode, int do_sync)\n \n \t/* write the data blocks */\n \tmark_buffer_dirty(bh);\n-\tif (do_sync) {\n-\t\tsync_dirty_buffer(bh);\n-\t\tif (buffer_write_io_error(bh)) {\n-\t\t\tudf_warn(inode-\u003ei_sb, \"IO error syncing udf inode [%08llx]\\n\",\n-\t\t\t\t inode-\u003ei_ino);\n-\t\t\terr = -EIO;\n-\t\t}\n-\t}\n \tbrelse(bh);\n+\tset_inode_metadata_writeback(inode);\n \n-\treturn err;\n+\treturn 0;\n }\n \n struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,\ndiff --git a/fs/udf/super.c b/fs/udf/super.c\nindex 7b85f5a2b79fc4..e7e9f2a0d24e80 100644\n--- a/fs/udf/super.c\n+++ b/fs/udf/super.c\n@@ -211,6 +211,7 @@ static const struct super_operations udf_sb_ops = {\n \t.alloc_inode\t= udf_alloc_inode,\n \t.free_inode\t= udf_free_in_core_inode,\n \t.write_inode\t= udf_write_inode,\n+\t.sync_inode_metadata = udf_sync_inode_metadata,\n \t.evict_inode\t= udf_evict_inode,\n \t.put_super\t= udf_put_super,\n \t.sync_fs\t= udf_sync_fs,\ndiff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h\nindex 6d951e05c0046a..86dc2d6a2ef1b8 100644\n--- a/fs/udf/udfdecl.h\n+++ b/fs/udf/udfdecl.h\n@@ -137,7 +137,6 @@ static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)\n \n /* file.c */\n extern long udf_ioctl(struct file *, unsigned int, unsigned long);\n-int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync);\n \n /* inode.c */\n extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,\n@@ -158,6 +157,7 @@ extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,\n extern int udf_setsize(struct inode *, loff_t);\n extern void udf_evict_inode(struct inode *);\n extern int udf_write_inode(struct inode *, struct writeback_control *wbc);\n+int udf_sync_inode_metadata(struct inode *, struct writeback_control *wbc);\n extern int inode_bmap(struct inode *inode, sector_t block,\n \t\t struct extent_position *pos, struct kernel_lb_addr *eloc,\n \t\t uint32_t *elen, sector_t *offset, int8_t *etype);\ndiff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h\nindex 8b23bc9a244cb3..fd2c7115c05427 100644\n--- a/include/linux/buffer_head.h\n+++ b/include/linux/buffer_head.h\n@@ -210,10 +210,6 @@ void bh_end_async_write(struct bio *bio);\n \n /* Things to do with metadata buffers list */\n void mmb_mark_buffer_dirty(struct buffer_head *bh, struct mapping_metadata_bhs *mmb);\n-int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,\n-\t\t loff_t start, loff_t end, bool datasync);\n-int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,\n-\t loff_t start, loff_t end, bool datasync);\n void clean_bdev_aliases(struct block_device *bdev, sector_t block,\n \t\t\tsector_t len);\n static inline void clean_bdev_bh_alias(struct buffer_head *bh)\ndiff --git a/include/linux/fs.h b/include/linux/fs.h\nindex 50ce731a2b78f1..729e3cb89e384b 100644\n--- a/include/linux/fs.h\n+++ b/include/linux/fs.h\n@@ -740,7 +740,8 @@ enum inode_state_flags_enum {\n \tI_CREATING\t\t= (1U \u003c\u003c 15),\n \tI_DONTCACHE\t\t= (1U \u003c\u003c 16),\n \tI_SYNC_QUEUED\t\t= (1U \u003c\u003c 17),\n-\tI_PINNING_NETFS_WB\t= (1U \u003c\u003c 18)\n+\tI_PINNING_NETFS_WB\t= (1U \u003c\u003c 18),\n+\tI_METADATA_WRITEBACK\t= (1U \u003c\u003c 19),\n };\n \n #define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)\n@@ -2213,6 +2214,13 @@ static inline void mark_inode_dirty_sync(struct inode *inode)\n \t__mark_inode_dirty(inode, I_DIRTY_SYNC);\n }\n \n+static inline void set_inode_metadata_writeback(struct inode *inode)\n+{\n+\tspin_lock(\u0026inode-\u003ei_lock);\n+\tinode_state_set(inode, I_METADATA_WRITEBACK);\n+\tspin_unlock(\u0026inode-\u003ei_lock);\n+}\n+\n /*\n * returns the refcount on the inode. it can change arbitrarily.\n */\ndiff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h\nindex ef7941e9dc79ac..170561e8f2e2c4 100644\n--- a/include/linux/fs/super_types.h\n+++ b/include/linux/fs/super_types.h\n@@ -86,6 +86,8 @@ struct super_operations {\n \tvoid (*free_inode)(struct inode *inode);\n \tvoid (*dirty_inode)(struct inode *inode, int flags);\n \tint (*write_inode)(struct inode *inode, struct writeback_control *wbc);\n+\tint (*sync_inode_metadata)(struct inode *inode,\n+\t\t\t\t struct writeback_control *wbc);\n \tint (*drop_inode)(struct inode *inode);\n \tvoid (*evict_inode)(struct inode *inode);\n \tvoid (*put_super)(struct super_block *sb);\n"
} |
| 2/1 | 2026/07/16 20:32 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"EXT4_FS",
"EXT2_FS",
"MSDOS_FS",
"VFAT_FS",
"MINIX_FS",
"UDF_FS",
"BFS_FS",
"AFFS_FS"
],
"FocusSymbols": [
"__writeback_single_inode",
"writeback_single_inode",
"ext4_sync_inode_metadata",
"ext2_sync_inode_metadata",
"fat_sync_inode_metadata",
"minix_sync_inode_metadata",
"udf_sync_inode_metadata",
"bfs_sync_inode_metadata",
"simple_fsync_noflush"
],
"Reasoning": "The patch introduces a new `sync_inode_metadata` callback in `super_operations` and modifies the VFS writeback logic to handle a new `I_METADATA_WRITEBACK` flag. It implements this callback for several filesystems (ext4, ext2, fat, minix, udf, bfs) and refactors their metadata synchronization logic, replacing `mmb_fsync`. This is a significant functional change to core VFS writeback and filesystem metadata handling, making it highly relevant for fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 9cb53a8d5963fd1e65bce9ea2f5a63adc83329bd
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Jul 16 20:32:01 2026 +0000
syz-cluster: applied patch under review
diff --git a/fs/affs/affs.h b/fs/affs/affs.h
index 44a3f69d275f96..a7faa91deed5ac 100644
--- a/fs/affs/affs.h
+++ b/fs/affs/affs.h
@@ -44,7 +44,6 @@ struct affs_inode_info {
struct mutex i_link_lock; /* Protects internal inode access. */
struct mutex i_ext_lock; /* Protects internal inode access. */
#define i_hash_lock i_ext_lock
- struct mapping_metadata_bhs i_metadata_bhs;
u32 i_blkcnt; /* block count */
u32 i_extcnt; /* extended block count */
u32 *i_lc; /* linear cache of extended blocks */
@@ -152,7 +151,6 @@ extern bool affs_nofilenametruncate(const struct dentry *dentry);
extern int affs_check_name(const unsigned char *name, int len,
bool notruncate);
extern int affs_copy_name(unsigned char *bstr, struct dentry *dentry);
-struct mapping_metadata_bhs *affs_get_metadata_bhs(struct inode *inode);
/* bitmap. c */
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c
index bed4fc805e8e66..6cc0fc9a4cbf92 100644
--- a/fs/affs/amigaffs.c
+++ b/fs/affs/amigaffs.c
@@ -57,7 +57,7 @@ affs_insert_hash(struct inode *dir, struct buffer_head *bh)
AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
affs_adjust_checksum(dir_bh, ino);
- mmb_mark_buffer_dirty(dir_bh, &AFFS_I(dir)->i_metadata_bhs);
+ mark_buffer_dirty(dir_bh);
affs_brelse(dir_bh);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
@@ -100,7 +100,7 @@ affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
else
AFFS_TAIL(sb, bh)->hash_chain = ino;
affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
- mmb_mark_buffer_dirty(bh, &AFFS_I(dir)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
AFFS_TAIL(sb, rem_bh)->parent = 0;
retval = 0;
break;
@@ -180,7 +180,7 @@ affs_remove_link(struct dentry *dentry)
affs_unlock_dir(dir);
goto done;
}
- mmb_mark_buffer_dirty(link_bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(link_bh);
memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
retval = affs_insert_hash(dir, bh);
@@ -188,7 +188,7 @@ affs_remove_link(struct dentry *dentry)
affs_unlock_dir(dir);
goto done;
}
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
affs_unlock_dir(dir);
iput(dir);
@@ -203,7 +203,7 @@ affs_remove_link(struct dentry *dentry)
__be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain;
AFFS_TAIL(sb, bh)->link_chain = ino2;
affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
retval = 0;
/* Fix the link count, if bh is a normal header block without links */
switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
@@ -306,7 +306,7 @@ affs_remove_header(struct dentry *dentry)
retval = affs_remove_hash(dir, bh);
if (retval)
goto done_unlock;
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
affs_unlock_dir(dir);
diff --git a/fs/affs/file.c b/fs/affs/file.c
index 144b17482d12da..23e088a7ed4fb3 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -140,14 +140,14 @@ affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino);
affs_fix_checksum(sb, new_bh);
- mmb_mark_buffer_dirty(new_bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(new_bh);
tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
if (tmp)
affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp);
AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr);
affs_adjust_checksum(bh, blocknr - tmp);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
AFFS_I(inode)->i_extcnt++;
mark_inode_dirty(inode);
@@ -581,7 +581,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
memset(AFFS_DATA(bh) + boff, 0, tmp);
be32_add_cpu(&AFFS_DATA_HEAD(bh)->size, tmp);
affs_fix_checksum(sb, bh);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
size += tmp;
bidx++;
} else if (bidx) {
@@ -603,7 +603,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
affs_fix_checksum(sb, bh);
bh->b_state &= ~(1UL << BH_New);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
if (prev_bh) {
u32 tmp_next = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
@@ -613,8 +613,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
bidx, tmp_next);
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
- mmb_mark_buffer_dirty(prev_bh,
- &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(prev_bh);
affs_brelse(prev_bh);
}
size += bsize;
@@ -733,7 +732,7 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(
max(boff + tmp, be32_to_cpu(AFFS_DATA_HEAD(bh)->size)));
affs_fix_checksum(sb, bh);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
written += tmp;
from += tmp;
bidx++;
@@ -766,13 +765,12 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
bidx, tmp_next);
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
- mmb_mark_buffer_dirty(prev_bh,
- &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(prev_bh);
}
}
affs_brelse(prev_bh);
affs_fix_checksum(sb, bh);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
written += bsize;
from += bsize;
bidx++;
@@ -801,14 +799,13 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
bidx, tmp_next);
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
- mmb_mark_buffer_dirty(prev_bh,
- &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(prev_bh);
}
} else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp)
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
affs_brelse(prev_bh);
affs_fix_checksum(sb, bh);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
written += tmp;
from += tmp;
bidx++;
@@ -945,7 +942,7 @@ affs_truncate(struct inode *inode)
}
AFFS_TAIL(sb, ext_bh)->extension = 0;
affs_fix_checksum(sb, ext_bh);
- mmb_mark_buffer_dirty(ext_bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(ext_bh);
affs_brelse(ext_bh);
if (inode->i_size) {
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index 5dd1b016bcb00a..d4a3f381c4bcff 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -206,7 +206,7 @@ affs_write_inode(struct inode *inode, struct writeback_control *wbc)
}
}
affs_fix_checksum(sb, bh);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
affs_brelse(bh);
affs_free_prealloc(inode);
return 0;
@@ -266,11 +266,8 @@ affs_evict_inode(struct inode *inode)
if (!inode->i_nlink) {
inode->i_size = 0;
affs_truncate(inode);
- } else {
- mmb_sync(&AFFS_I(inode)->i_metadata_bhs);
}
- mmb_invalidate(&AFFS_I(inode)->i_metadata_bhs);
clear_inode(inode);
affs_free_prealloc(inode);
cache_page = (unsigned long)AFFS_I(inode)->i_lc;
@@ -305,7 +302,7 @@ affs_new_inode(struct inode *dir)
bh = affs_getzeroblk(sb, block);
if (!bh)
goto err_bh;
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
affs_brelse(bh);
inode->i_uid = current_fsuid();
@@ -393,17 +390,17 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3
AFFS_TAIL(sb, bh)->link_chain = chain;
AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block);
affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain));
- mmb_mark_buffer_dirty(inode_bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(inode_bh);
set_nlink(inode, 2);
ihold(inode);
}
affs_fix_checksum(sb, bh);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
dentry->d_fsdata = (void *)(long)bh->b_blocknr;
affs_lock_dir(dir);
retval = affs_insert_hash(dir, bh);
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
affs_unlock_dir(dir);
affs_unlock_link(inode);
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index c3c6532da4b0da..57d8d755aada05 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -373,7 +373,7 @@ affs_symlink(struct mnt_idmap *idmap, struct inode *dir,
}
*p = 0;
inode->i_size = i + 1;
- mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
affs_brelse(bh);
mark_inode_dirty(inode);
@@ -443,8 +443,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
/* TODO: move it back to old_dir, if error? */
done:
- mmb_mark_buffer_dirty(bh,
- &AFFS_I(retval ? old_dir : new_dir)->i_metadata_bhs);
+ mark_buffer_dirty(bh);
affs_brelse(bh);
return retval;
}
@@ -497,8 +496,8 @@ affs_xrename(struct inode *old_dir, struct dentry *old_dentry,
retval = affs_insert_hash(old_dir, bh_new);
affs_unlock_dir(old_dir);
done:
- mmb_mark_buffer_dirty(bh_old, &AFFS_I(new_dir)->i_metadata_bhs);
- mmb_mark_buffer_dirty(bh_new, &AFFS_I(old_dir)->i_metadata_bhs);
+ mark_buffer_dirty(bh_old);
+ mark_buffer_dirty(bh_new);
affs_brelse(bh_old);
affs_brelse(bh_new);
return retval;
diff --git a/fs/affs/super.c b/fs/affs/super.c
index b232251aa7bbd0..0ad5127e9fb8ee 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -108,7 +108,6 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
i->i_lc = NULL;
i->i_ext_bh = NULL;
i->i_pa_cnt = 0;
- mmb_init(&i->i_metadata_bhs, &i->vfs_inode.i_data);
return &i->vfs_inode;
}
diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c
index 5b40ab09a796de..9b37ec4bd89a8c 100644
--- a/fs/bfs/dir.c
+++ b/fs/bfs/dir.c
@@ -68,17 +68,10 @@ static int bfs_readdir(struct file *f, struct dir_context *ctx)
return 0;
}
-static int bfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
-{
- return mmb_fsync(file,
- &BFS_I(file->f_mapping->host)->i_metadata_bhs,
- start, end, datasync);
-}
-
const struct file_operations bfs_dir_operations = {
.read = generic_read_dir,
.iterate_shared = bfs_readdir,
- .fsync = bfs_fsync,
+ .fsync = simple_fsync,
.llseek = generic_file_llseek,
};
diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c
index e41efdd35db9ef..cacc7eff9c33ce 100644
--- a/fs/bfs/inode.c
+++ b/fs/bfs/inode.c
@@ -136,7 +136,6 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc)
unsigned long i_sblock;
struct bfs_inode *di;
struct buffer_head *bh;
- int err = 0;
dprintf("ino=%08x\n", ino);
@@ -165,13 +164,31 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc)
di->i_eoffset = cpu_to_le32(i_sblock * BFS_BSIZE + inode->i_size - 1);
mark_buffer_dirty(bh);
- if (wbc->sync_mode == WB_SYNC_ALL) {
- sync_dirty_buffer(bh);
- if (buffer_req(bh) && !buffer_uptodate(bh))
- err = -EIO;
- }
brelse(bh);
mutex_unlock(&info->bfs_lock);
+ set_inode_metadata_writeback(inode);
+ return 0;
+}
+
+static int bfs_sync_inode_metadata(struct inode *inode,
+ struct writeback_control *wbc)
+{
+ int err = 0;
+ struct bfs_inode *di;
+ struct buffer_head *bh;
+
+ di = find_inode(inode->i_sb, (u16)inode->ino, &bh);
+ if (IS_ERR(di))
+ return PTR_ERR(di);
+
+ sync_dirty_buffer(bh);
+ if (buffer_write_io_error(bh)) {
+ err = -EIO;
+ goto out;
+ }
+ err = mmb_sync(&BFS_I(inode)->i_metadata_bhs);
+out:
+ brelse(bh);
return err;
}
@@ -302,6 +319,7 @@ static const struct super_operations bfs_sops = {
.alloc_inode = bfs_alloc_inode,
.free_inode = bfs_free_inode,
.write_inode = bfs_write_inode,
+ .sync_inode_metadata = bfs_sync_inode_metadata,
.evict_inode = bfs_evict_inode,
.put_super = bfs_put_super,
.statfs = bfs_statfs,
diff --git a/fs/buffer.c b/fs/buffer.c
index 9af5f061a1f85a..be8b57a635cd82 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -628,79 +628,6 @@ int mmb_sync(struct mapping_metadata_bhs *mmb)
}
EXPORT_SYMBOL(mmb_sync);
-/**
- * mmb_fsync_noflush - fsync implementation for simple filesystems with
- * metadata buffers list
- *
- * @file: file to synchronize
- * @mmb: list of metadata bhs to flush
- * @start: start offset in bytes
- * @end: end offset in bytes (inclusive)
- * @datasync: only synchronize essential metadata if true
- *
- * This is an implementation of the fsync method for simple filesystems which
- * track all non-inode metadata in the buffers list hanging off the @mmb
- * structure.
- */
-int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,
- loff_t start, loff_t end, bool datasync)
-{
- struct inode *inode = file->f_mapping->host;
- int err;
- int ret = 0;
-
- err = file_write_and_wait_range(file, start, end);
- if (err)
- return err;
-
- if (mmb)
- ret = mmb_sync(mmb);
- if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
- goto out;
- if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
- goto out;
-
- err = sync_inode_metadata(inode, 1);
- if (ret == 0)
- ret = err;
-
-out:
- /* check and advance again to catch errors after syncing out buffers */
- err = file_check_and_advance_wb_err(file);
- if (ret == 0)
- ret = err;
- return ret;
-}
-EXPORT_SYMBOL(mmb_fsync_noflush);
-
-/**
- * mmb_fsync - fsync implementation for simple filesystems with metadata
- * buffers list
- *
- * @file: file to synchronize
- * @mmb: list of metadata bhs to flush
- * @start: start offset in bytes
- * @end: end offset in bytes (inclusive)
- * @datasync: only synchronize essential metadata if true
- *
- * This is an implementation of the fsync method for simple filesystems which
- * track all non-inode metadata in the buffers list hanging off the @mmb
- * structure. This also makes sure that a device cache flush operation is
- * called at the end.
- */
-int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
- loff_t start, loff_t end, bool datasync)
-{
- struct inode *inode = file->f_mapping->host;
- int ret;
-
- ret = mmb_fsync_noflush(file, mmb, start, end, datasync);
- if (!ret)
- ret = blkdev_issue_flush(inode->i_sb->s_bdev);
- return ret;
-}
-EXPORT_SYMBOL(mmb_fsync);
-
/*
* Called when we've recently written block `bblock', and it is known that
* `bblock' was for a buffer_boundary() buffer. This means that the block at
@@ -1123,12 +1050,18 @@ EXPORT_SYMBOL(mark_buffer_dirty);
void mark_buffer_write_io_error(struct buffer_head *bh)
{
+ struct mapping_metadata_bhs *mmb;
+
set_buffer_write_io_error(bh);
/* FIXME: do we need to set this in both places? */
if (bh->b_folio && bh->b_folio->mapping)
mapping_set_error(bh->b_folio->mapping, -EIO);
- if (bh->b_mmb)
- mapping_set_error(bh->b_mmb->mapping, -EIO);
+ /* Protect us from mmb & inode getting freed while we work on it */
+ rcu_read_lock();
+ mmb = READ_ONCE(bh->b_mmb);
+ if (mmb)
+ mapping_set_error(mmb->mapping, -EIO);
+ rcu_read_unlock();
}
EXPORT_SYMBOL(mark_buffer_write_io_error);
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 278d4be8ecbe77..e17bbc7598c143 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -734,6 +734,6 @@ const struct file_operations ext2_dir_operations = {
#ifdef CONFIG_COMPAT
.compat_ioctl = ext2_compat_ioctl,
#endif
- .fsync = ext2_fsync,
+ .fsync = simple_fsync,
.setlease = generic_setlease,
};
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index 79f7b395258c2a..5642451bf19173 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -735,6 +735,7 @@ extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
/* inode.c */
extern struct inode *ext2_iget (struct super_block *, unsigned long);
extern int ext2_write_inode (struct inode *, struct writeback_control *);
+extern int ext2_sync_inode_metadata(struct inode *, struct writeback_control *);
extern void ext2_evict_inode(struct inode *);
void ext2_write_failed(struct address_space *mapping, loff_t to);
extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
@@ -772,8 +773,6 @@ extern void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es,
extern const struct file_operations ext2_dir_operations;
/* file.c */
-extern int ext2_fsync(struct file *file, loff_t start, loff_t end,
- int datasync);
extern const struct inode_operations ext2_file_inode_operations;
extern const struct file_operations ext2_file_operations;
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 8dca9ec4cacd9a..b9020df7d89e03 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -47,21 +47,6 @@ static int ext2_release_file (struct inode * inode, struct file * filp)
return 0;
}
-int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
-{
- int ret;
- struct inode *inode = file->f_mapping->host;
- struct super_block *sb = inode->i_sb;
-
- ret = mmb_fsync(file, &EXT2_I(inode)->i_metadata_bhs,
- start, end, datasync);
- if (ret == -EIO)
- /* We don't really know where the IO error happened... */
- ext2_error(sb, __func__,
- "detected IO error when writing metadata buffers");
- return ret;
-}
-
static ssize_t ext2_dio_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct file *file = iocb->ki_filp;
@@ -213,7 +198,7 @@ const struct file_operations ext2_file_operations = {
.mmap_prepare = generic_file_mmap_prepare,
.open = ext2_file_open,
.release = ext2_release_file,
- .fsync = ext2_fsync,
+ .fsync = simple_fsync,
.get_unmapped_area = thp_get_unmapped_area,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 29808629cce56f..99acd79a4af272 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -39,8 +39,6 @@
#include "acl.h"
#include "xattr.h"
-static int __ext2_write_inode(struct inode *inode, int do_sync);
-
/*
* Test whether an inode is a fast symlink.
*/
@@ -87,7 +85,7 @@ void ext2_evict_inode(struct inode * inode)
/* set dtime */
EXT2_I(inode)->i_dtime = ktime_get_real_seconds();
mark_inode_dirty(inode);
- __ext2_write_inode(inode, inode_needs_sync(inode));
+ sync_inode_metadata(inode, inode_needs_sync(inode));
/* truncate to 0 */
inode->i_size = 0;
if (inode->i_blocks)
@@ -1258,12 +1256,9 @@ static int ext2_setsize(struct inode *inode, loff_t newsize)
filemap_invalidate_unlock(inode->i_mapping);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
- if (inode_needs_sync(inode)) {
- mmb_sync(&EXT2_I(inode)->i_metadata_bhs);
+ mark_inode_dirty(inode);
+ if (inode_needs_sync(inode))
sync_inode_metadata(inode, 1);
- } else {
- mark_inode_dirty(inode);
- }
return 0;
}
@@ -1469,7 +1464,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
return ERR_PTR(ret);
}
-static int __ext2_write_inode(struct inode *inode, int do_sync)
+int ext2_write_inode(struct inode *inode, struct writeback_control *wbc)
{
struct ext2_inode_info *ei = EXT2_I(inode);
struct super_block *sb = inode->i_sb;
@@ -1560,22 +1555,33 @@ static int __ext2_write_inode(struct inode *inode, int do_sync)
} else for (n = 0; n < EXT2_N_BLOCKS; n++)
raw_inode->i_block[n] = ei->i_data[n];
mark_buffer_dirty(bh);
- if (do_sync) {
- sync_dirty_buffer(bh);
- if (buffer_req(bh) && !buffer_uptodate(bh)) {
- printk ("IO error syncing ext2 inode [%s:%08lx]\n",
- sb->s_id, (unsigned long) ino);
- err = -EIO;
- }
- }
ei->i_state &= ~EXT2_STATE_NEW;
brelse (bh);
+ set_inode_metadata_writeback(inode);
return err;
}
-int ext2_write_inode(struct inode *inode, struct writeback_control *wbc)
+int ext2_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)
{
- return __ext2_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+ struct buffer_head *bh;
+ struct ext2_inode *raw_inode = ext2_get_inode(inode->i_sb, inode->i_ino,
+ &bh);
+ int err = 0;
+
+ if (IS_ERR(raw_inode))
+ return -EIO;
+ err = mmb_sync(&EXT2_I(inode)->i_metadata_bhs);
+ if (err)
+ goto out;
+ sync_dirty_buffer(bh);
+ if (buffer_write_io_error(bh)) {
+ printk("IO error syncing ext2 inode [%s:%08lx]\n",
+ inode->i_sb->s_id, (unsigned long)inode->i_ino);
+ err = -EIO;
+ }
+out:
+ brelse(bh);
+ return err;
}
int ext2_getattr(struct mnt_idmap *idmap, const struct path *path,
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 3999f8f3b156e3..a40f530872a407 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -362,6 +362,7 @@ static const struct super_operations ext2_sops = {
.alloc_inode = ext2_alloc_inode,
.free_inode = ext2_free_in_core_inode,
.write_inode = ext2_write_inode,
+ .sync_inode_metadata = ext2_sync_inode_metadata,
.evict_inode = ext2_evict_inode,
.put_super = ext2_put_super,
.sync_fs = ext2_sync_fs,
diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c
index e55d16abf422f4..be63f89402a381 100644
--- a/fs/ext2/xattr.c
+++ b/fs/ext2/xattr.c
@@ -777,6 +777,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
/* Update the inode. */
EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
inode_set_ctime_current(inode);
+ mark_inode_dirty(inode);
if (IS_SYNC(inode)) {
error = sync_inode_metadata(inode, 1);
/* In case sync failed due to ENOSPC the inode was actually
@@ -789,8 +790,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
}
goto cleanup;
}
- } else
- mark_inode_dirty(inode);
+ }
error = 0;
if (old_bh && old_bh != new_bh) {
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index b37c136ea3ab33..0f06155a35a694 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1151,7 +1151,7 @@ struct ext4_inode_info {
struct rw_semaphore i_data_sem;
struct inode vfs_inode;
struct jbd2_inode *jinode;
- struct mapping_metadata_bhs i_metadata_bhs;
+ struct mapping_metadata_bhs *i_metadata_bhs;
/*
* File creation time. Its function is same as that of
@@ -2126,6 +2126,17 @@ static inline bool ext4_inode_orphan_tracked(struct inode *inode)
!list_empty(&EXT4_I(inode)->i_orphan);
}
+static inline struct mapping_metadata_bhs *ext4_i_metadata_bhs(
+ struct inode *inode)
+{
+ /*
+ * i_metadata_bhs is set in ext4_inode_attach_mmb() using cmpxchg().
+ * We use READ_ONCE when accessing i_metadata_bhs to make sure we get
+ * consistent view for all accesses.
+ */
+ return READ_ONCE(EXT4_I(inode)->i_metadata_bhs);
+}
+
/*
* Codes for operating systems
*/
@@ -3155,6 +3166,7 @@ extern struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
__ext4_iget((sb), (ino), (flags), __func__, __LINE__)
extern int ext4_write_inode(struct inode *, struct writeback_control *);
+extern int ext4_sync_inode_metadata(struct inode *, struct writeback_control *);
extern int ext4_setattr(struct mnt_idmap *, struct dentry *,
struct iattr *);
extern u32 ext4_dio_alignment(struct inode *inode);
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 9a8c225f275309..3b2f8ac20bf85b 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -350,6 +350,21 @@ int __ext4_journal_get_create_access(const char *where, unsigned int line,
return 0;
}
+static void ext4_inode_attach_mmb(struct inode *inode)
+{
+ struct mapping_metadata_bhs *mmb;
+
+ /*
+ * It's difficult to handle failure when marking buffer dirty without
+ * leaving filesystem corrupted
+ */
+ mmb = kmalloc_obj(*mmb, GFP_NOFS | __GFP_NOFAIL);
+ mmb_init(mmb, &inode->i_data);
+ /* Someone swapped another mmb before us? */
+ if (cmpxchg(&EXT4_I(inode)->i_metadata_bhs, NULL, mmb))
+ kfree(mmb);
+}
+
int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
handle_t *handle, struct inode *inode,
struct buffer_head *bh)
@@ -389,11 +404,13 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
err);
}
} else {
- if (inode)
- mmb_mark_buffer_dirty(bh,
- &EXT4_I(inode)->i_metadata_bhs);
- else
+ if (inode) {
+ if (!ext4_i_metadata_bhs(inode))
+ ext4_inode_attach_mmb(inode);
+ mmb_mark_buffer_dirty(bh, ext4_i_metadata_bhs(inode));
+ } else {
mark_buffer_dirty(bh);
+ }
if (inode && inode_needs_sync(inode)) {
sync_dirty_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh)) {
diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c
index 924726dcc85ff5..2999c2cc8fcfa0 100644
--- a/fs/ext4/fsync.c
+++ b/fs/ext4/fsync.c
@@ -68,9 +68,6 @@ static int ext4_sync_parent(struct inode *inode)
* through ext4_evict_inode()) and so we are safe to flush
* metadata blocks and the inode.
*/
- ret = mmb_sync(&EXT4_I(inode)->i_metadata_bhs);
- if (ret)
- break;
ret = sync_inode_metadata(inode, 1);
if (ret)
break;
@@ -83,22 +80,11 @@ static int ext4_fsync_nojournal(struct file *file, loff_t start, loff_t end,
int datasync, bool *needs_barrier)
{
struct inode *inode = file->f_inode;
- struct writeback_control wbc = {
- .sync_mode = WB_SYNC_ALL,
- .nr_to_write = 0,
- };
int ret;
- ret = mmb_fsync_noflush(file, &EXT4_I(inode)->i_metadata_bhs,
- start, end, datasync);
+ ret = sync_inode_metadata(inode, 1);
if (ret)
return ret;
-
- /* Force writeout of inode table buffer to disk */
- ret = ext4_write_inode(inode, &wbc);
- if (ret)
- return ret;
-
ret = ext4_sync_parent(inode);
if (test_opt(inode->i_sb, BARRIER))
@@ -156,6 +142,10 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
if (sb_rdonly(inode->i_sb))
goto out;
+ ret = file_write_and_wait_range(file, start, end);
+ if (ret)
+ goto out;
+
if (!EXT4_SB(inode->i_sb)->s_journal) {
ret = ext4_fsync_nojournal(file, start, end, datasync,
&needs_barrier);
@@ -164,10 +154,6 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
goto out;
}
- ret = file_write_and_wait_range(file, start, end);
- if (ret)
- goto out;
-
/*
* The caller's filemap_fdatawrite()/wait will sync the data.
* Metadata is in the journal, we wait for proper transaction to
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ce99807c5f5b2c..0c220b535590f4 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -186,6 +186,8 @@ void ext4_evict_inode(struct inode *inode)
if (EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)
ext4_evict_ea_inode(inode);
if (inode->i_nlink) {
+ struct mapping_metadata_bhs *mmb;
+
/*
* If there's dirty page will lead to data loss, user
* could see stale data.
@@ -195,9 +197,9 @@ void ext4_evict_inode(struct inode *inode)
ext4_warning_inode(inode, "data will be lost");
truncate_inode_pages_final(&inode->i_data);
- /* Avoid mballoc special inode which has no proper iops */
- if (!EXT4_SB(inode->i_sb)->s_journal)
- mmb_sync(&EXT4_I(inode)->i_metadata_bhs);
+ mmb = ext4_i_metadata_bhs(inode);
+ if (mmb)
+ mmb_sync(mmb);
goto no_delete;
}
@@ -3452,6 +3454,7 @@ static bool ext4_release_folio(struct folio *folio, gfp_t wait)
static bool ext4_inode_datasync_dirty(struct inode *inode)
{
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
+ struct mapping_metadata_bhs *mmb;
if (journal) {
if (jbd2_transaction_committed(journal,
@@ -3462,8 +3465,9 @@ static bool ext4_inode_datasync_dirty(struct inode *inode)
return true;
}
+ mmb = ext4_i_metadata_bhs(inode);
/* Any metadata buffers to write? */
- if (mmb_has_buffers(&EXT4_I(inode)->i_metadata_bhs))
+ if (mmb && mmb_has_buffers(mmb))
return true;
return inode_state_read_once(inode) & I_DIRTY_DATASYNC;
}
@@ -5838,24 +5842,38 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
err = ext4_fc_commit(EXT4_SB(inode->i_sb)->s_journal,
EXT4_I(inode)->i_sync_tid);
} else {
- struct ext4_iloc iloc;
+ set_inode_metadata_writeback(inode);
+ }
+ return err;
+}
- err = __ext4_get_inode_loc_noinmem(inode, &iloc);
+int ext4_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)
+{
+ struct ext4_iloc iloc;
+ struct mapping_metadata_bhs *mmb;
+ int err;
+
+ /* We should only get here in nojournal mode */
+ if (WARN_ON_ONCE(EXT4_SB(inode->i_sb)->s_journal))
+ return -EFSCORRUPTED;
+
+ err = __ext4_get_inode_loc_noinmem(inode, &iloc);
+ if (err)
+ return err;
+ mmb = READ_ONCE(EXT4_I(inode)->i_metadata_bhs);
+ if (mmb) {
+ err = mmb_sync(mmb);
if (err)
- return err;
- /*
- * sync(2) will flush the whole buffer cache. No need to do
- * it here separately for each inode.
- */
- if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync)
- sync_dirty_buffer(iloc.bh);
- if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) {
- ext4_error_inode_block(inode, iloc.bh->b_blocknr, EIO,
- "IO error syncing inode");
- err = -EIO;
- }
- brelse(iloc.bh);
+ goto out;
+ }
+ sync_dirty_buffer(iloc.bh);
+ if (buffer_write_io_error(iloc.bh)) {
+ ext4_error_inode_block(inode, iloc.bh->b_blocknr, EIO,
+ "IO error syncing inode");
+ err = -EIO;
}
+out:
+ brelse(iloc.bh);
return err;
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 245f67d10ded34..ae33f5bcb13314 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1430,7 +1430,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
ext4_fc_init_inode(&ei->vfs_inode);
spin_lock_init(&ei->i_fc_lock);
- mmb_init(&ei->i_metadata_bhs, &ei->vfs_inode.i_data);
+ ei->i_metadata_bhs = NULL;
#ifdef CONFIG_LOCKDEP
lockdep_set_subclass(&ei->i_data_sem, I_DATA_SEM_NORMAL);
#endif
@@ -1451,6 +1451,7 @@ static int ext4_drop_inode(struct inode *inode)
static void ext4_free_in_core_inode(struct inode *inode)
{
fscrypt_free_inode(inode);
+ kfree(ext4_i_metadata_bhs(inode));
if (!list_empty(&(EXT4_I(inode)->i_fc_list))) {
pr_warn("%s: inode %llu still in fc list",
__func__, inode->i_ino);
@@ -1529,9 +1530,11 @@ static void destroy_inodecache(void)
void ext4_clear_inode(struct inode *inode)
{
+ struct mapping_metadata_bhs *mmb = ext4_i_metadata_bhs(inode);
+
ext4_fc_del(inode);
- if (!EXT4_SB(inode->i_sb)->s_journal)
- mmb_invalidate(&EXT4_I(inode)->i_metadata_bhs);
+ if (mmb)
+ mmb_invalidate(mmb);
clear_inode(inode);
ext4_discard_preallocations(inode);
/*
@@ -1605,9 +1608,13 @@ static int ext4_nfs_commit_metadata(struct inode *inode)
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL
};
+ int ret;
trace_ext4_nfs_commit_metadata(inode);
- return ext4_write_inode(inode, &wbc);
+ ret = ext4_write_inode(inode, &wbc);
+ if (!ret && inode_state_read_once(inode) & I_METADATA_WRITEBACK)
+ ret = ext4_sync_inode_metadata(inode, &wbc);
+ return ret;
}
#ifdef CONFIG_QUOTA
@@ -1664,6 +1671,7 @@ static const struct super_operations ext4_sops = {
.free_inode = ext4_free_in_core_inode,
.destroy_inode = ext4_destroy_inode,
.write_inode = ext4_write_inode,
+ .sync_inode_metadata = ext4_sync_inode_metadata,
.dirty_inode = ext4_dirty_inode,
.drop_inode = ext4_drop_inode,
.evict_inode = ext4_evict_inode,
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index c6cca5d00ffd7e..916f5c4cdcf390 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -1053,11 +1053,7 @@ static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots)
nr_slots--;
}
mmb_mark_buffer_dirty(bh, &MSDOS_I(dir)->i_metadata_bhs);
- if (IS_DIRSYNC(dir))
- err = sync_dirty_buffer(bh);
brelse(bh);
- if (err)
- break;
/* pos is *next* de's position, so this does `- sizeof(de)' */
pos += ((orig_slots - nr_slots) * sizeof(*de)) - sizeof(*de);
@@ -1088,11 +1084,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)
nr_slots--;
}
mmb_mark_buffer_dirty(bh, &MSDOS_I(dir)->i_metadata_bhs);
- if (IS_DIRSYNC(dir))
- err = sync_dirty_buffer(bh);
brelse(bh);
- if (err)
- return err;
inode_inc_iversion(dir);
if (nr_slots) {
@@ -1109,12 +1101,12 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)
}
fat_truncate_time(dir, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
+ err = 0;
+ mark_inode_dirty(dir);
if (IS_DIRSYNC(dir))
- (void)fat_sync_inode(dir);
- else
- mark_inode_dirty(dir);
+ err = sync_inode_metadata(dir, 1);
- return 0;
+ return err;
}
EXPORT_SYMBOL_GPL(fat_remove_entries);
@@ -1144,21 +1136,11 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
n++;
blknr++;
if (n == nr_bhs) {
- if (IS_DIRSYNC(dir)) {
- err = fat_sync_bhs(bhs, n);
- if (err)
- goto error;
- }
for (i = 0; i < n; i++)
brelse(bhs[i]);
n = 0;
}
}
- if (IS_DIRSYNC(dir)) {
- err = fat_sync_bhs(bhs, n);
- if (err)
- goto error;
- }
for (i = 0; i < n; i++)
brelse(bhs[i]);
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 99ed9228a677be..dcb5ba757073ed 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -421,7 +421,6 @@ extern void fat_detach(struct inode *inode);
extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos);
extern struct inode *fat_build_inode(struct super_block *sb,
struct msdos_dir_entry *de, loff_t i_pos);
-extern int fat_sync_inode(struct inode *inode);
extern int fat_fill_super(struct super_block *sb, struct fs_context *fc,
void (*setup)(struct super_block *));
extern int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 37e7049b4c8c44..1c835ca5f21a51 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -190,8 +190,7 @@ int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
struct inode *fat_inode = MSDOS_SB(inode->i_sb)->fat_inode;
int err;
- err = mmb_fsync_noflush(filp, &MSDOS_I(inode)->i_metadata_bhs,
- start, end, datasync);
+ err = simple_fsync_noflush(filp, start, end, datasync);
if (err)
return err;
@@ -332,15 +331,15 @@ static int fat_free(struct inode *inode, int skip)
}
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME);
+ mark_inode_dirty(inode);
if (wait) {
- err = fat_sync_inode(inode);
+ err = sync_inode_metadata(inode, 1);
if (err) {
MSDOS_I(inode)->i_start = i_start;
MSDOS_I(inode)->i_logstart = i_logstart;
return err;
}
- } else
- mark_inode_dirty(inode);
+ }
/* Write a new EOF, and get the remaining cluster chain for freeing. */
if (skip) {
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 3aa52481ad5cf3..e3bb7b4713f2ac 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -623,7 +623,34 @@ struct inode *fat_build_inode(struct super_block *sb,
EXPORT_SYMBOL_GPL(fat_build_inode);
-static int __fat_write_inode(struct inode *inode, int wait);
+static int __fat_write_inode(struct inode *inode);
+
+static int fat_sync_inode_metadata(struct inode *inode,
+ struct writeback_control *wbc)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+ struct buffer_head *bh;
+ loff_t i_pos;
+ sector_t blocknr;
+ int offset;
+
+ if (inode->i_ino == MSDOS_ROOT_INO)
+ return 0;
+ i_pos = fat_i_pos_read(sbi, inode);
+ if (!i_pos)
+ return 0;
+
+ fat_get_blknr_offset(sbi, i_pos, &blocknr, &offset);
+ bh = sb_find_get_block_nonatomic(inode->i_sb, blocknr);
+ /*
+ * Buffer present? We leave buffer_dirty check for sync_dirty_buffer()
+ * for proper synchronization with ongoing IO.
+ */
+ if (bh && buffer_uptodate(bh))
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return mmb_sync(&MSDOS_I(inode)->i_metadata_bhs);
+}
static void fat_free_eofblocks(struct inode *inode)
{
@@ -640,7 +667,7 @@ static void fat_free_eofblocks(struct inode *inode)
* any corruption on the next access to the cluster
* chain for the file.
*/
- err = __fat_write_inode(inode, inode_needs_sync(inode));
+ err = sync_inode_metadata(inode, inode_needs_sync(inode));
if (err) {
fat_msg(inode->i_sb, KERN_WARNING, "Failed to "
"update on disk inode for unused "
@@ -854,7 +881,7 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0;
}
-static int __fat_write_inode(struct inode *inode, int wait)
+static int __fat_write_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
@@ -863,7 +890,7 @@ static int __fat_write_inode(struct inode *inode, int wait)
struct timespec64 mtime;
loff_t i_pos;
sector_t blocknr;
- int err, offset;
+ int offset;
if (inode->i_ino == MSDOS_ROOT_INO)
return 0;
@@ -907,11 +934,9 @@ static int __fat_write_inode(struct inode *inode, int wait)
}
spin_unlock(&sbi->inode_hash_lock);
mark_buffer_dirty(bh);
- err = 0;
- if (wait)
- err = sync_dirty_buffer(bh);
brelse(bh);
- return err;
+ set_inode_metadata_writeback(inode);
+ return 0;
}
static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -925,23 +950,17 @@ static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)
err = fat_clusters_flush(sb);
mutex_unlock(&MSDOS_SB(sb)->s_lock);
} else
- err = __fat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+ err = __fat_write_inode(inode);
return err;
}
-int fat_sync_inode(struct inode *inode)
-{
- return __fat_write_inode(inode, 1);
-}
-
-EXPORT_SYMBOL_GPL(fat_sync_inode);
-
static int fat_show_options(struct seq_file *m, struct dentry *root);
static const struct super_operations fat_sops = {
.alloc_inode = fat_alloc_inode,
.free_inode = fat_free_inode,
.write_inode = fat_write_inode,
+ .sync_inode_metadata = fat_sync_inode_metadata,
.evict_inode = fat_evict_inode,
.put_super = fat_put_super,
.statfs = fat_statfs,
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 3027ef53af214c..be18f6b5819ba7 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -146,16 +146,17 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
} else {
MSDOS_I(inode)->i_start = new_dclus;
MSDOS_I(inode)->i_logstart = new_dclus;
+ mark_inode_dirty(inode);
/*
* Since generic_write_sync() synchronizes regular files later,
* we sync here only directories.
*/
if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) {
- ret = fat_sync_inode(inode);
+ ret = sync_inode_metadata(inode, 1);
if (ret)
return ret;
- } else
- mark_inode_dirty(inode);
+ }
+
}
if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
fat_fs_error_ratelimit(
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
index 0fd2971ad4b131..868ef091048e40 100644
--- a/fs/fat/namei_msdos.c
+++ b/fs/fat/namei_msdos.c
@@ -252,12 +252,11 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name,
return err;
fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
+ mark_inode_dirty(dir);
if (IS_DIRSYNC(dir))
- (void)fat_sync_inode(dir);
- else
- mark_inode_dirty(dir);
+ err = sync_inode_metadata(dir, 1);
- return 0;
+ return err;
}
/***** Create a file */
@@ -473,21 +472,20 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
else
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+ mark_inode_dirty(old_inode);
if (IS_DIRSYNC(old_dir)) {
- err = fat_sync_inode(old_inode);
+ err = sync_inode_metadata(old_inode, 1);
if (err) {
MSDOS_I(old_inode)->i_attrs = old_attrs;
goto out;
}
- } else
- mark_inode_dirty(old_inode);
+ }
inode_inc_iversion(old_dir);
fat_truncate_time(old_dir, NULL, FAT_UPDATE_CMTIME);
+ mark_inode_dirty(old_dir);
if (IS_DIRSYNC(old_dir))
- (void)fat_sync_inode(old_dir);
- else
- mark_inode_dirty(old_dir);
+ err = sync_inode_metadata(old_dir, 1);
goto out;
}
}
@@ -518,26 +516,21 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
else
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
- if (IS_DIRSYNC(new_dir)) {
- err = fat_sync_inode(old_inode);
- if (err)
- goto error_inode;
- } else
- mark_inode_dirty(old_inode);
-
if (update_dotdot) {
fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
mmb_mark_buffer_dirty(dotdot_bh,
&MSDOS_I(old_inode)->i_metadata_bhs);
- if (IS_DIRSYNC(new_dir)) {
- err = sync_dirty_buffer(dotdot_bh);
- if (err)
- goto error_dotdot;
- }
drop_nlink(old_dir);
if (!new_inode)
inc_nlink(new_dir);
}
+ mark_inode_dirty(old_inode);
+ if (IS_DIRSYNC(new_dir)) {
+ err = sync_inode_metadata(old_inode, 1);
+ if (err)
+ goto error_dotdot;
+ }
+
err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */
old_sinfo.bh = NULL;
@@ -545,10 +538,9 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
goto error_dotdot;
inode_inc_iversion(old_dir);
fat_truncate_time(old_dir, &ts, FAT_UPDATE_CMTIME);
+ mark_inode_dirty(old_dir);
if (IS_DIRSYNC(old_dir))
- (void)fat_sync_inode(old_dir);
- else
- mark_inode_dirty(old_dir);
+ err = sync_inode_metadata(old_dir, 1);
if (new_inode) {
drop_nlink(new_inode);
@@ -571,14 +563,15 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
&MSDOS_I(old_inode)->i_metadata_bhs);
corrupt |= sync_dirty_buffer(dotdot_bh);
}
-error_inode:
fat_detach(old_inode);
fat_attach(old_inode, old_sinfo.i_pos);
MSDOS_I(old_inode)->i_attrs = old_attrs;
if (new_inode) {
fat_attach(new_inode, new_i_pos);
- if (corrupt)
- corrupt |= fat_sync_inode(new_inode);
+ if (corrupt) {
+ mark_inode_dirty(new_inode);
+ corrupt |= sync_inode_metadata(new_inode, 1);
+ }
} else {
/*
* If new entry was not sharing the data cluster, it
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index e909447873e364..308158895b1d8a 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -678,10 +678,9 @@ static int vfat_add_entry(struct inode *dir, const struct qstr *qname,
/* update timestamp */
fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
+ mark_inode_dirty(dir);
if (IS_DIRSYNC(dir))
- (void)fat_sync_inode(dir);
- else
- mark_inode_dirty(dir);
+ err = sync_inode_metadata(dir, 1);
cleanup:
kfree(slots);
return err;
@@ -904,9 +903,9 @@ static int vfat_get_dotdot_de(struct inode *inode, struct buffer_head **bh,
static int vfat_sync_ipos(struct inode *dir, struct inode *inode)
{
- if (IS_DIRSYNC(dir))
- return fat_sync_inode(inode);
mark_inode_dirty(inode);
+ if (IS_DIRSYNC(dir))
+ return sync_inode_metadata(inode, 1);
return 0;
}
@@ -921,14 +920,14 @@ static int vfat_update_dotdot_de(struct inode *dir, struct inode *inode,
return 0;
}
-static void vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts)
+static int vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts)
{
inode_inc_iversion(dir);
fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
+ mark_inode_dirty(dir);
if (IS_DIRSYNC(dir))
- (void)fat_sync_inode(dir);
- else
- mark_inode_dirty(dir);
+ return sync_inode_metadata(dir, 1);
+ return 0;
}
static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -996,7 +995,7 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
old_sinfo.bh = NULL;
if (err)
goto error_dotdot;
- vfat_update_dir_metadata(old_dir, &ts);
+ err = vfat_update_dir_metadata(old_dir, &ts);
if (new_inode) {
drop_nlink(new_inode);
@@ -1024,8 +1023,10 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
fat_attach(old_inode, old_sinfo.i_pos);
if (new_inode) {
fat_attach(new_inode, new_i_pos);
- if (corrupt)
- corrupt |= fat_sync_inode(new_inode);
+ if (corrupt) {
+ mark_inode_dirty(new_inode);
+ corrupt |= sync_inode_metadata(new_inode, 1);
+ }
} else {
/*
* If new entry was not sharing the data cluster, it
@@ -1123,10 +1124,13 @@ static int vfat_rename_exchange(struct inode *old_dir, struct dentry *old_dentry
vfat_move_nlink(new_dir, old_dir);
}
- vfat_update_dir_metadata(old_dir, &ts);
+ err = vfat_update_dir_metadata(old_dir, &ts);
/* if directories are not the same, update new_dir as well */
- if (old_dir != new_dir)
- vfat_update_dir_metadata(new_dir, &ts);
+ if (old_dir != new_dir) {
+ int err2 = vfat_update_dir_metadata(new_dir, &ts);
+ if (!err)
+ err = err2;
+ }
out:
brelse(old_dotdot_bh);
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index fdb8766d275a17..9d96357731a3d4 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -1851,6 +1851,22 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
if (ret == 0)
ret = err;
}
+
+ /*
+ * Do we need to wait for inode metadata IO possibly submitted
+ * by previous WB_SYNC_NONE writeback?
+ */
+ if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync &&
+ inode_state_read_once(inode) & I_METADATA_WRITEBACK) {
+ int err;
+
+ spin_lock(&inode->i_lock);
+ inode_state_clear(inode, I_METADATA_WRITEBACK);
+ spin_unlock(&inode->i_lock);
+ err = inode->i_sb->s_op->sync_inode_metadata(inode, wbc);
+ if (ret == 0)
+ ret = err;
+ }
wbc->unpinned_netfs_wb = false;
trace_writeback_single_inode(inode, wbc, nr_to_write);
return ret;
@@ -1892,14 +1908,17 @@ static int writeback_single_inode(struct inode *inode,
/*
* If the inode is already fully clean, then there's nothing to do.
*
- * For data-integrity syncs we also need to check whether any pages are
- * still under writeback, e.g. due to prior WB_SYNC_NONE writeback. If
- * there are any such pages, we'll need to wait for them.
+ * For data-integrity syncs we also need to check whether any folios or
+ * metadata are still under writeback, e.g. due to prior WB_SYNC_NONE
+ * writeback. If there, we'll need to wait for them.
*/
- if (!(inode_state_read(inode) & I_DIRTY_ALL) &&
- (wbc->sync_mode != WB_SYNC_ALL ||
- !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK)))
- goto out;
+ if (!(inode_state_read(inode) & I_DIRTY_ALL)) {
+ if (wbc->sync_mode != WB_SYNC_ALL)
+ goto out;
+ if (!mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK) &&
+ !(inode_state_read(inode) & I_METADATA_WRITEBACK))
+ goto out;
+ }
inode_state_set(inode, I_SYNC);
wbc_attach_and_unlock_inode(wbc, inode);
diff --git a/fs/libfs.c b/fs/libfs.c
index 5a0d276379d13b..27d7dc16fcb0a0 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1559,9 +1559,12 @@ int simple_fsync_noflush(struct file *file, loff_t start, loff_t end,
if (err)
return err;
- if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
+ if (!(inode_state_read_once(inode) &
+ (I_DIRTY_ALL | I_SYNC | I_METADATA_WRITEBACK)))
goto out;
- if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
+ if (datasync &&
+ !(inode_state_read_once(inode) &
+ (I_DIRTY_DATASYNC | I_SYNC | I_METADATA_WRITEBACK)))
goto out;
ret = sync_inode_metadata(inode, 1);
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index 361d26d87d2e2d..2ca16f849d5ac0 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -23,7 +23,7 @@ const struct file_operations minix_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate_shared = minix_readdir,
- .fsync = minix_fsync,
+ .fsync = simple_fsync,
};
/*
diff --git a/fs/minix/file.c b/fs/minix/file.c
index 86e5943cd2ff74..02aabbdb5deaa0 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -10,13 +10,6 @@
#include <linux/buffer_head.h>
#include "minix.h"
-int minix_fsync(struct file *file, loff_t start, loff_t end, int datasync)
-{
- return mmb_fsync(file,
- &minix_i(file->f_mapping->host)->i_metadata_bhs,
- start, end, datasync);
-}
-
/*
* We have mostly NULLs here: the current defaults are OK for
* the minix filesystem.
@@ -26,7 +19,7 @@ const struct file_operations minix_file_operations = {
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.mmap_prepare = generic_file_mmap_prepare,
- .fsync = minix_fsync,
+ .fsync = simple_fsync,
.splice_read = filemap_splice_read,
};
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index c30cc590698d6f..daf83e4ff25c81 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -24,6 +24,8 @@
static int minix_write_inode(struct inode *inode,
struct writeback_control *wbc);
+static int minix_sync_inode_metadata(struct inode *inode,
+ struct writeback_control *wbc);
static int minix_statfs(struct dentry *dentry, struct kstatfs *buf);
void __minix_error_inode(struct inode *inode, const char *function,
@@ -128,6 +130,7 @@ static const struct super_operations minix_sops = {
.alloc_inode = minix_alloc_inode,
.free_inode = minix_free_in_core_inode,
.write_inode = minix_write_inode,
+ .sync_inode_metadata = minix_sync_inode_metadata,
.evict_inode = minix_evict_inode,
.put_super = minix_put_super,
.statfs = minix_statfs,
@@ -630,7 +633,7 @@ struct inode *minix_iget(struct super_block *sb, unsigned long ino)
/*
* The minix V1 function to synchronize an inode.
*/
-static struct buffer_head * V1_minix_update_inode(struct inode * inode)
+static int V1_minix_update_inode(struct inode * inode)
{
struct buffer_head * bh;
struct minix_inode * raw_inode;
@@ -639,7 +642,7 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode)
raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
if (!raw_inode)
- return NULL;
+ return -EIO;
raw_inode->i_mode = inode->i_mode;
raw_inode->i_uid = fs_high2lowuid(i_uid_read(inode));
raw_inode->i_gid = fs_high2lowgid(i_gid_read(inode));
@@ -651,13 +654,15 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode)
else for (i = 0; i < 9; i++)
raw_inode->i_zone[i] = minix_inode->u.i1_data[i];
mark_buffer_dirty(bh);
- return bh;
+ brelse(bh);
+ set_inode_metadata_writeback(inode);
+ return 0;
}
/*
* The minix V2 function to synchronize an inode.
*/
-static struct buffer_head * V2_minix_update_inode(struct inode * inode)
+static int V2_minix_update_inode(struct inode * inode)
{
struct buffer_head * bh;
struct minix2_inode * raw_inode;
@@ -666,7 +671,7 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)
raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
if (!raw_inode)
- return NULL;
+ return -EIO;
raw_inode->i_mode = inode->i_mode;
raw_inode->i_uid = fs_high2lowuid(i_uid_read(inode));
raw_inode->i_gid = fs_high2lowgid(i_gid_read(inode));
@@ -680,29 +685,42 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)
else for (i = 0; i < 10; i++)
raw_inode->i_zone[i] = minix_inode->u.i2_data[i];
mark_buffer_dirty(bh);
- return bh;
+ brelse(bh);
+ set_inode_metadata_writeback(inode);
+ return 0;
}
static int minix_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+ if (INODE_VERSION(inode) == MINIX_V1)
+ return V1_minix_update_inode(inode);
+ return V2_minix_update_inode(inode);
+}
+
+static int minix_sync_inode_metadata(struct inode *inode,
+ struct writeback_control *wbc)
{
int err = 0;
struct buffer_head *bh;
+ void *raw_inode;
if (INODE_VERSION(inode) == MINIX_V1)
- bh = V1_minix_update_inode(inode);
+ raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
else
- bh = V2_minix_update_inode(inode);
- if (!bh)
+ raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
+ if (!raw_inode)
return -EIO;
- if (wbc->sync_mode == WB_SYNC_ALL && buffer_dirty(bh)) {
- sync_dirty_buffer(bh);
- if (buffer_req(bh) && !buffer_uptodate(bh)) {
- printk("IO error syncing minix inode [%s:%08llx]\n",
- inode->i_sb->s_id, inode->i_ino);
- err = -EIO;
- }
+ err = mmb_sync(&minix_i(inode)->i_metadata_bhs);
+ if (err)
+ goto out;
+ sync_dirty_buffer(bh);
+ if (buffer_write_io_error(bh)) {
+ printk("IO error syncing minix inode [%s:%08llx]\n",
+ inode->i_sb->s_id, inode->i_ino);
+ err = -EIO;
}
- brelse (bh);
+out:
+ brelse(bh);
return err;
}
diff --git a/fs/udf/dir.c b/fs/udf/dir.c
index ebc9f6a379fe3a..425e3c16293504 100644
--- a/fs/udf/dir.c
+++ b/fs/udf/dir.c
@@ -157,6 +157,6 @@ const struct file_operations udf_dir_operations = {
.read = generic_read_dir,
.iterate_shared = udf_readdir,
.unlocked_ioctl = udf_ioctl,
- .fsync = udf_fsync,
+ .fsync = simple_fsync,
.setlease = generic_setlease,
};
diff --git a/fs/udf/file.c b/fs/udf/file.c
index f7f1422de30fa4..57d11606a2a776 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -198,13 +198,6 @@ static int udf_file_mmap(struct file *file, struct vm_area_struct *vma)
return 0;
}
-int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync)
-{
- return mmb_fsync(file,
- &UDF_I(file->f_mapping->host)->i_metadata_bhs,
- start, end, datasync);
-}
-
const struct file_operations udf_file_operations = {
.read_iter = generic_file_read_iter,
.unlocked_ioctl = udf_ioctl,
@@ -212,7 +205,7 @@ const struct file_operations udf_file_operations = {
.mmap = udf_file_mmap,
.write_iter = udf_file_write_iter,
.release = udf_release_file,
- .fsync = udf_fsync,
+ .fsync = simple_fsync,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
.llseek = generic_file_llseek,
@@ -253,6 +246,8 @@ static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
setattr_copy(&nop_mnt_idmap, inode, attr);
mark_inode_dirty(inode);
+ if (IS_SYNC(inode))
+ sync_inode_metadata(inode, 1);
return 0;
}
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 67bcf83758c8fb..68c6c2ba8ed1a2 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -51,8 +51,6 @@
struct udf_map_rq;
static umode_t udf_convert_permissions(struct fileEntry *);
-static int udf_update_inode(struct inode *, int);
-static int udf_sync_inode(struct inode *inode);
static int udf_alloc_i_data(struct inode *inode, size_t size);
static int inode_getblk(struct inode *inode, struct udf_map_rq *map);
static int udf_insert_aext(struct inode *, struct extent_position,
@@ -142,7 +140,7 @@ void udf_evict_inode(struct inode *inode)
if (!inode->i_nlink) {
want_delete = 1;
udf_setsize(inode, 0);
- udf_update_inode(inode, IS_SYNC(inode));
+ sync_inode_metadata(inode, IS_SYNC(inode));
}
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB &&
inode->i_size != iinfo->i_lenExtents) {
@@ -936,10 +934,7 @@ static int inode_getblk(struct inode *inode, struct udf_map_rq *map)
iinfo->i_next_alloc_goal = newblocknum + 1;
inode_set_ctime_current(inode);
- if (IS_SYNC(inode))
- udf_sync_inode(inode);
- else
- mark_inode_dirty(inode);
+ mark_inode_dirty(inode);
ret = 0;
out_free:
brelse(prev_epos.bh);
@@ -1326,10 +1321,7 @@ int udf_setsize(struct inode *inode, loff_t newsize)
}
update_time:
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
- if (IS_SYNC(inode))
- udf_sync_inode(inode);
- else
- mark_inode_dirty(inode);
+ mark_inode_dirty(inode);
return err;
}
@@ -1705,14 +1697,28 @@ void udf_update_extra_perms(struct inode *inode, umode_t mode)
iinfo->i_extraPerms |= FE_PERM_O_DELETE;
}
-int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
+int udf_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)
{
- return udf_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
-}
+ struct buffer_head *bh;
+ int err = 0;
-static int udf_sync_inode(struct inode *inode)
-{
- return udf_update_inode(inode, 1);
+ bh = sb_getblk(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb,
+ &UDF_I(inode)->i_location, 0));
+ if (!bh)
+ return -EIO;
+
+ sync_dirty_buffer(bh);
+ if (buffer_write_io_error(bh)) {
+ udf_warn(inode->i_sb, "IO error syncing udf inode [%08llx]\n",
+ inode->i_ino);
+ err = -EIO;
+ goto out;
+ }
+ err = mmb_sync(&UDF_I(inode)->i_metadata_bhs);
+out:
+ brelse(bh);
+ return err;
}
static void udf_adjust_time(struct udf_inode_info *iinfo, struct timespec64 time)
@@ -1723,7 +1729,7 @@ static void udf_adjust_time(struct udf_inode_info *iinfo, struct timespec64 time
iinfo->i_crtime = time;
}
-static int udf_update_inode(struct inode *inode, int do_sync)
+int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
{
struct buffer_head *bh = NULL;
struct fileEntry *fe;
@@ -1732,7 +1738,6 @@ static int udf_update_inode(struct inode *inode, int do_sync)
uint32_t udfperms;
uint16_t icbflags;
uint16_t crclen;
- int err = 0;
struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
struct udf_inode_info *iinfo = UDF_I(inode);
@@ -1937,17 +1942,10 @@ static int udf_update_inode(struct inode *inode, int do_sync)
/* write the data blocks */
mark_buffer_dirty(bh);
- if (do_sync) {
- sync_dirty_buffer(bh);
- if (buffer_write_io_error(bh)) {
- udf_warn(inode->i_sb, "IO error syncing udf inode [%08llx]\n",
- inode->i_ino);
- err = -EIO;
- }
- }
brelse(bh);
+ set_inode_metadata_writeback(inode);
- return err;
+ return 0;
}
struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 7b85f5a2b79fc4..e7e9f2a0d24e80 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -211,6 +211,7 @@ static const struct super_operations udf_sb_ops = {
.alloc_inode = udf_alloc_inode,
.free_inode = udf_free_in_core_inode,
.write_inode = udf_write_inode,
+ .sync_inode_metadata = udf_sync_inode_metadata,
.evict_inode = udf_evict_inode,
.put_super = udf_put_super,
.sync_fs = udf_sync_fs,
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
index 6d951e05c0046a..86dc2d6a2ef1b8 100644
--- a/fs/udf/udfdecl.h
+++ b/fs/udf/udfdecl.h
@@ -137,7 +137,6 @@ static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)
/* file.c */
extern long udf_ioctl(struct file *, unsigned int, unsigned long);
-int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync);
/* inode.c */
extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
@@ -158,6 +157,7 @@ extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
extern int udf_setsize(struct inode *, loff_t);
extern void udf_evict_inode(struct inode *);
extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
+int udf_sync_inode_metadata(struct inode *, struct writeback_control *wbc);
extern int inode_bmap(struct inode *inode, sector_t block,
struct extent_position *pos, struct kernel_lb_addr *eloc,
uint32_t *elen, sector_t *offset, int8_t *etype);
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 8b23bc9a244cb3..fd2c7115c05427 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -210,10 +210,6 @@ void bh_end_async_write(struct bio *bio);
/* Things to do with metadata buffers list */
void mmb_mark_buffer_dirty(struct buffer_head *bh, struct mapping_metadata_bhs *mmb);
-int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,
- loff_t start, loff_t end, bool datasync);
-int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
- loff_t start, loff_t end, bool datasync);
void clean_bdev_aliases(struct block_device *bdev, sector_t block,
sector_t len);
static inline void clean_bdev_bh_alias(struct buffer_head *bh)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 50ce731a2b78f1..729e3cb89e384b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -740,7 +740,8 @@ enum inode_state_flags_enum {
I_CREATING = (1U << 15),
I_DONTCACHE = (1U << 16),
I_SYNC_QUEUED = (1U << 17),
- I_PINNING_NETFS_WB = (1U << 18)
+ I_PINNING_NETFS_WB = (1U << 18),
+ I_METADATA_WRITEBACK = (1U << 19),
};
#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)
@@ -2213,6 +2214,13 @@ static inline void mark_inode_dirty_sync(struct inode *inode)
__mark_inode_dirty(inode, I_DIRTY_SYNC);
}
+static inline void set_inode_metadata_writeback(struct inode *inode)
+{
+ spin_lock(&inode->i_lock);
+ inode_state_set(inode, I_METADATA_WRITEBACK);
+ spin_unlock(&inode->i_lock);
+}
+
/*
* returns the refcount on the inode. it can change arbitrarily.
*/
diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
index ef7941e9dc79ac..170561e8f2e2c4 100644
--- a/include/linux/fs/super_types.h
+++ b/include/linux/fs/super_types.h
@@ -86,6 +86,8 @@ struct super_operations {
void (*free_inode)(struct inode *inode);
void (*dirty_inode)(struct inode *inode, int flags);
int (*write_inode)(struct inode *inode, struct writeback_control *wbc);
+ int (*sync_inode_metadata)(struct inode *inode,
+ struct writeback_control *wbc);
int (*drop_inode)(struct inode *inode);
void (*evict_inode)(struct inode *inode);
void (*put_super)(struct super_block *sb);
|
| 3/2 | 2026/07/16 20:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28497 output: 67 thoughts: 631 Thoughts: **Fuzzing Target Identification: Metadata Sync Patch** Okay, so we have a patch here that's making some pretty significant changes to how the kernel handles inode metadata synchronization. It's adding `sync_inode_metadata` to the `super_operations` struct and then updating a whole bunch of filesystems – affs, bfs, ext2, ext4, fat, minix, and udf – to use this new function instead of the older `mmb_fsync` and `mmb_fsync_noflush` methods. This also touches `fs/fs-writeback.c`, specifically to deal with `I_METADATA_WRITEBACK`. This is definitely a functional change, touching core VFS writeback and multiple filesystems, so `WorthFuzzing=true` is a no-brainer. Now, let's nail down the specific functions we need to focus our fuzzing efforts on. The patch introduces `sync_inode_metadata`, so that's obviously a key target. It also modifies both `writeback_single_inode` and `__writeback_single_inode` within `fs/fs-writeback.c`. Specifically, it looks like it's updating those filesystems’ respective implementations: `ext4_sync_inode_metadata`, `ext2_sync_inode_metadata`, `fat_sync_inode_metadata`, `minix_sync_inode_metadata`, `udf_sync_inode_metadata`, and `bfs_sync_inode_metadata`. Plus, we have these other write functions impacted: `ext4_write_inode`, `ext2_write_inode`, `fat_write_inode`, `minix_write_inode`, `udf_write_inode`, and `bfs_write_inode`. Let's not forget about the eviction changes too, `ext4_evict_inode`, `ext2_evict_inode`, `fat_evict_inode`, `minix_evict_inode`, `udf_evict_inode`, and `bfs_evict_inode`. Oh, and `simple_fsync_noflush` in `fs/libfs.c` also. So, to recap the functions, I should focus on `__writeback_single_inode` and `writeback_single_inode` in `fs/fs-writeback.c`, then `ext4_sync_inode_metadata` in `fs/ext4/inode.c`, `ext2_sync_inode_metadata` in `fs/ext2/inode.c`, and then `fat_sync_inode_metadata` in `fs/fat/inode.c`. Okay, let's verify that the kernel configs we have are valid, so the modules will actually be present. Looks like we have `CONFIG_EXT4_FS`, `CONFIG_EXT2_FS`, `CONFIG_FAT_FS`, `CONFIG_MINIX_FS`, `CONFIG_UDF_FS`, `CONFIG_BFS_FS`, and `CONFIG_AFFS_FS`. Alright, and those align with the filesystems we’re dealing with. Although, wait... `FAT_FS` is usually `MSDOS_FS` and `VFAT_FS` - let me just quickly double-check the `fs/fat/Kconfig` file to be sure. Alright, that's verified. |
| 4/2 | 2026/07/16 20:32 | tool | grepper |
0mArgs:{
"Expression": "config (EXT4_FS|EXT2_FS|FAT_FS|MSDOS_FS|VFAT_FS|MINIX_FS|UDF_FS|BFS_FS|AFFS_FS)",
"PathPrefix": "fs/"
}Results: {
"Output": "fs/affs/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/affs/Kconfig:2:config AFFS_FS\nfs/affs/Kconfig-3-\ttristate \"Amiga FFS file system support\"\n--\nfs/bfs/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/bfs/Kconfig:2:config BFS_FS\nfs/bfs/Kconfig-3-\ttristate \"BFS file system support\"\n--\nfs/ext2/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/ext2/Kconfig:2:config EXT2_FS\nfs/ext2/Kconfig-3-\ttristate \"Second extended fs support (DEPRECATED)\"\n--\nfs/ext2/Kconfig-20-\nfs/ext2/Kconfig:21:config EXT2_FS_XATTR\nfs/ext2/Kconfig-22-\tbool \"Ext2 extended attributes\"\n--\nfs/ext2/Kconfig-29-\nfs/ext2/Kconfig:30:config EXT2_FS_POSIX_ACL\nfs/ext2/Kconfig-31-\tbool \"Ext2 POSIX Access Control Lists\"\n--\nfs/ext2/Kconfig-39-\nfs/ext2/Kconfig:40:config EXT2_FS_SECURITY\nfs/ext2/Kconfig-41-\tbool \"Ext2 Security Labels\"\n--\nfs/ext4/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/ext4/Kconfig:2:config EXT4_FS\nfs/ext4/Kconfig-3-\ttristate \"The Extended 4 (ext4) filesystem\"\n--\nfs/ext4/Kconfig=35=config EXT4_USE_FOR_EXT2\n--\nfs/ext4/Kconfig-45-\nfs/ext4/Kconfig:46:config EXT4_FS_POSIX_ACL\nfs/ext4/Kconfig-47-\tbool \"Ext4 POSIX Access Control Lists\"\n--\nfs/ext4/Kconfig-55-\nfs/ext4/Kconfig:56:config EXT4_FS_SECURITY\nfs/ext4/Kconfig-57-\tbool \"Ext4 Security Labels\"\n--\nfs/fat/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/fat/Kconfig:2:config FAT_FS\nfs/fat/Kconfig-3-\ttristate\n--\nfs/fat/Kconfig-37-\nfs/fat/Kconfig:38:config MSDOS_FS\nfs/fat/Kconfig-39-\ttristate \"MSDOS fs support\"\n--\nfs/fat/Kconfig-62-\nfs/fat/Kconfig:63:config VFAT_FS\nfs/fat/Kconfig-64-\ttristate \"VFAT (Windows-95) fs support\"\n--\nfs/minix/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/minix/Kconfig:2:config MINIX_FS\nfs/minix/Kconfig-3-\ttristate \"Minix file system support\"\n--\nfs/minix/Kconfig-20-\nfs/minix/Kconfig:21:config MINIX_FS_NATIVE_ENDIAN\nfs/minix/Kconfig-22-\tdef_bool MINIX_FS\n--\nfs/minix/Kconfig-24-\nfs/minix/Kconfig:25:config MINIX_FS_BIG_ENDIAN_16BIT_INDEXED\nfs/minix/Kconfig-26-\tdef_bool MINIX_FS\n--\nfs/udf/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/udf/Kconfig:2:config UDF_FS\nfs/udf/Kconfig-3-\ttristate \"UDF file system support\"\n"
} |
| 5/2 | 2026/07/16 20:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 30898 output: 34 thoughts: 347 Thoughts: **Analyzing Filesystem Synchronization Operations** Alright, let's see, I've got a list of filesystems to consider: `EXT4_FS`, `EXT2_FS`, `MSDOS_FS`, `VFAT_FS`, `MINIX_FS`, `UDF_FS`, `BFS_FS`, `AFFS_FS`. Now, I need to understand how their synchronization mechanisms are implemented. I'm focusing on the core functions. Okay, let's look at the functions themselves: `__writeback_single_inode`, `writeback_single_inode`, `ext4_sync_inode_metadata`, `ext2_sync_inode_metadata`, `fat_sync_inode_metadata`, `minix_sync_inode_metadata`, `udf_sync_inode_metadata`, `bfs_sync_inode_metadata`, and `simple_fsync_noflush`. A quick glance at the function names, and my knowledge tells me these are all related to writing data out to the disk in a controlled way, making them crucial for data integrity. Now, let me check where these are defined. I see that `__writeback_single_inode` and `writeback_single_inode` are in `fs/fs-writeback.c`. Let's confirm quickly whether they are static or global. Ah, `writeback_single_inode` is static. And `__writeback_single_inode` is also static. Now, the `ext4`, `ext2`, `udf` and `simple_fsync_noflush` sync functions are global. Looks like `fat_sync_inode_metadata`, `minix_sync_inode_metadata`, and `bfs_sync_inode_metadata` are also static, indicating they are likely internal to their respective filesystems and not part of a common synchronization interface. I should examine `fs/fs-writeback.c` further, in particular `writeback_single_inode`, to get a better understanding of how the core writeback process works. That's a good starting point. |
| 6/2 | 2026/07/16 20:32 | tool | grepper |
0mArgs:{
"Expression": "writeback_single_inode",
"PathPrefix": "fs/fs-writeback.c"
}Results: {
"Output": "fs/fs-writeback.c=1775=static int\nfs/fs-writeback.c:1776:__writeback_single_inode(struct inode *inode, struct writeback_control *wbc)\nfs/fs-writeback.c-1777-{\n--\nfs/fs-writeback.c-1784-\nfs/fs-writeback.c:1785:\ttrace_writeback_single_inode_start(inode, wbc, nr_to_write);\nfs/fs-writeback.c-1786-\n--\nfs/fs-writeback.c-1870-\twbc-\u003eunpinned_netfs_wb = false;\nfs/fs-writeback.c:1871:\ttrace_writeback_single_inode(inode, wbc, nr_to_write);\nfs/fs-writeback.c-1872-\treturn ret;\n--\nfs/fs-writeback.c-1883- */\nfs/fs-writeback.c:1884:static int writeback_single_inode(struct inode *inode,\nfs/fs-writeback.c-1885-\t\t\t\t struct writeback_control *wbc)\n--\nfs/fs-writeback.c-1924-\nfs/fs-writeback.c:1925:\tret = __writeback_single_inode(inode, wbc);\nfs/fs-writeback.c-1926-\n--\nfs/fs-writeback.c=1999=static long writeback_sb_inodes(struct super_block *sb,\n--\nfs/fs-writeback.c-2097-\t\t */\nfs/fs-writeback.c:2098:\t\t__writeback_single_inode(inode, \u0026wbc);\nfs/fs-writeback.c-2099-\n--\nfs/fs-writeback.c=2693=void __mark_inode_dirty(struct inode *inode, int flags)\n--\nfs/fs-writeback.c-2747-\t/*\nfs/fs-writeback.c:2748:\t * Paired with smp_mb() in __writeback_single_inode() for the\nfs/fs-writeback.c-2749-\t * following lockless i_state test. See there for details.\n--\nfs/fs-writeback.c=3066=int write_inode_now(struct inode *inode, int sync)\n--\nfs/fs-writeback.c-3078-\tmight_sleep();\nfs/fs-writeback.c:3079:\treturn writeback_single_inode(inode, \u0026wbc);\nfs/fs-writeback.c-3080-}\n--\nfs/fs-writeback.c=3092=int sync_inode_metadata(struct inode *inode, int wait)\n--\nfs/fs-writeback.c-3098-\nfs/fs-writeback.c:3099:\treturn writeback_single_inode(inode, \u0026wbc);\nfs/fs-writeback.c-3100-}\n"
} |
| 7/2 | 2026/07/16 20:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 32506 output: 285 thoughts: 1217 Thoughts: **Focusing on Metadata Synchronization** Okay, so this patch introduces a new function pointer, `sync_inode_metadata`, into the `super_operations` struct. It looks like it's going to be used to provide a more specific way to flush metadata for an inode, potentially as part of writeback. The introduction of the `I_METADATA_WRITEBACK` flag is also significant; it suggests a refined control flow for when and how metadata is written back. The core of the logic seems to reside in `__writeback_single_inode` and `writeback_single_inode`. I'm going to add the implementations of `sync_inode_metadata` for the various filesystems to my list of symbols to watch: `__writeback_single_inode`, `writeback_single_inode`, `ext4_sync_inode_metadata`, `ext2_sync_inode_metadata`, `fat_sync_inode_metadata`, `minix_sync_inode_metadata`, `udf_sync_inode_metadata`, `bfs_sync_inode_metadata`, and `simple_fsync_noflush`. Wait, just a quick sanity check. I need to make sure I include those static functions. Okay, `__writeback_single_inode` and `writeback_single_inode` are indeed static, but those are fine to be in `FocusSymbols`. Let me quickly confirm the other functions. Okay, `fat_sync_inode_metadata`, `minix_sync_inode_metadata`, and `bfs_sync_inode_metadata` are also static. Let me confirm the globals. `ext4_sync_inode_metadata`, `ext2_sync_inode_metadata`, `udf_sync_inode_metadata`, and `simple_fsync_noflush` are global. Now, a quick review of the relevant config options to ensure I'm covering the right filesystems: `EXT4_FS`, `EXT2_FS`, `MSDOS_FS`, `VFAT_FS`, `MINIX_FS`, `UDF_FS`, `BFS_FS`, and `AFFS_FS`. I should probably do a quick scan of related functions to see if I'm missing anything related to metadata manipulation in these filesystems. Let's see... looking at the context, and cross referencing related functions... nothing else jumps out at me as a critical part of this particular functional change. So the main focus will be on the added `sync_inode_metadata` implementations themselves, as well as the core writeback functions. I'm adding these to `FocusSymbols`. Final list: * `__writeback_single_inode` * `writeback_single_inode` * `ext4_sync_inode_metadata` * `ext2_sync_inode_metadata` * `fat_sync_inode_metadata` * `minix_sync_inode_metadata` * `udf_sync_inode_metadata` * `bfs_sync_inode_metadata` * `simple_fsync_noflush` Configs: * `EXT4_FS` * `EXT2_FS` * `MSDOS_FS` * `VFAT_FS` * `MINIX_FS` * `UDF_FS` * `BFS_FS` * `AFFS_FS` The patch is touching core VFS writeback and the filesystem metadata handling. This is a big change, so it's a good candidate for fuzzing. Alright, time to call `set-results`. |
| 8/2 | 2026/07/16 20:32 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"EXT4_FS",
"EXT2_FS",
"MSDOS_FS",
"VFAT_FS",
"MINIX_FS",
"UDF_FS",
"BFS_FS",
"AFFS_FS"
],
"FocusSymbols": [
"__writeback_single_inode",
"writeback_single_inode",
"ext4_sync_inode_metadata",
"ext2_sync_inode_metadata",
"fat_sync_inode_metadata",
"minix_sync_inode_metadata",
"udf_sync_inode_metadata",
"bfs_sync_inode_metadata",
"simple_fsync_noflush"
],
"Reasoning": "The patch introduces a new `sync_inode_metadata` callback in `super_operations` and modifies the VFS writeback logic to handle a new `I_METADATA_WRITEBACK` flag. It implements this callback for several filesystems (ext4, ext2, fat, minix, udf, bfs) and refactors their metadata synchronization logic, replacing `mmb_fsync`. This is a significant functional change to core VFS writeback and filesystem metadata handling, making it highly relevant for fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"EXT4_FS",
"EXT2_FS",
"MSDOS_FS",
"VFAT_FS",
"MINIX_FS",
"UDF_FS",
"BFS_FS",
"AFFS_FS"
],
"FocusSymbols": [
"__writeback_single_inode",
"writeback_single_inode",
"ext4_sync_inode_metadata",
"ext2_sync_inode_metadata",
"fat_sync_inode_metadata",
"minix_sync_inode_metadata",
"udf_sync_inode_metadata",
"bfs_sync_inode_metadata",
"simple_fsync_noflush"
],
"Reasoning": "The patch introduces a new `sync_inode_metadata` callback in `super_operations` and modifies the VFS writeback logic to handle a new `I_METADATA_WRITEBACK` flag. It implements this callback for several filesystems (ext4, ext2, fat, minix, udf, bfs) and refactors their metadata synchronization logic, replacing `mmb_fsync`. This is a significant functional change to core VFS writeback and filesystem metadata handling, making it highly relevant for fuzzing.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|