From: Josef Bacik <josef@toxicpanda.com>
To: Miklos Szeredi <miklos@szeredi.hu>,
Amir Goldstein <amir73il@gmail.com>,
Christian Brauner <brauner@kernel.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>, Jan Kara <jack@suse.cz>,
Theodore Ts'o <tytso@mit.edu>, Carlos Maiolino <cem@kernel.org>,
David Sterba <dsterba@suse.com>, Chris Mason <mason@kernel.org>,
Chengguang Xu <cgxu519@mykernel.net>,
linux-unionfs@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-ext4@vger.kernel.org, linux-xfs@vger.kernel.org,
linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org,
Josef Bacik <josef@toxicpanda.com>
Subject: [PATCH 09/10] ovl: handle lazytime on the upper filesystem
Date: Tue, 27 Oct 2026 21:09:35 +0000 [thread overview]
Message-ID: <20261027-b4-ovl-containerized-sync-v1-9-802e512379a6@toxicpanda.com> (raw)
In-Reply-To: <20261027-b4-ovl-containerized-sync-v1-0-802e512379a6@toxicpanda.com>
With lazytime the timestamps of the upper inode stay in memory. A pass of
the flusher writes the data and leaves them alone, for up to 12 hours, so
the overlay inode can't be clean after it: a syncfs() which only looks at
dirty overlay inodes would miss the timestamps.
Keeping it dirty for that long isn't an option either. The flusher would
walk it every time, and a dirty inode can't be reclaimed. The upper inode
doesn't have that problem. It sits on a list of its own, and its
timestamps are written when its last reference goes away, which is what
happens under memory pressure. But the overlay inode holds a reference to
it.
Do the same one level up. When the timestamps are all that is left, give
the overlay inode I_DIRTY_TIME. That takes it off the list which the
flusher walks, while syncfs() still gets to it. Implement
->sync_lazytime() to pass on to the upper inode what the VFS tells the
overlay inode: on syncfs() and fsync(), when the 12 hours are over, and
when the last reference to the overlay inode goes away.
A change through the overlay to an inode which is parked like this also
writes the timestamps, because marking it dirty ends in ->sync_lazytime().
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/overlayfs/dir.c | 1 +
fs/overlayfs/inode.c | 17 +++++++++++++++++
fs/overlayfs/overlayfs.h | 1 +
fs/overlayfs/super.c | 24 ++++++++++++++++++++++++
4 files changed, 43 insertions(+)
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 1194ccf981c3..fc47beffa67e 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -1489,6 +1489,7 @@ const struct inode_operations ovl_dir_inode_operations = {
.get_acl = ovl_get_acl,
.set_acl = ovl_set_acl,
.update_time = ovl_update_time,
+ .sync_lazytime = ovl_sync_lazytime,
.fileattr_get = ovl_fileattr_get,
.fileattr_set = ovl_fileattr_set,
.tmpfile = ovl_tmpfile,
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 9a4bf7c2502b..858802969017 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -743,6 +743,20 @@ int ovl_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
return err;
}
+/*
+ * The overlay inode has I_DIRTY_TIME because the upper inode has, see
+ * ovl_park_inode(). Pass it on, and keep track of the upper inode until that
+ * has been written.
+ */
+void ovl_sync_lazytime(struct inode *inode)
+{
+ struct inode *upper = ovl_inode_upper(inode);
+
+ if (upper && (inode_state_read_once(upper) & I_DIRTY_TIME))
+ mark_inode_dirty_sync(upper);
+ ovl_mark_inode_dirty(inode);
+}
+
static const struct inode_operations ovl_file_inode_operations = {
.setattr = ovl_setattr,
.permission = ovl_permission,
@@ -752,6 +766,7 @@ static const struct inode_operations ovl_file_inode_operations = {
.get_acl = ovl_get_acl,
.set_acl = ovl_set_acl,
.update_time = ovl_update_time,
+ .sync_lazytime = ovl_sync_lazytime,
.fiemap = ovl_fiemap,
.fileattr_get = ovl_fileattr_get,
.fileattr_set = ovl_fileattr_set,
@@ -763,6 +778,7 @@ static const struct inode_operations ovl_symlink_inode_operations = {
.getattr = ovl_getattr,
.listxattr = ovl_listxattr,
.update_time = ovl_update_time,
+ .sync_lazytime = ovl_sync_lazytime,
};
static const struct inode_operations ovl_special_inode_operations = {
@@ -774,6 +790,7 @@ static const struct inode_operations ovl_special_inode_operations = {
.get_acl = ovl_get_acl,
.set_acl = ovl_set_acl,
.update_time = ovl_update_time,
+ .sync_lazytime = ovl_sync_lazytime,
};
static const struct address_space_operations ovl_aops = {
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index f5a17bc258b6..ad1084be6191 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -867,6 +867,7 @@ static inline struct posix_acl *ovl_get_acl_path(const struct path *path,
int ovl_update_time(struct inode *inode, enum fs_update_time type,
unsigned int flags);
+void ovl_sync_lazytime(struct inode *inode);
bool ovl_is_private_xattr(struct super_block *sb, const char *name);
struct ovl_inode_params {
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 954648d41700..631f68a97980 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -344,6 +344,28 @@ static bool ovl_upper_unfinished(struct inode *upper)
(inode_state_read_once(upper) & I_SYNC);
}
+/*
+ * All that is left of @inode are timestamps which lazytime holds back on the
+ * upper filesystem. A pass which does not wait leaves them alone, for up to 12
+ * hours. Do to the overlay inode what was done to the upper inode: with
+ * I_DIRTY_TIME it is off the list which the flusher walks, syncfs() still gets
+ * to it, and ovl_sync_lazytime() is told when the time has come.
+ *
+ * One such time is when the last reference goes away, which it never does for
+ * the upper inode as long as the overlay inode is there. It may have gone away
+ * already. iput() looks for I_DIRTY_TIME under i_lock before it drops the last
+ * reference, so either it sees the flag or the count is zero here.
+ */
+static void ovl_park_inode(struct inode *inode)
+{
+ if (!ovl_tracks_dirty(OVL_FS(inode->i_sb)))
+ return;
+
+ __mark_inode_dirty(inode, I_DIRTY_TIME);
+ if (!icount_read_once(inode))
+ mark_inode_dirty_sync(inode);
+}
+
static int ovl_write_inode(struct inode *inode, struct writeback_control *wbc)
{
struct inode *upper = ovl_inode_upper(inode);
@@ -360,6 +382,8 @@ static int ovl_write_inode(struct inode *inode, struct writeback_control *wbc)
if (ovl_upper_unfinished(upper))
ovl_mark_inode_dirty(inode);
+ else if (inode_state_read_once(upper) & I_DIRTY_TIME)
+ ovl_park_inode(inode);
return ret;
}
--
2.55.0
next prev parent reply other threads:[~2026-10-27 21:10 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 21:09 [PATCH 00/10] ovl: only sync what was written through the overlay Josef Bacik
2026-10-27 21:09 ` [PATCH 01/10] fs: add SB_I_OVL_USE_SYNC_FS Josef Bacik
2026-10-28 15:21 ` Christoph Hellwig
2026-10-28 15:36 ` Amir Goldstein
2026-10-27 21:09 ` [PATCH 02/10] ext4: set SB_I_OVL_USE_SYNC_FS if there is a journal Josef Bacik
2026-10-27 21:09 ` [PATCH 03/10] xfs: set SB_I_OVL_USE_SYNC_FS Josef Bacik
2026-10-28 15:19 ` Christoph Hellwig
2026-10-27 21:09 ` [PATCH 04/10] btrfs: " Josef Bacik
2026-10-27 21:09 ` [PATCH 05/10] ovl: setup overlayfs' private bdi Josef Bacik
2026-10-27 21:09 ` [PATCH 06/10] ovl: mark the overlay inode dirty on modification Josef Bacik
2026-10-27 21:09 ` [PATCH 07/10] ovl: implement overlayfs' own ->write_inode operation Josef Bacik
2026-10-27 21:09 ` [PATCH 08/10] ovl: cache dirty overlayfs' inode Josef Bacik
2026-10-27 21:09 ` Josef Bacik [this message]
2026-10-27 21:09 ` [PATCH 10/10] ovl: implement containerized syncfs for overlayfs Josef Bacik
2026-10-28 15:12 ` [PATCH 00/10] ovl: only sync what was written through the overlay Amir Goldstein
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261027-b4-ovl-containerized-sync-v1-9-802e512379a6@toxicpanda.com \
--to=josef@toxicpanda.com \
--cc=amir73il@gmail.com \
--cc=brauner@kernel.org \
--cc=cem@kernel.org \
--cc=cgxu519@mykernel.net \
--cc=dsterba@suse.com \
--cc=jack@suse.cz \
--cc=linux-btrfs@vger.kernel.org \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-unionfs@vger.kernel.org \
--cc=linux-xfs@vger.kernel.org \
--cc=mason@kernel.org \
--cc=miklos@szeredi.hu \
--cc=tytso@mit.edu \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®