[PATCH 07/10] ovl: implement overlayfs' own ->write_inode operation
From: Josef Bacik
Date: Tue Oct 27 2026 - 17:15:15 EST
From: Chengguang Xu <charliecgxu@xxxxxxxxxxx>
Sync dirty data and meta of upper inode in overlayfs' ->write_inode().
The overlay inode stays dirty for as long as there may be more to do:
while the upper inode is open for writing, because stores through a shared
mapping go unnoticed and the mapping holds the file open, and while the
upper inode has pages which are dirty or under writeback, because a pass
which doesn't wait has only started them. So by the time the overlay
inode is clean, the data which was written through it is on disk, and a
syncfs() which only looks at the dirty ones misses nothing.
Signed-off-by: Chengguang Xu <charliecgxu@xxxxxxxxxxx>
[josef: redirty only while the upper inode is unfinished instead of always,
no check for volatile, nothing is marked dirty there]
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
fs/overlayfs/super.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index d21e7cbb70c2..0aec7059e9da 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -15,6 +15,8 @@
#include <linux/seq_file.h>
#include <linux/posix_acl_xattr.h>
#include <linux/exportfs.h>
+#include <linux/pagemap.h>
+#include <linux/writeback.h>
#include <linux/file.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
@@ -313,12 +315,62 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
return err;
}
+/*
+ * Could there be anything left to write or to wait for on @upper?
+ *
+ * Whoever has it open for writing may have it mapped, and stores through a
+ * mapping go unnoticed. The mapping holds the file open.
+ *
+ * Writeback which was only started, by us or by the flusher of the upper
+ * filesystem, shows in the page cache tags until it is done. The dirty flags
+ * of the inode do not, they are cleared at the start.
+ *
+ * Metadata is not looked at. The upper filesystem logs it, see
+ * ovl_tracks_dirty(), and ovl_sync_fs() has the log committed.
+ *
+ * Nothing orders these reads. The one order which matters is to not see
+ * i_writecount at zero and miss the tag of a page which was dirtied through
+ * the mapping. The tag is set when the mapping goes away at the latest, the
+ * count drops when the file is released after that, with the locks of
+ * unmapping and of the final fput in between.
+ */
+static bool ovl_upper_unfinished(struct inode *upper)
+{
+ struct address_space *mapping = upper->i_mapping;
+
+ return inode_is_open_for_write(upper) ||
+ mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) ||
+ mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK) ||
+ (inode_state_read_once(upper) & I_SYNC);
+}
+
+static int ovl_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+ struct inode *upper = ovl_inode_upper(inode);
+ int ret;
+
+ if (!upper)
+ return 0;
+
+ /*
+ * Go through writeback_single_inode(), I_SYNC is what serializes us
+ * with the writeback of the upper filesystem.
+ */
+ ret = write_inode_now(upper, wbc->sync_mode == WB_SYNC_ALL);
+
+ if (ovl_upper_unfinished(upper))
+ ovl_mark_inode_dirty(inode);
+
+ return ret;
+}
+
static const struct super_operations ovl_super_operations = {
.alloc_inode = ovl_alloc_inode,
.free_inode = ovl_free_inode,
.destroy_inode = ovl_destroy_inode,
.drop_inode = inode_just_drop,
.put_super = ovl_put_super,
+ .write_inode = ovl_write_inode,
.sync_fs = ovl_sync_fs,
.statfs = ovl_statfs,
.show_options = ovl_show_options,
--
2.55.0