[PATCH v2 10/10] ovl: implement containerized syncfs for overlayfs
From: Josef Bacik
Date: Wed Oct 28 2026 - 21:13:26 EST
From: Chengguang Xu <charliecgxu@xxxxxxxxxxx>
Now overlayfs can only sync own dirty inodes during syncfs,
so remove unnecessary sync_filesystem() on upper file system.
Call ->sync_fs() and write the block device of the upper filesystem
instead, which is what sync_filesystem() does after it has written the
inodes. Do the same for a read-only remount.
With many containers on one upper filesystem, stopping one of them no
longer writes out the dirty data of all the others.
Signed-off-by: Chengguang Xu <charliecgxu@xxxxxxxxxxx>
[josef: everything after the first paragraph, no wait_sb_inodes() on the
upper filesystem]
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
fs/overlayfs/params.c | 14 +++-----------
fs/overlayfs/super.c | 28 ++++++++++++++++++++--------
2 files changed, 23 insertions(+), 19 deletions(-)
diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c
index 5a15e5478a6b..43fe0b1ef11f 100644
--- a/fs/overlayfs/params.c
+++ b/fs/overlayfs/params.c
@@ -764,22 +764,14 @@ static int ovl_reconfigure(struct fs_context *fc)
{
struct super_block *sb = fc->root->d_sb;
struct ovl_fs *ofs = OVL_FS(sb);
- struct super_block *upper_sb;
- int ret = 0;
if (!(fc->sb_flags & SB_RDONLY) && ovl_force_readonly(ofs))
return -EROFS;
- if (fc->sb_flags & SB_RDONLY && !sb_rdonly(sb)) {
- upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
- if (ovl_should_sync(ofs)) {
- down_read(&upper_sb->s_umount);
- ret = sync_filesystem(upper_sb);
- up_read(&upper_sb->s_umount);
- }
- }
+ if (fc->sb_flags & SB_RDONLY && !sb_rdonly(sb) && ovl_should_sync(ofs))
+ return sync_filesystem(sb);
- return ret;
+ return 0;
}
static const struct fs_context_operations ovl_context_ops = {
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 84258edb5530..361a3afa4ac8 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -15,6 +15,7 @@
#include <linux/seq_file.h>
#include <linux/posix_acl_xattr.h>
#include <linux/exportfs.h>
+#include <linux/blkdev.h>
#include <linux/pagemap.h>
#include <linux/writeback.h>
#include <linux/file.h>
@@ -271,18 +272,29 @@ static int ovl_sync_fs(struct super_block *sb, int wait)
/*
* Not called for sync(2) call or an emergency sync (SB_I_SKIP_SYNC).
* All the super blocks will be iterated, including upper_sb.
- *
- * If this is a syncfs(2) call, then we do need to call
- * sync_filesystem() on upper_sb, but enough if we do it when being
- * called with wait == 1.
*/
- if (!wait)
- return 0;
-
upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
+ /*
+ * sync_filesystem() has already written the dirty inodes of this
+ * overlay through ovl_write_inode(), including the fake ones, see
+ * ovl_upper_modified(). What is left are the last two steps of
+ * sync_filesystem() on the upper filesystem. Do not write the inodes
+ * that others have dirtied there.
+ */
down_read(&upper_sb->s_umount);
- ret = sync_filesystem(upper_sb);
+ /* Like sync_filesystem(), do not call ->sync_fs() on a read-only sb */
+ ret = 0;
+ if (sb_rdonly(upper_sb))
+ goto out;
+
+ if (upper_sb->s_op->sync_fs)
+ ret = upper_sb->s_op->sync_fs(upper_sb, wait);
+ if (!ret && wait)
+ ret = sync_blockdev(upper_sb->s_bdev);
+ else if (!ret)
+ ret = sync_blockdev_nowait(upper_sb->s_bdev);
+out:
up_read(&upper_sb->s_umount);
return ret;
--
2.55.0