summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/fs-writeback.c33
-rw-r--r--fs/libfs.c6
-rw-r--r--include/linux/fs.h10
-rw-r--r--include/linux/fs/super_types.h2
4 files changed, 41 insertions, 10 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index fdb8766d275a..9d96357731a3 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 57e5971b6331..27d7dc16fcb0 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1559,10 +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 | I_SYNC)))
+ 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 | I_SYNC)))
+ !(inode_state_read_once(inode) &
+ (I_DIRTY_DATASYNC | I_SYNC | I_METADATA_WRITEBACK)))
goto out;
ret = sync_inode_metadata(inode, 1);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 50ce731a2b78..729e3cb89e38 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 ef7941e9dc79..170561e8f2e2 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);