lazytime fixes and cleanups
Subsystems (9)
cover lazytime fixes and cleanups
Hello,
This patchset fixes the lazytime bug which I reported last year
(https://lore.kernel.org/r/[email protected]). This bug causes inodes with dirty timestamps to remain dirty after a sync, which causes the inodes to be unnecessarily written again, and also causes the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl to not work properly. This bug is causing xfstest generic/580 to fail when lazytime is enabled.
Ted and Christoph proposed fixes for this. Ted's fix
(https://lore.kernel.org/r/[email protected]) changed the call to mark_inode_dirty_sync(inode) in __writeback_single_inode() to ->dirty_inode(inode, I_DIRTY_TIME_EXPIRED). However this would have broken XFS, which wants an I_DIRTY_SYNC notification. Also, people preferred a larger rework involving adding a ->lazytime_expired method.
Christoph's fix
(https://lore.kernel.org/r/[email protected])
introduced ->lazytime_expired, but it wasn't correct because it didn't consider cases in which timestamps are force-expired.
To resolve this, I propose that we first fix the bug by making __writeback_single_inode() do an I_DIRTY_SYNC notification if the timestamps expired (patch #1).
Then, the remaining patches introduce ->lazytime_expired and make XFS use it. They also clean up various things, such as improving comments.
Also, it turns out that lazytime on XFS is broken because it doesn't actually write timestamps to disk after a sync() or after 24 hours. This is fixed by the patch to switch XFS to use ->lazytime_expired. I've written an xfstest which reproduces this bug.
This patchset applies to v5.11-rc2.
Eric Biggers (13):
fs: avoid double-writing inodes on lazytime expiration
gfs2: don't worry about I_DIRTY_TIME in gfs2_fsync()
fs: only specify I_DIRTY_TIME when needed in generic_update_time()
fat: only specify I_DIRTY_TIME when needed in fat_update_time()
fs: don't call ->dirty_inode for lazytime timestamp updates
fs: pass only I_DIRTY_INODE flags to ->dirty_inode
fs: correctly document the inode dirty flags
ext4: simplify i_state checks in __ext4_update_other_inode_time()
fs: drop redundant checks from __writeback_single_inode()
fs: clean up __mark_inode_dirty() a bit
fs: add a lazytime_expired method
xfs: remove a stale comment from xfs_file_aio_write_checks()
xfs: implement lazytime_expired
Documentation/filesystems/locking.rst | 2 + Documentation/filesystems/vfs.rst | 15 ++++- fs/ext4/inode.c | 20 ++---- fs/f2fs/super.c | 3 - fs/fat/misc.c | 21 +++--- fs/fs-writeback.c | 94 +++++++++++++++++++-------- fs/gfs2/file.c | 4 +- fs/gfs2/super.c | 2 - fs/inode.c | 40 ++++++------ fs/sync.c | 2 +- fs/xfs/xfs_file.c | 6 -- fs/xfs/xfs_super.c | 12 +--- include/linux/fs.h | 25 +++++-- 13 files changed, 143 insertions(+), 103 deletions(-)
base-commit: e71ba9452f0b5b2e8dc8aa5445198cd9214a6a62
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
PATCH 1/13 fs: avoid double-writing inodes on lazytime expiration
From: Eric Biggers <[email protected]>
When lazytime is enabled and an inode with dirty timestamps is being expired, either due to dirtytime_expire_interval being exceeded or due to a sync or syncfs system call, we need to inform the filesystem that the inode is dirty so that the inode's timestamps can be copied out to the on-disk data structures. That's because if the filesystem supports lazytime, it will have ignored the ->dirty_inode(inode, I_DIRTY_TIME) notification when the timestamp was modified in memory.
Currently this is accomplished by calling mark_inode_dirty_sync() from __writeback_single_inode(). However, this has the unfortunate side effect of also putting the inode the writeback list. That's not appropriate in this case, since the inode is already being written.
That causes the inode to remain dirty after a sync. Normally that's just wasteful, as it causes the inode to be written twice. But when fscrypt is used this bug also partially breaks the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl, as the ioctl reports that files are still in-use when they aren't. For more details, see the original report at https://lore.kernel.org/r/[email protected]
Fix this by calling ->dirty_inode(inode, I_DIRTY_SYNC) directly instead of mark_inode_dirty_sync().
This fixes xfstest generic/580 when lazytime is enabled.
A later patch will introduce a ->lazytime_expired method to cleanly separate out the lazytime expiration case, in particular for XFS which uses the VFS-level dirtiness tracking only for lazytime. But that's separate from fixing this bug. Also, note that XFS will incorrectly ignore the I_DIRTY_SYNC notification from __writeback_single_inode() both before and after this patch, as I_DIRTY_TIME was already cleared in i_state. Later patches will fix this separate bug.
--- fs/fs-writeback.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
fs/fs-writeback.c +14 -3
@@ -1509,11 +1509,22 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)spin_unlock (&inode->i_lock); -if (dirty &I_DIRTY_TIME ) -mark_inode_dirty_sync (inode);/* Don't write the inode if only I_DIRTY_PAGES was set */ if (dirty & ~I_DIRTY_PAGES ) { -int err =write_inode (inode, wbc); +int err; + +/* +* If the inode is being written due to a lazytime timestamp +* expiration, then the filesystem needs to be notified about it +* so that e.g. the filesystem can update on-disk fields and +* journal the timestamp update. Just calling write_inode() +* isn't enough. Don't call mark_inode_dirty_sync(), as that +* would put the inode back on the dirty list. +*/ +if ((dirty &I_DIRTY_TIME ) && inode->i_sb->s_op->dirty_inode) + inode->i_sb->s_op->dirty_inode (inode,I_DIRTY_SYNC ); + + err =write_inode (inode, wbc);if (ret ==0 ) ret = err; }
base-commit: e71ba9452f0b5b2e8dc8aa5445198cd9214a6a62
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Jan Kara
maintainerRe: [PATCH 01/13] fs: avoid double-writing inodes on lazytime expiration
Quoted 35 lines · Eric Biggers
From: Eric Biggers <[email protected]>
When lazytime is enabled and an inode with dirty timestamps is being expired, either due to
dirtytime_expire_intervalbeing exceeded or due to a sync or syncfs system call, we need to inform the filesystem that the inode is dirty so that the inode's timestamps can be copied out to the on-disk data structures. That's because if the filesystem supports lazytime, it will have ignored the ->dirty_inode(inode,I_DIRTY_TIME) notification when the timestamp was modified in memory.Currently this is accomplished by calling
mark_inode_dirty_sync()from__writeback_single_inode(). However, this has the unfortunate side effect of also putting the inode the writeback list. That's not appropriate in this case, since the inode is already being written.That causes the inode to remain dirty after a sync. Normally that's just wasteful, as it causes the inode to be written twice. But when fscrypt is used this bug also partially breaks the
FS_IOC_REMOVE_ENCRYPTION_KEYioctl, as the ioctl reports that files are still in-use when they aren't. For more details, see the original report at https://lore.kernel.org/r/[email protected]Fix this by calling ->dirty_inode(inode,
I_DIRTY_SYNC) directly instead ofmark_inode_dirty_sync().This fixes xfstest generic/580 when lazytime is enabled.
A later patch will introduce a ->lazytime_expired method to cleanly separate out the lazytime expiration case, in particular for XFS which uses the VFS-level dirtiness tracking only for lazytime. But that's separate from fixing this bug. Also, note that XFS will incorrectly ignore the
I_DIRTY_SYNCnotification from__writeback_single_inode()both before and after this patch, asI_DIRTY_TIMEwas already cleared ini_state. Later patches will fix this separate bug.
Quoted 3 lines
Fixes 0ae45f63d4ef ("vfs: add support for a lazytime mount option")Signed-off-by Eric Biggers <[email protected]>
Good catch! It could also cause issues with filesystem freezing which kind of assumes that the filesystem will be clean after sync_filesystem() (otherwise writeback threads can get stalled on frozen filesystem while holding some locks and generally the system behavior becomes kind of awkward).
Quoted 20 lines
fs/fs-writeback.c +7
-3@@ -1509,11 +1509,22 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)spin_unlock (&inode->i_lock); -if (dirty &I_DIRTY_TIME ) -mark_inode_dirty_sync (inode);/* Don't write the inode if only I_DIRTY_PAGES was set */ if (dirty & ~I_DIRTY_PAGES ) { -int err =write_inode (inode, wbc); +int err; + +/* +* If the inode is being written due to a lazytime timestamp +* expiration, then the filesystem needs to be notified about it +* so that e.g. the filesystem can update on-disk fields and +* journal the timestamp update. Just calling write_inode()
fs/fs-writeback.c __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) · line 1530
Quoted 10 lines
fs/fs-writeback.c +7
-0+* isn't enough. Don't call mark_inode_dirty_sync(), as that +* would put the inode back on the dirty list. +*/ +if ((dirty &I_DIRTY_TIME ) && inode->i_sb->s_op->dirty_inode) + inode->i_sb->s_op->dirty_inode (inode,I_DIRTY_SYNC ); + + err =write_inode (inode, wbc);if (ret ==0 ) ret = err; }
I have to say I dislike this special call of ->dirty_inode(). It works but it makes me wonder, didn't we forget about something or won't we forget in the future? Because it's very easy to miss this special case...
I think attached patch (compile-tested only) should actually fix the problem as well without this special ->dirty_inode() call. It basically only moves the mark_inode_dirty_sync() before inode->i_state clearing. Because conceptually mark_inode_dirty_sync() is IMO the right function to call. It will take care of clearing I_DIRTY_TIME flag (because we are setting I_DIRTY_SYNC), it will also not touch inode->i_io_list if the inode is queued for sync (I_SYNC_QUEUED is set in that case). The only problem with calling it was that it was called *after* clearing dirty bits from i_state... What do you think?
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
From 80ccc6a78d1c0532f600b98884f7a64e58333485 Mon Sep 17 00:00:00 2001
From: Jan Kara <[email protected]>
Date: Thu, 7 Jan 2021 15:36:05 +0100
Subject: [PATCH] fs: Make sure inode is clean after __writeback_single_inode()
--- fs/fs-writeback.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
fs/fs-writeback.c +12 -11
@@ -1473,22 +1473,25 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) ret = err; } +/* +* If inode has dirty timestamps and we need to write them, call +* mark_inode_dirty_sync() to notify filesystem about it. +*/ +if (inode->i_state &I_DIRTY_TIME && + (wbc->for_sync || wbc->sync_mode ==WB_SYNC_ALL || +time_after (jiffies, inode->dirtied_time_when + + dirtytime_expire_interval *HZ ))) { +trace_writeback_lazytime (inode); +mark_inode_dirty_sync (inode); + } +/* * Some filesystems may redirty the inode during the writeback * due to delalloc, clear dirty metadata flags right before * write_inode() */ spin_lock (&inode->i_lock); - dirty = inode->i_state &I_DIRTY ; -if ((inode->i_state &I_DIRTY_TIME ) && - ((dirty &I_DIRTY_INODE ) || - wbc->sync_mode ==WB_SYNC_ALL || wbc->for_sync || -time_after (jiffies, inode->dirtied_time_when + - dirtytime_expire_interval *HZ ))) { - dirty |=I_DIRTY_TIME ; -trace_writeback_lazytime (inode); - } inode->i_state &= ~dirty;/*
@@ -1509,8 +1512,6 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)spin_unlock (&inode->i_lock); -if (dirty &I_DIRTY_TIME ) -mark_inode_dirty_sync (inode);/* Don't write the inode if only I_DIRTY_PAGES was set */ if (dirty & ~I_DIRTY_PAGES ) {int err =write_inode (inode, wbc);
Signature · 1 line
2.26.2
Matthew Wilcox
maintainerRe: [PATCH 01/13] fs: avoid double-writing inodes on lazytime expiration
Quoted 9 lines · Jan Kara
I think attached patch (compile-tested only) should actually fix the problem as well without this special ->dirty_inode() call. It basically only moves the
mark_inode_dirty_sync()beforeinode->i_stateclearing. Because conceptuallymark_inode_dirty_sync()is IMO the right function to call. It will take care of clearingI_DIRTY_TIMEflag (because we are settingI_DIRTY_SYNC), it will also not touchinode->i_io_listif the inode is queued for sync (I_SYNC_QUEUEDis set in that case). The only problem with calling it was that it was called *after* clearing dirty bits fromi_state... What do you think?
I like this patch far more, fwiw.
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 01/13] fs: avoid double-writing inodes on lazytime expiration
In reply to Jan KaraQuoted 100 lines · Jan Kara
Quoted 20 lines
fs/fs-writeback.c +7
-3@@ -1509,11 +1509,22 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)spin_unlock (&inode->i_lock); -if (dirty &I_DIRTY_TIME ) -mark_inode_dirty_sync (inode);/* Don't write the inode if only I_DIRTY_PAGES was set */ if (dirty & ~I_DIRTY_PAGES ) { -int err =write_inode (inode, wbc); +int err; + +/* +* If the inode is being written due to a lazytime timestamp +* expiration, then the filesystem needs to be notified about it +* so that e.g. the filesystem can update on-disk fields and +* journal the timestamp update. Just calling write_inode() fs/fs-writeback.c __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) · line 1530
Quoted 10 lines
fs/fs-writeback.c +7
-0+* isn't enough. Don't call mark_inode_dirty_sync(), as that +* would put the inode back on the dirty list. +*/ +if ((dirty &I_DIRTY_TIME ) && inode->i_sb->s_op->dirty_inode) + inode->i_sb->s_op->dirty_inode (inode,I_DIRTY_SYNC ); + + err =write_inode (inode, wbc);if (ret ==0 ) ret = err; }I have to say I dislike this special call of ->dirty_inode(). It works but it makes me wonder, didn't we forget about something or won't we forget in the future? Because it's very easy to miss this special case...
I think attached patch (compile-tested only) should actually fix the problem as well without this special ->dirty_inode() call. It basically only moves the
mark_inode_dirty_sync()beforeinode->i_stateclearing. Because conceptuallymark_inode_dirty_sync()is IMO the right function to call. It will take care of clearingI_DIRTY_TIMEflag (because we are settingI_DIRTY_SYNC), it will also not touchinode->i_io_listif the inode is queued for sync (I_SYNC_QUEUEDis set in that case). The only problem with calling it was that it was called *after* clearing dirty bits fromi_state... What do you think?Honza
Signature · 12 lines
Jan Kara <[email protected]> SUSE Labs, CR From 80ccc6a78d1c0532f600b98884f7a64e58333485 Mon Sep 17 00:00:00 2001 From: Jan Kara <[email protected]> Date: Thu, 7 Jan 2021 15:36:05 +0100 Subject: [PATCH] fs: Make sure inode is clean after __writeback_single_inode() Signed-off-by: Jan Kara <[email protected]> --- fs/fs-writeback.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)fs/fs-writeback.c +12
-9@@ -1473,22 +1473,25 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) ret = err; } +/* +* If inode has dirty timestamps and we need to write them, call +* mark_inode_dirty_sync() to notify filesystem about it. +*/ +if (inode->i_state &I_DIRTY_TIME && + (wbc->for_sync || wbc->sync_mode ==WB_SYNC_ALL || +time_after (jiffies, inode->dirtied_time_when + + dirtytime_expire_interval *HZ ))) { +trace_writeback_lazytime (inode); +mark_inode_dirty_sync (inode); + } +/* * Some filesystems may redirty the inode during the writeback * due to delalloc, clear dirty metadata flags right before * write_inode() */ spin_lock (&inode->i_lock); - dirty = inode->i_state &I_DIRTY ; -if ((inode->i_state &I_DIRTY_TIME ) && - ((dirty &I_DIRTY_INODE ) || - wbc->sync_mode ==WB_SYNC_ALL || wbc->for_sync || -time_after (jiffies, inode->dirtied_time_when + - dirtytime_expire_interval *HZ ))) { - dirty |=I_DIRTY_TIME ; -trace_writeback_lazytime (inode); - } inode->i_state &= ~dirty;/*
fs/fs-writeback.c __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) · line 1517
Quoted 8 lines
fs/fs-writeback.c +0
-2@@ -1509,8 +1512,6 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)spin_unlock (&inode->i_lock); -if (dirty &I_DIRTY_TIME ) -mark_inode_dirty_sync (inode);/* Don't write the inode if only I_DIRTY_PAGES was set */ if (dirty & ~I_DIRTY_PAGES ) {int err =write_inode (inode, wbc);
It looks like that's going to work, and it fixes the XFS bug too.
Note that if __writeback_single_inode() is called from writeback_single_inode() (rather than writeback_sb_inodes()), then the inode might not be queued for sync, in which case mark_inode_dirty_sync() will move it to a writeback list.
That's okay because afterwards, writeback_single_inode() will delete the inode from any writeback list if it's been fully cleaned, right? So clean inodes won't get left on a writeback list.
It's confusing because there are comments in writeback_single_inode() and above __writeback_single_inode() that say that the inode must not be moved between writeback lists. I take it that those comments are outdated, as they predate I_SYNC_QUEUED being introduced by commit 5afced3bf281 ("writeback: Avoid skipping inode writeback")?
- Eric
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 01/13] fs: avoid double-writing inodes on lazytime expiration
Quoted 10 lines · Eric Biggers
It looks like that's going to work, and it fixes the XFS bug too.
Note that if
__writeback_single_inode()is called fromwriteback_single_inode()(rather thanwriteback_sb_inodes()), then the inode might not be queued for sync, in which casemark_inode_dirty_sync()will move it to a writeback list.That's okay because afterwards,
writeback_single_inode()will delete the inode from any writeback list if it's been fully cleaned, right? So clean inodes won't get left on a writeback list.
Quoted 5 lines
It's confusing because there are comments in
writeback_single_inode()and above__writeback_single_inode()that say that the inode must not be moved between writeback lists. I take it that those comments are outdated, as they predateI_SYNC_QUEUEDbeing introduced by commit5afced3bf281("writeback: Avoid skipping inode writeback")?
Yes. I think we need to update the comment as well.
Re: [PATCH 01/13] fs: avoid double-writing inodes on lazytime expiration
In reply to Jan Karafs/fs-writeback.c __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) · line 1483
Quoted 8 lines
fs/fs-writeback.c +8
-0+/* +* If inode has dirty timestamps and we need to write them, call +* mark_inode_dirty_sync() to notify filesystem about it. +*/ +if (inode->i_state &I_DIRTY_TIME && + (wbc->for_sync || wbc->sync_mode ==WB_SYNC_ALL || +time_after (jiffies, inode->dirtied_time_when + + dirtytime_expire_interval *HZ ))) {
If we're touching this area, it would be nice to split this condition into a readable helper ala:
static inline bool inode_needs_timestamp_sync(struct writeback_control *wbc,
struct inode *inode)
{
if (!(inode->i_state & I_DIRTY_TIME))
return false;
if (wbc->for_sync || wbc->sync_mode == WB_SYNC_ALL)
return true;
return time_after(jiffies, inode->dirtied_time_when +
dirtytime_expire_interval * HZ);
}Re: [PATCH 01/13] fs: avoid double-writing inodes on lazytime expiration
Quoted 14 lines · Christoph Hellwig
fs/fs-writeback.c __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) · line 1483
Quoted 8 lines
fs/fs-writeback.c +8
-0+/* +* If inode has dirty timestamps and we need to write them, call +* mark_inode_dirty_sync() to notify filesystem about it. +*/ +if (inode->i_state &I_DIRTY_TIME && + (wbc->for_sync || wbc->sync_mode ==WB_SYNC_ALL || +time_after (jiffies, inode->dirtied_time_when + + dirtytime_expire_interval *HZ ))) {If we're touching this area, it would be nice to split this condition into a readable helper ala:
static inline bool inode_needs_timestamp_sync(struct
writeback_control*wbc,
struct inode *inode)
Quoted 8 lines
{
if (!(inode->i_state & I_DIRTY_TIME))
return false;
if (wbc->for_sync || wbc->sync_mode == WB_SYNC_ALL)
return true;
return time_after(jiffies, inode->dirtied_time_when +
dirtytime_expire_interval * HZ);
}I didn't end up doing this since it would only be called once, and IMO it's more readable to keep it inlined next to the comment that explains what's going on. Especially considering the later patch that drops the check for wbc->for_sync.
- Eric
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
PATCH 2/13 gfs2: don't worry about I_DIRTY_TIME in gfs2_fsync()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
The I_DIRTY_TIME flag is primary used within the VFS, and there's no reason for ->fsync() implementations to do anything with it. This is because when !datasync, the VFS will expire dirty timestamps before calling ->fsync(). (See vfs_fsync_range().) This turns I_DIRTY_TIME into I_DIRTY_SYNC.
Therefore, change gfs2_fsync() to not check for I_DIRTY_TIME.
--- fs/gfs2/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/gfs2/file.c +2 -2
@@ -749,7 +749,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, {struct address_space *mapping = file->f_mapping;struct inode *inode = mapping->host; -int sync_state = inode->i_state &I_DIRTY_ALL ; +int sync_state = inode->i_state &I_DIRTY ;struct gfs2_inode *ip =GFS2_I (inode);int ret =0 , ret1 =0 ;
@@ -762,7 +762,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end,if (!gfs2_is_jdata (ip)) sync_state &= ~I_DIRTY_PAGES ;if (datasync) - sync_state &= ~(I_DIRTY_SYNC |I_DIRTY_TIME ); + sync_state &= ~I_DIRTY_SYNC ;if (sync_state) { ret =sync_inode_metadata (inode,1 );
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 02/13] gfs2: don't worry about I_DIRTY_TIME in gfs2_fsync()
Looks good,
PATCH 3/13 fs: only specify I_DIRTY_TIME when needed in generic_update_time()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
generic_update_time() always passes I_DIRTY_TIME to __mark_inode_dirty(), which doesn't really make sense because (a) generic_update_time() might be asked to do only an i_version update, not also a timestamps update; and (b) I_DIRTY_TIME is only supposed to be set in i_state if the filesystem has lazytime enabled, so using it unconditionally in generic_update_time() is inconsistent.
As a result there is a weird edge case where if only an i_version update was requested (not also a timestamps update) but it is no longer needed (i.e. inode_maybe_inc_iversion() returns false), then I_DIRTY_TIME will be set in i_state even if the filesystem isn't mounted with lazytime.
Fix this by only passing I_DIRTY_TIME to __mark_inode_dirty() if the timestamps were updated and the filesystem has lazytime enabled.
--- fs/inode.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-)
fs/inode.c +20 -18
@@ -1743,24 +1743,26 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,int generic_update_time (struct inode *inode,struct timespec64 *time,int flags) { -int iflags =I_DIRTY_TIME ; -bool dirty = false; - -if (flags &S_ATIME ) - inode->i_atime = *time; -if (flags &S_VERSION ) - dirty =inode_maybe_inc_iversion (inode, false); -if (flags &S_CTIME ) - inode->i_ctime = *time; -if (flags &S_MTIME ) - inode->i_mtime = *time; -if ((flags & (S_ATIME |S_CTIME |S_MTIME )) && - !(inode->i_sb->s_flags &SB_LAZYTIME )) - dirty = true; - -if (dirty) - iflags |=I_DIRTY_SYNC ; -__mark_inode_dirty (inode, iflags); +int dirty_flags =0 ; + +if (flags & (S_ATIME |S_CTIME |S_MTIME )) { +if (flags &S_ATIME ) + inode->i_atime = *time; +if (flags &S_CTIME ) + inode->i_ctime = *time; +if (flags &S_MTIME ) + inode->i_mtime = *time; + +if (inode->i_sb->s_flags &SB_LAZYTIME ) + dirty_flags |=I_DIRTY_TIME ; +else + dirty_flags |=I_DIRTY_SYNC ; + } + +if ((flags &S_VERSION ) &&inode_maybe_inc_iversion (inode, false)) + dirty_flags |=I_DIRTY_SYNC ; + +__mark_inode_dirty (inode, dirty_flags);return 0 ; }EXPORT_SYMBOL (generic_update_time);
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 03/13] fs: only specify I_DIRTY_TIME when needed in generic_update_time()
Looks good,
PATCH 4/13 fat: only specify I_DIRTY_TIME when needed in fat_update_time()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
As was done for generic_update_time(), only pass I_DIRTY_TIME to __mark_inode_dirty() when the inode's timestamps were actually updated and lazytime is enabled. This avoids a weird edge case where I_DIRTY_TIME could be set in i_state when lazytime isn't enabled.
--- fs/fat/misc.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
fs/fat/misc.c +11 -10
@@ -329,21 +329,22 @@ EXPORT_SYMBOL_GPL(fat_truncate_time);int fat_update_time (struct inode *inode,struct timespec64 *now,int flags) { -int iflags =I_DIRTY_TIME ; -bool dirty = false; +int dirty_flags =0 ;if (inode->i_ino ==MSDOS_ROOT_INO )return 0 ; -fat_truncate_time (inode, now, flags); -if (flags &S_VERSION ) - dirty =inode_maybe_inc_iversion (inode, false); -if ((flags & (S_ATIME |S_CTIME |S_MTIME )) && - !(inode->i_sb->s_flags &SB_LAZYTIME )) - dirty = true; +if (flags & (S_ATIME |S_CTIME |S_MTIME )) { +fat_truncate_time (inode, now, flags); +if (inode->i_sb->s_flags &SB_LAZYTIME ) + dirty_flags |=I_DIRTY_TIME ; +else + dirty_flags |=I_DIRTY_SYNC ; + } + +if ((flags &S_VERSION ) &&inode_maybe_inc_iversion (inode, false)) + dirty_flags |=I_DIRTY_SYNC ; -if (dirty) - iflags |=I_DIRTY_SYNC ;__mark_inode_dirty (inode, iflags);return 0 ; }
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Jan Kara
maintainerRe: [PATCH 04/13] fat: only specify I_DIRTY_TIME when needed in fat_update_time()
Quoted 8 lines · Eric Biggers
From: Eric Biggers <[email protected]>
As was done for
generic_update_time(), only passI_DIRTY_TIMEto__mark_inode_dirty()when the inode's timestamps were actually updated and lazytime is enabled. This avoids a weird edge case whereI_DIRTY_TIMEcould be set ini_statewhen lazytime isn't enabled.Signed-off-by Eric Biggers <[email protected]>
...
fs/fat/misc.c EXPORT_SYMBOL_GPL(fat_truncate_time); · line 348
Quoted 6 lines
fs/fat/misc.c +2
-2+if ((flags &S_VERSION ) &&inode_maybe_inc_iversion (inode, false)) + dirty_flags |=I_DIRTY_SYNC ; -if (dirty) - iflags |=I_DIRTY_SYNC ;__mark_inode_dirty (inode, iflags);
^^^ dirty_flags here?
Otherwise the change looks good to me.
Honza
Signature · 8 lines
Jan Kara <[email protected]> SUSE Labs, CR _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 04/13] fat: only specify I_DIRTY_TIME when needed in fat_update_time()
Quoted 14 lines · Jan Kara
Quoted 8 lines · Eric Biggers
From: Eric Biggers <[email protected]>
As was done for
generic_update_time(), only passI_DIRTY_TIMEto__mark_inode_dirty()when the inode's timestamps were actually updated and lazytime is enabled. This avoids a weird edge case whereI_DIRTY_TIMEcould be set ini_statewhen lazytime isn't enabled.Signed-off-by Eric Biggers <[email protected]>...
Quoted 3 lines
fs/fat/misc.c +2
-0+if ((flags &S_VERSION ) &&inode_maybe_inc_iversion (inode, false)) + dirty_flags |=I_DIRTY_SYNC ;
Quoted 6 lines
fs/fat/misc.c EXPORT_SYMBOL_GPL(fat_truncate_time); · line 348
Quoted 3 lines
fs/fat/misc.c +0
-2-if (dirty) - iflags |=I_DIRTY_SYNC ;__mark_inode_dirty (inode, iflags);^^^
dirty_flagshere?Otherwise the change looks good to me.
Yeah, I'll fix that. I accidentally didn't have CONFIG_FAT_FS enabled.
- Eric
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
PATCH 5/13 fs: don't call ->dirty_inode for lazytime timestamp updates
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
There is no need to call ->dirty_inode for lazytime timestamp updates (i.e. for __mark_inode_dirty(I_DIRTY_TIME)), since by the definition of lazytime, filesystems must ignore these updates. Filesystems only need to care about the updated timestamps when they expire.
Therefore, only call ->dirty_inode when I_DIRTY_INODE is set.
Based on a patch from Christoph Hellwig:
https://lore.kernel.org/r/[email protected]
--- fs/ext4/inode.c | 12 +----------- fs/f2fs/super.c | 3 --- fs/fs-writeback.c | 6 +++--- fs/gfs2/super.c | 2 -- 4 files changed, 4 insertions(+), 19 deletions(-)
fs/ext4/inode.c +1 -11
@@ -5933,26 +5933,16 @@ int __ext4_mark_inode_dirty(handle_t *handle, struct inode *inode,* If the inode is marked synchronous, we don't honour that here - doing * so would cause a commit on atime updates, which we don't bother doing. * We handle synchronous inodes at the highest possible level. -* -* If only the I_DIRTY_TIME flag is set, we can skip everything. If -* I_DIRTY_TIME and I_DIRTY_SYNC is set, the only inode fields we need -* to copy into the on-disk inode structure are the timestamp files. */ void ext4_dirty_inode (struct inode *inode,int flags) {handle_t *handle; -if (flags ==I_DIRTY_TIME ) -return ; handle =ext4_journal_start (inode,EXT4_HT_INODE ,2 );if (IS_ERR (handle)) - goto out; - +return ;ext4_mark_inode_dirty (handle, inode); -ext4_journal_stop (handle); -out: -return ; }int ext4_change_inode_journal_flag (struct inode *inode,int val)
fs/f2fs/super.c +0 -3
@@ -1196,9 +1196,6 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) inode->i_ino == F2FS_META_INO(sbi))return ; -if (flags ==I_DIRTY_TIME ) -return ; -if (is_inode_flag_set (inode,FI_AUTO_RECOVER ))clear_inode_flag (inode,FI_AUTO_RECOVER );
fs/fs-writeback.c +3 -3
@@ -2264,16 +2264,16 @@ void __mark_inode_dirty(struct inode *inode, int flags)* Don't do this for I_DIRTY_PAGES - that doesn't actually * dirty the inode itself */ -if (flags & (I_DIRTY_INODE |I_DIRTY_TIME )) { +if (flags &I_DIRTY_INODE ) {trace_writeback_dirty_inode_start (inode, flags);if (sb->s_op->dirty_inode) sb->s_op->dirty_inode (inode, flags);trace_writeback_dirty_inode (inode, flags); - } -if (flags &I_DIRTY_INODE ) + flags &= ~I_DIRTY_TIME ; + } dirtytime = flags &I_DIRTY_TIME ;/*
fs/gfs2/super.c +0 -2
@@ -562,8 +562,6 @@ static void gfs2_dirty_inode(struct inode *inode, int flags)int need_endtrans =0 ;int ret; -if (!(flags &I_DIRTY_INODE )) -return ;if (unlikely (gfs2_withdrawn (sdp)))return ;if (!gfs2_glock_is_locked_by_me (ip->i_gl)) {
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Jan Kara
maintainerRe: [PATCH 05/13] fs: don't call ->dirty_inode for lazytime timestamp updates
Quoted 13 lines · Eric Biggers
From: Eric Biggers <[email protected]>
There is no need to call ->dirty_inode for lazytime timestamp updates (i.e. for
__mark_inode_dirty(I_DIRTY_TIME)), since by the definition of lazytime, filesystems must ignore these updates. Filesystems only need to care about the updated timestamps when they expire.Therefore, only call ->dirty_inode when
I_DIRTY_INODEis set.Based on a patch from Christoph Hellwig:
https://lore.kernel.org/r/[email protected]Signed-off-by Eric Biggers <[email protected]>
...
Quoted 5 lines
fs/fs-writeback.c +0
-0
fs/fs-writeback.c void __mark_inode_dirty(struct inode *inode, int flags) · line 2271
Quoted 9 lines
fs/fs-writeback.c +1
-1@@ -2264,16 +2264,16 @@ void __mark_inode_dirty(struct inode *inode, int flags)* Don't do this for I_DIRTY_PAGES - that doesn't actually * dirty the inode itself */ -if (flags & (I_DIRTY_INODE |I_DIRTY_TIME )) { +if (flags &I_DIRTY_INODE ) {trace_writeback_dirty_inode_start (inode, flags);if (sb->s_op->dirty_inode) sb->s_op->dirty_inode (inode, flags);
OK, but shouldn't we pass just (flags & I_DIRTY_INODE) to ->dirty_inode(). Just to make it clear what the filesystem is supposed to consume in 'flags'...
Honza
Signature · 8 lines
Jan Kara <[email protected]> SUSE Labs, CR _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Jan Kara
maintainerRe: [PATCH 05/13] fs: don't call ->dirty_inode for lazytime timestamp updates
Quoted 29 lines · Jan Kara
Quoted 13 lines · Eric Biggers
From: Eric Biggers <[email protected]>
There is no need to call ->dirty_inode for lazytime timestamp updates (i.e. for
__mark_inode_dirty(I_DIRTY_TIME)), since by the definition of lazytime, filesystems must ignore these updates. Filesystems only need to care about the updated timestamps when they expire.Therefore, only call ->dirty_inode when
I_DIRTY_INODEis set.Based on a patch from Christoph Hellwig:
https://lore.kernel.org/r/[email protected]Signed-off-by Eric Biggers <[email protected]>...
Quoted 12 lines
fs/fs-writeback.c +1
-1@@ -2264,16 +2264,16 @@ void __mark_inode_dirty(struct inode *inode, int flags)* Don't do this for I_DIRTY_PAGES - that doesn't actually * dirty the inode itself */ -if (flags & (I_DIRTY_INODE |I_DIRTY_TIME )) { +if (flags &I_DIRTY_INODE ) {trace_writeback_dirty_inode_start (inode, flags);
Quoted 6 lines
fs/fs-writeback.c void __mark_inode_dirty(struct inode *inode, int flags) · line 2271
Quoted 2 lines
fs/fs-writeback.c +0
-0if (sb->s_op->dirty_inode) sb->s_op->dirty_inode (inode, flags);OK, but shouldn't we pass just (flags &
I_DIRTY_INODE) to ->dirty_inode(). Just to make it clear what the filesystem is supposed to consume in 'flags'...
Aha, you just did that in the following patch ;) So taking back my comment.
Honza
Signature · 8 lines
Jan Kara <[email protected]> SUSE Labs, CR _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 05/13] fs: don't call ->dirty_inode for lazytime timestamp updates
In reply to Eric BiggersLooks good,
PATCH 6/13 fs: pass only I_DIRTY_INODE flags to ->dirty_inode
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
->dirty_inode is now only called when I_DIRTY_INODE (I_DIRTY_SYNC and/or I_DIRTY_DATASYNC) is set. However it may still be passed other dirty flags at the same time, provided that these other flags happened to be passed to __mark_inode_dirty() at the same time as I_DIRTY_INODE.
This doesn't make sense because there is no reason for filesystems to care about these extra flags. Nor are filesystems notified about all updates to these other flags.
Therefore, mask the flags before passing them to ->dirty_inode.
Also properly document ->dirty_inode in vfs.rst.
--- Documentation/filesystems/vfs.rst | 5 ++++- fs/fs-writeback.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-)
Documentation/filesystems/vfs.rst +4 -1
@@ -270,7 +270,10 @@ or bottom half). ->alloc_inode. ``dirty_inode`` - this method is called by the VFS to mark an inode dirty. + this method is called by the VFS when an inode is marked dirty. + This is specifically for the inode itself being marked dirty, + not its data. If the update needs to be persisted by fdatasync(), + then I_DIRTY_DATASYNC will be set in the flags argument. ``write_inode`` this method is called when the VFS needs to write an inode to
fs/fs-writeback.c +1 -1
@@ -2268,7 +2268,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)trace_writeback_dirty_inode_start (inode, flags);if (sb->s_op->dirty_inode) - sb->s_op->dirty_inode (inode, flags); + sb->s_op->dirty_inode (inode, flags &I_DIRTY_INODE );trace_writeback_dirty_inode (inode, flags);
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 06/13] fs: pass only I_DIRTY_INODE flags to ->dirty_inode
Looks good,
PATCH 7/13 fs: correctly document the inode dirty flags
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
The documentation for I_DIRTY_SYNC and I_DIRTY_DATASYNC is a bit misleading, and I_DIRTY_TIME isn't documented at all. Fix this.
--- include/linux/fs.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
include/linux/fs.h +13 -5
@@ -2084,8 +2084,8 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,/* * Inode state bits. Protected by inode->i_lock * -* Three bits determine the dirty state of the inode, I_DIRTY_SYNC, -* I_DIRTY_DATASYNC and I_DIRTY_PAGES. +* Four bits determine the dirty state of the inode: I_DIRTY_SYNC, +* I_DIRTY_DATASYNC, I_DIRTY_PAGES, and I_DIRTY_TIME. * * Four bits define the lifetime of an inode. Initially, inodes are I_NEW, * until that flag is cleared. I_WILL_FREE, I_FREEING and I_CLEAR are set at
@@ -2094,12 +2094,20 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,* Two bits are used for locking and completion notification, I_NEW and I_SYNC. * * I_DIRTY_SYNC Inode is dirty, but doesn't have to be written on -* fdatasync(). i_atime is the usual cause. -* I_DIRTY_DATASYNC Data-related inode changes pending. We keep track of +* fdatasync() (unless I_DIRTY_DATASYNC is also set). +* Timestamp updates are the usual cause. +* I_DIRTY_DATASYNC Data-related inode changes pending. We keep track of * these changes separately from I_DIRTY_SYNC so that we * don't have to write inode on fdatasync() when only -* mtime has changed in it. +* e.g. the timestamps have changed. * I_DIRTY_PAGES Inode has dirty pages. Inode itself may be clean. +* I_DIRTY_TIME The inode itself only has dirty timestamps, and the +* lazytime mount option is enabled. We keep track of this +* separately from I_DIRTY_SYNC in order to implement +* lazytime. This gets cleared if I_DIRTY_INODE +* (I_DIRTY_SYNC and/or I_DIRTY_DATASYNC) gets set. I.e. +* either I_DIRTY_TIME *or* I_DIRTY_INODE can be set in +* i_state, but not both. I_DIRTY_PAGES may still be set. * I_NEW Serves as both a mutex and completion notification. * New inodes set I_NEW. If two processes both create * the same inode, one of them will release its inode and
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 07/13] fs: correctly document the inode dirty flags
Looks good,
PATCH 8/13 ext4: simplify i_state checks in __ext4_update_other_inode_time()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Since I_DIRTY_TIME and I_DIRTY_INODE are mutually exclusive in i_state, there's no need to check for I_DIRTY_TIME && !I_DIRTY_INODE. Just check for I_DIRTY_TIME.
--- fs/ext4/inode.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
fs/ext4/inode.c +3 -5
@@ -4962,14 +4962,12 @@ static void __ext4_update_other_inode_time(struct super_block *sb,return ;if ((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | -I_DIRTY_INODE )) || - ((inode->i_state &I_DIRTY_TIME ) ==0 )) +I_DIRTY_TIME )) !=I_DIRTY_TIME )return ;spin_lock (&inode->i_lock); -if (((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | -I_DIRTY_INODE )) ==0 ) && - (inode->i_state &I_DIRTY_TIME )) { +if ((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | +I_DIRTY_TIME )) !=I_DIRTY_TIME ) {struct ext4_inode_info *ei =EXT4_I (inode); inode->i_state &= ~I_DIRTY_TIME ;
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Jan Kara
maintainerRe: [PATCH 08/13] ext4: simplify i_state checks in __ext4_update_other_inode_time()
Quoted 16 lines · Eric Biggers
From: Eric Biggers <[email protected]>
Since
I_DIRTY_TIMEandI_DIRTY_INODEare mutually exclusive ini_state, there's no need to check forI_DIRTY_TIME&& !I_DIRTY_INODE. Just check forI_DIRTY_TIME.Signed-off-by Eric Biggers <[email protected]>--- fs/ext4/inode.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)fs/ext4/inode.c +0
-0
fs/ext4/inode.c static void __ext4_update_other_inode_time(struct super_block *sb, · line 4966
Quoted 7 lines
fs/ext4/inode.c +1
-2@@ -4962,14 +4962,12 @@ static void __ext4_update_other_inode_time(struct super_block *sb,return ;if ((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | -I_DIRTY_INODE )) || - ((inode->i_state &I_DIRTY_TIME ) ==0 )) +I_DIRTY_TIME )) !=I_DIRTY_TIME )return ;
This is OK.
fs/ext4/inode.c static void __ext4_update_other_inode_time(struct super_block *sb, · line 4970
Quoted 6 lines
fs/ext4/inode.c +2
-3spin_lock (&inode->i_lock); -if (((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | -I_DIRTY_INODE )) ==0 ) && - (inode->i_state &I_DIRTY_TIME )) { +if ((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | +I_DIRTY_TIME )) !=I_DIRTY_TIME ) {
But this condition is negated AFAICT. We should have == I_DIRTY_TIME here AFAICT.
Honza
Signature · 8 lines
Jan Kara <[email protected]> SUSE Labs, CR _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 08/13] ext4: simplify i_state checks in __ext4_update_other_inode_time()
Quoted 27 lines · Jan Kara
Quoted 16 lines · Eric Biggers
From: Eric Biggers <[email protected]>
Since
I_DIRTY_TIMEandI_DIRTY_INODEare mutually exclusive ini_state, there's no need to check forI_DIRTY_TIME&& !I_DIRTY_INODE. Just check forI_DIRTY_TIME.Signed-off-by Eric Biggers <[email protected]>--- fs/ext4/inode.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)fs/ext4/inode.c +0
-0fs/ext4/inode.c static void __ext4_update_other_inode_time(struct super_block *sb, · line 4966
Quoted 7 lines
fs/ext4/inode.c +1
-2@@ -4962,14 +4962,12 @@ static void __ext4_update_other_inode_time(struct super_block *sb,return ;if ((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | -I_DIRTY_INODE )) || - ((inode->i_state &I_DIRTY_TIME ) ==0 )) +I_DIRTY_TIME )) !=I_DIRTY_TIME )return ;This is OK.
Quoted 9 lines
fs/ext4/inode.c static void __ext4_update_other_inode_time(struct super_block *sb, · line 4970
Quoted 6 lines
fs/ext4/inode.c +2
-3spin_lock (&inode->i_lock); -if (((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | -I_DIRTY_INODE )) ==0 ) && - (inode->i_state &I_DIRTY_TIME )) { +if ((inode->i_state & (I_FREEING |I_WILL_FREE |I_NEW | +I_DIRTY_TIME )) !=I_DIRTY_TIME ) {But this condition is negated AFAICT. We should have ==
I_DIRTY_TIMEhere AFAICT.
Indeed, I'll fix that. Thanks for catching this!
- Eric
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
PATCH 9/13 fs: drop redundant checks from __writeback_single_inode()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
I_DIRTY_TIME and I_DIRTY_INODE are mutually exclusive in i_state. So after seeing that I_DIRTY_TIME is set, there's no point in checking for I_DIRTY_INODE, as it must be clear.
Separately, wbc->for_sync implies wbc->sync_mode == WB_SYNC_ALL. So there's no need to check for both. Just check for WB_SYNC_ALL.
--- fs/fs-writeback.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
fs/fs-writeback.c +1 -2
@@ -1482,8 +1482,7 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) dirty = inode->i_state &I_DIRTY ;if ((inode->i_state &I_DIRTY_TIME ) && - ((dirty &I_DIRTY_INODE ) || - wbc->sync_mode ==WB_SYNC_ALL || wbc->for_sync || + (wbc->sync_mode ==WB_SYNC_ALL ||time_after (jiffies, inode->dirtied_time_when + dirtytime_expire_interval *HZ ))) { dirty |=I_DIRTY_TIME ;
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 09/13] fs: drop redundant checks from __writeback_single_inode()
Looks good, but will change a bit with Jan's patch as the base.
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
PATCH 10/13 fs: clean up __mark_inode_dirty() a bit
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Improve some comments, and don't bother checking for the I_DIRTY_TIME flag in the case where we just cleared it.
Also, warn if I_DIRTY_TIME and I_DIRTY_PAGES are passed to __mark_inode_dirty() at the same time, as this case isn't handled.
--- fs/fs-writeback.c | 49 +++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 19 deletions(-)
fs/fs-writeback.c +30 -19
@@ -2227,23 +2227,24 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode) }/** -* __mark_inode_dirty - internal function +* __mark_inode_dirty - internal function to mark an inode dirty * * @inode: inode to mark -* @flags: what kind of dirty (i.e. I_DIRTY_SYNC) +* @flags: what kind of dirty, e.g. I_DIRTY_SYNC. This can be a combination of +* multiple I_DIRTY_* flags, except that I_DIRTY_TIME can't be combined +* with I_DIRTY_PAGES. * -* Mark an inode as dirty. Callers should use mark_inode_dirty or -* mark_inode_dirty_sync. +* Mark an inode as dirty. We notify the filesystem, then update the inode's +* dirty flags. Then, if needed we add the inode to the appropriate dirty list. * -* Put the inode on the super block's dirty list. +* Most callers should use mark_inode_dirty() or mark_inode_dirty_sync() +* instead of calling this directly. * -* CAREFUL! We mark it dirty unconditionally, but move it onto the -* dirty list only if it is hashed or if it refers to a blockdev. -* If it was not hashed, it will never be added to the dirty list -* even if it is later hashed, as it will have been marked dirty already. +* CAREFUL! We only add the inode to the dirty list if it is hashed or if it +* refers to a blockdev. Unhashed inodes will never be added to the dirty list +* even if they are later hashed, as they will have been marked dirty already. * -* In short, make sure you hash any inodes _before_ you start marking -* them dirty. +* In short, ensure you hash any inodes _before_ you start marking them dirty. * * Note that for blockdevs, inode->dirtied_when represents the dirtying time of * the block-special inode (/dev/hda1) itself. And the ->dirtied_when field of
@@ -2255,25 +2256,34 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode)void __mark_inode_dirty (struct inode *inode,int flags) {struct super_block *sb = inode->i_sb; -int dirtytime; +int dirtytime =0 ;trace_writeback_mark_inode_dirty (inode, flags); -/* -* Don't do this for I_DIRTY_PAGES - that doesn't actually -* dirty the inode itself -*/ if (flags &I_DIRTY_INODE ) { +/* +* Notify the filesystem about the inode being dirtied, so that +* (if needed) it can update on-disk fields and journal the +* inode. This is only needed when the inode itself is being +* dirtied now. I.e. it's only needed for I_DIRTY_INODE, not +* for just I_DIRTY_PAGES or I_DIRTY_TIME. +*/ trace_writeback_dirty_inode_start (inode, flags); -if (sb->s_op->dirty_inode) sb->s_op->dirty_inode (inode, flags &I_DIRTY_INODE ); -trace_writeback_dirty_inode (inode, flags); +/* I_DIRTY_INODE supersedes I_DIRTY_TIME. */ flags &= ~I_DIRTY_TIME ; + }else { +/* +* Else it's either I_DIRTY_PAGES, I_DIRTY_TIME, or nothing. +* (We don't support setting both I_DIRTY_PAGES and I_DIRTY_TIME +* in one call to __mark_inode_dirty().) +*/ + dirtytime = flags &I_DIRTY_TIME ; +WARN_ON_ONCE (dirtytime && (flags !=I_DIRTY_TIME )); } - dirtytime = flags &I_DIRTY_TIME ;/* * Paired with smp_mb() in __writeback_single_inode() for the
@@ -2296,6 +2306,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)inode_attach_wb (inode,NULL ); +/* I_DIRTY_INODE supersedes I_DIRTY_TIME. */ if (flags &I_DIRTY_INODE ) inode->i_state &= ~I_DIRTY_TIME ; inode->i_state |= flags;
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 10/13] fs: clean up __mark_inode_dirty() a bit
fs/fs-writeback.c static noinline void block_dump___mark_inode_dirty(struct inode *inode) · line 2285
Quoted 8 lines · Eric Biggers
fs/fs-writeback.c +8
-0+ }else { +/* +* Else it's either I_DIRTY_PAGES, I_DIRTY_TIME, or nothing. +* (We don't support setting both I_DIRTY_PAGES and I_DIRTY_TIME +* in one call to __mark_inode_dirty().) +*/ + dirtytime = flags &I_DIRTY_TIME ; +WARN_ON_ONCE (dirtytime && (flags !=I_DIRTY_TIME ));
No need for the inner braces here.
Otherwise looks good:
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
PATCH 11/13 fs: add a lazytime_expired method
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Add a lazytime_expired method to 'struct super_operations'. Filesystems can implement this to be notified when an inode's lazytime timestamps have expired and need to be written to disk.
This avoids any potential ambiguity with ->dirty_inode(inode, I_DIRTY_SYNC), which can also mean a generic dirtying of the inode, not just a lazytime timestamp expiration. In particular, this will be useful for XFS.
If not implemented, then ->dirty_inode(inode, I_DIRTY_SYNC) continues to be called.
Note that there are three cases where we have to make sure to call lazytime_expired():
- __writeback_single_inode(): inode is being written now
- vfs_fsync_range(): inode is going to be synced
- iput(): inode is going to be evicted
In the latter two cases, the inode still needs to be put on the writeback list. So, we can't just replace the calls to mark_inode_dirty_sync() with lazytime_expired(). Instead, add a new flag I_DIRTY_TIME_EXPIRED which can be passed to __mark_inode_dirty(). It's like I_DIRTY_SYNC, except it causes the filesystem to be notified of a lazytime expiration rather than a generic I_DIRTY_SYNC.
--- Documentation/filesystems/locking.rst | 2 ++ Documentation/filesystems/vfs.rst | 10 ++++++++++ fs/fs-writeback.c | 27 ++++++++++++++++++++++----- fs/inode.c | 2 +- fs/sync.c | 2 +- include/linux/fs.h | 7 ++++++- 6 files changed, 42 insertions(+), 8 deletions(-)
Documentation/filesystems/locking.rst +2 -0
@@ -150,6 +150,7 @@ prototypes:: void (*free_inode)(struct inode *); void (*destroy_inode)(struct inode *); void (*dirty_inode) (struct inode *, int flags); + void (*lazytime_expired) (struct inode *); int (*write_inode) (struct inode *, struct writeback_control *wbc); int (*drop_inode) (struct inode *); void (*evict_inode) (struct inode *);
@@ -175,6 +176,7 @@ alloc_inode: free_inode: called from RCU callback destroy_inode: dirty_inode: +lazytime_expired: write_inode: drop_inode: !!!inode->i_lock!!! evict_inode:
Documentation/filesystems/vfs.rst +10 -0
@@ -231,6 +231,7 @@ filesystem. As of kernel 2.6.22, the following members are defined: void (*destroy_inode)(struct inode *); void (*dirty_inode) (struct inode *, int flags); + void (*lazytime_expired) (struct inode *); int (*write_inode) (struct inode *, int); void (*drop_inode) (struct inode *); void (*delete_inode) (struct inode *);
@@ -275,6 +276,15 @@ or bottom half). not its data. If the update needs to be persisted by fdatasync(), then I_DIRTY_DATASYNC will be set in the flags argument. +``lazytime_expired`` + when the lazytime mount option is enabled, this method is + called when an inode's in-memory updated timestamps have + expired and thus need to be written to disk. This happens + when the timestamps have been in memory for too long, when the + inode is going to be evicted, or when userspace triggers a + sync. If this method is not implemented, then + ->dirty_inode(inode, I_DIRTY_SYNC) is called instead. + ``write_inode`` this method is called when the VFS needs to write an inode to disc. The second parameter indicates whether the write should
fs/fs-writeback.c +22 -5
@@ -1441,6 +1441,14 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, } } +static void lazytime_expired (struct inode *inode) +{ +if (inode->i_sb->s_op->lazytime_expired) + inode->i_sb->s_op->lazytime_expired (inode); +else if (inode->i_sb->s_op->dirty_inode) + inode->i_sb->s_op->dirty_inode (inode,I_DIRTY_SYNC ); +} +/* * Write out an inode and its dirty pages. Do not update the writeback list * linkage. That is left to the caller. The caller is also responsible for
@@ -1520,8 +1528,8 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)* isn't enough. Don't call mark_inode_dirty_sync(), as that * would put the inode back on the dirty list. */ -if ((dirty &I_DIRTY_TIME ) && inode->i_sb->s_op->dirty_inode) - inode->i_sb->s_op->dirty_inode (inode,I_DIRTY_SYNC ); +if (dirty &I_DIRTY_TIME ) +lazytime_expired (inode); err =write_inode (inode, wbc);if (ret ==0 )
@@ -2231,8 +2239,9 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode)* * @inode: inode to mark * @flags: what kind of dirty, e.g. I_DIRTY_SYNC. This can be a combination of -* multiple I_DIRTY_* flags, except that I_DIRTY_TIME can't be combined -* with I_DIRTY_PAGES. +* multiple I_DIRTY_* flags, except that: +* - I_DIRTY_TIME can't be combined with I_DIRTY_PAGES +* - I_DIRTY_TIME_EXPIRED must be used by itself * * Mark an inode as dirty. We notify the filesystem, then update the inode's * dirty flags. Then, if needed we add the inode to the appropriate dirty list.
@@ -2260,7 +2269,15 @@ void __mark_inode_dirty(struct inode *inode, int flags)trace_writeback_mark_inode_dirty (inode, flags); -if (flags &I_DIRTY_INODE ) { +if (flags &I_DIRTY_TIME_EXPIRED ) { +/* +* Notify the filesystem about a lazytime timestamp expiration. +* Afterwards, this case just turns into I_DIRTY_SYNC. +*/ +WARN_ON_ONCE (flags & ~I_DIRTY_TIME_EXPIRED ); +lazytime_expired (inode); + flags =I_DIRTY_SYNC ; + }else if (flags &I_DIRTY_INODE ) {/* * Notify the filesystem about the inode being dirtied, so that * (if needed) it can update on-disk fields and journal the
fs/inode.c +1 -1
@@ -1673,7 +1673,7 @@ void iput(struct inode *inode)atomic_inc (&inode->i_count);spin_unlock (&inode->i_lock);trace_writeback_lazytime_iput (inode); -mark_inode_dirty_sync (inode); +__mark_inode_dirty (inode,I_DIRTY_TIME_EXPIRED ); goto retry; }iput_final (inode);
fs/sync.c +1 -1
@@ -196,7 +196,7 @@ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)if (!file->f_op->fsync)return -EINVAL ;if (!datasync && (inode->i_state &I_DIRTY_TIME )) -mark_inode_dirty_sync (inode); +__mark_inode_dirty (inode,I_DIRTY_TIME_EXPIRED );return file->f_op->fsync (file, start, end, datasync); }EXPORT_SYMBOL (vfs_fsync_range);
include/linux/fs.h +6 -1
@@ -1935,7 +1935,8 @@ struct super_operations {void (*destroy_inode)(struct inode *);void (*free_inode)(struct inode *); -void (*dirty_inode) (struct inode *,int flags); +void (*dirty_inode) (struct inode *,int flags); +void (*lazytime_expired)(struct inode *);int (*write_inode) (struct inode *,struct writeback_control *wbc);int (*drop_inode) (struct inode *);void (*evict_inode) (struct inode *);
@@ -2108,6 +2109,9 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,* (I_DIRTY_SYNC and/or I_DIRTY_DATASYNC) gets set. I.e. * either I_DIRTY_TIME *or* I_DIRTY_INODE can be set in * i_state, but not both. I_DIRTY_PAGES may still be set. +* I_DIRTY_TIME_EXPIRED Passed to __mark_inode_dirty() to indicate the intent to +* expire the inode's timestamps. Not stored in i_state. +* * I_NEW Serves as both a mutex and completion notification. * New inodes set I_NEW. If two processes both create * the same inode, one of them will release its inode and
@@ -2173,6 +2177,7 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,#define I_DIO_WAKEUP (1 << __I_DIO_WAKEUP)#define I_LINKABLE (1 << 10)#define I_DIRTY_TIME (1 << 11) +#define I_DIRTY_TIME_EXPIRED (1 << 12)#define I_WB_SWITCH (1 << 13)#define I_OVL_INUSE (1 << 14)#define I_CREATING (1 << 15)
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Jan Kara
maintainerRe: [PATCH 11/13] fs: add a lazytime_expired method
Quoted 21 lines · Eric Biggers
From: Eric Biggers <[email protected]>
Add a
lazytime_expiredmethod to 'structsuper_operations'. Filesystems can implement this to be notified when an inode's lazytime timestamps have expired and need to be written to disk.This avoids any potential ambiguity with ->dirty_inode(inode,
I_DIRTY_SYNC), which can also mean a generic dirtying of the inode, not just a lazytime timestamp expiration. In particular, this will be useful for XFS.If not implemented, then ->dirty_inode(inode,
I_DIRTY_SYNC) continues to be called.Note that there are three cases where we have to make sure to call
lazytime_expired():-
__writeback_single_inode(): inode is being written now
-vfs_fsync_range(): inode is going to be synced
-iput(): inode is going to be evicted
Quoted 8 lines
In the latter two cases, the inode still needs to be put on the writeback list. So, we can't just replace the calls to
mark_inode_dirty_sync()withlazytime_expired(). Instead, add a new flagI_DIRTY_TIME_EXPIREDwhich can be passed to__mark_inode_dirty(). It's likeI_DIRTY_SYNC, except it causes the filesystem to be notified of a lazytime expiration rather than a genericI_DIRTY_SYNC.Signed-off-by Eric Biggers <[email protected]>
Hum, seeing this patch I kind of wonder: Why don't we dirty the inode after expiring the lazytime timestamps with I_DIRTY_SYNC | I_DIRTY_TIME_EXPIRED and propagate I_DIRTY_TIME_EXPIRED even to ->dirty_inode() where XFS can catch it and act? Functionally it would be the same but we'd save a bunch of generic code and ->lazytime_expired helper used just by a single filesystem...
Honza
Quoted 177 lines
--- Documentation/filesystems/locking.rst | 2 ++ Documentation/filesystems/vfs.rst | 10 ++++++++++ fs/fs-writeback.c | 27 ++++++++++++++++++++++----- fs/inode.c | 2 +- fs/sync.c | 2 +- include/linux/fs.h | 7 ++++++- 6 files changed, 42 insertions(+), 8 deletions(-)Documentation/filesystems/locking.rst +2
-0@@ -150,6 +150,7 @@ prototypes:: void (*free_inode)(struct inode *); void (*destroy_inode)(struct inode *); void (*dirty_inode) (struct inode *, int flags); + void (*lazytime_expired) (struct inode *); int (*write_inode) (struct inode *, struct writeback_control *wbc); int (*drop_inode) (struct inode *); void (*evict_inode) (struct inode *);@@ -175,6 +176,7 @@ alloc_inode: free_inode: called from RCU callback destroy_inode: dirty_inode: +lazytime_expired: write_inode: drop_inode: !!!inode->i_lock!!! evict_inode:Documentation/filesystems/vfs.rst +10
-0@@ -231,6 +231,7 @@ filesystem. As of kernel 2.6.22, the following members are defined: void (*destroy_inode)(struct inode *); void (*dirty_inode) (struct inode *, int flags); + void (*lazytime_expired) (struct inode *); int (*write_inode) (struct inode *, int); void (*drop_inode) (struct inode *); void (*delete_inode) (struct inode *);@@ -275,6 +276,15 @@ or bottom half). not its data. If the update needs to be persisted by fdatasync(), then I_DIRTY_DATASYNC will be set in the flags argument. +``lazytime_expired`` + when the lazytime mount option is enabled, this method is + called when an inode's in-memory updated timestamps have + expired and thus need to be written to disk. This happens + when the timestamps have been in memory for too long, when the + inode is going to be evicted, or when userspace triggers a + sync. If this method is not implemented, then + ->dirty_inode(inode, I_DIRTY_SYNC) is called instead. + ``write_inode`` this method is called when the VFS needs to write an inode to disc. The second parameter indicates whether the write shouldfs/fs-writeback.c +22
-5@@ -1441,6 +1441,14 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, } } +static void lazytime_expired (struct inode *inode) +{ +if (inode->i_sb->s_op->lazytime_expired) + inode->i_sb->s_op->lazytime_expired (inode); +else if (inode->i_sb->s_op->dirty_inode) + inode->i_sb->s_op->dirty_inode (inode,I_DIRTY_SYNC ); +} +/* * Write out an inode and its dirty pages. Do not update the writeback list * linkage. That is left to the caller. The caller is also responsible for @@ -1520,8 +1528,8 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)* isn't enough. Don't call mark_inode_dirty_sync(), as that * would put the inode back on the dirty list. */ -if ((dirty &I_DIRTY_TIME ) && inode->i_sb->s_op->dirty_inode) - inode->i_sb->s_op->dirty_inode (inode,I_DIRTY_SYNC ); +if (dirty &I_DIRTY_TIME ) +lazytime_expired (inode); err =write_inode (inode, wbc);if (ret ==0 )@@ -2231,8 +2239,9 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode)* * @inode: inode to mark * @flags: what kind of dirty, e.g. I_DIRTY_SYNC. This can be a combination of -* multiple I_DIRTY_* flags, except that I_DIRTY_TIME can't be combined -* with I_DIRTY_PAGES. +* multiple I_DIRTY_* flags, except that: +* - I_DIRTY_TIME can't be combined with I_DIRTY_PAGES +* - I_DIRTY_TIME_EXPIRED must be used by itself * * Mark an inode as dirty. We notify the filesystem, then update the inode's * dirty flags. Then, if needed we add the inode to the appropriate dirty list. @@ -2260,7 +2269,15 @@ void __mark_inode_dirty(struct inode *inode, int flags)trace_writeback_mark_inode_dirty (inode, flags); -if (flags &I_DIRTY_INODE ) { +if (flags &I_DIRTY_TIME_EXPIRED ) { +/* +* Notify the filesystem about a lazytime timestamp expiration. +* Afterwards, this case just turns into I_DIRTY_SYNC. +*/ +WARN_ON_ONCE (flags & ~I_DIRTY_TIME_EXPIRED ); +lazytime_expired (inode); + flags =I_DIRTY_SYNC ; + }else if (flags &I_DIRTY_INODE ) {/* * Notify the filesystem about the inode being dirtied, so that * (if needed) it can update on-disk fields and journal the fs/inode.c +1
-1@@ -1673,7 +1673,7 @@ void iput(struct inode *inode)atomic_inc (&inode->i_count);spin_unlock (&inode->i_lock);trace_writeback_lazytime_iput (inode); -mark_inode_dirty_sync (inode); +__mark_inode_dirty (inode,I_DIRTY_TIME_EXPIRED ); goto retry; }iput_final (inode);fs/sync.c +1
-1@@ -196,7 +196,7 @@ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)if (!file->f_op->fsync)return -EINVAL ;if (!datasync && (inode->i_state &I_DIRTY_TIME )) -mark_inode_dirty_sync (inode); +__mark_inode_dirty (inode,I_DIRTY_TIME_EXPIRED );return file->f_op->fsync (file, start, end, datasync); }EXPORT_SYMBOL (vfs_fsync_range);include/linux/fs.h +6
-1@@ -1935,7 +1935,8 @@ struct super_operations {void (*destroy_inode)(struct inode *);void (*free_inode)(struct inode *); -void (*dirty_inode) (struct inode *,int flags); +void (*dirty_inode) (struct inode *,int flags); +void (*lazytime_expired)(struct inode *);int (*write_inode) (struct inode *,struct writeback_control *wbc);int (*drop_inode) (struct inode *);void (*evict_inode) (struct inode *);@@ -2108,6 +2109,9 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,* (I_DIRTY_SYNC and/or I_DIRTY_DATASYNC) gets set. I.e. * either I_DIRTY_TIME *or* I_DIRTY_INODE can be set in * i_state, but not both. I_DIRTY_PAGES may still be set. +* I_DIRTY_TIME_EXPIRED Passed to __mark_inode_dirty() to indicate the intent to +* expire the inode's timestamps. Not stored in i_state. +* * I_NEW Serves as both a mutex and completion notification. * New inodes set I_NEW. If two processes both create * the same inode, one of them will release its inode and @@ -2173,6 +2177,7 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,#define I_DIO_WAKEUP (1 << __I_DIO_WAKEUP)#define I_LINKABLE (1 << 10)#define I_DIRTY_TIME (1 << 11) +#define I_DIRTY_TIME_EXPIRED (1 << 12)#define I_WB_SWITCH (1 << 13)#define I_OVL_INUSE (1 << 14)#define I_CREATING (1 << 15)Signature · 1 line
2.30.0
Signature · 8 lines
Jan Kara <[email protected]> SUSE Labs, CR _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 11/13] fs: add a lazytime_expired method
Quoted 29 lines · Jan Kara
Quoted 28 lines · Eric Biggers
From: Eric Biggers <[email protected]>
Add a
lazytime_expiredmethod to 'structsuper_operations'. Filesystems can implement this to be notified when an inode's lazytime timestamps have expired and need to be written to disk.This avoids any potential ambiguity with ->dirty_inode(inode,
I_DIRTY_SYNC), which can also mean a generic dirtying of the inode, not just a lazytime timestamp expiration. In particular, this will be useful for XFS.If not implemented, then ->dirty_inode(inode,
I_DIRTY_SYNC) continues to be called.Note that there are three cases where we have to make sure to call
lazytime_expired():-
__writeback_single_inode(): inode is being written now
-vfs_fsync_range(): inode is going to be synced
-iput(): inode is going to be evictedIn the latter two cases, the inode still needs to be put on the writeback list. So, we can't just replace the calls to
mark_inode_dirty_sync()withlazytime_expired(). Instead, add a new flagI_DIRTY_TIME_EXPIREDwhich can be passed to__mark_inode_dirty(). It's likeI_DIRTY_SYNC, except it causes the filesystem to be notified of a lazytime expiration rather than a genericI_DIRTY_SYNC.
Quoted 9 lines
Quoted 1 line
Signed-off-by Eric Biggers <[email protected]>Hum, seeing this patch I kind of wonder: Why don't we dirty the inode after expiring the lazytime timestamps with
I_DIRTY_SYNC|I_DIRTY_TIME_EXPIREDand propagateI_DIRTY_TIME_EXPIREDeven to ->dirty_inode() where XFS can catch it and act? Functionally it would be the same but we'd save a bunch of generic code and ->lazytime_expired helper used just by a single filesystem...
Yes, that would be equivalent to what this patch does.
Either way, note that if we also use your suggestion for patch #1, then that already fixes the XFS bug, since i_state will start containing I_DIRTY_TIME when ->dirty_inode(I_DIRTY_SYNC) is called. So xfs_fs_dirty_inode() will start working as intended.
That makes introducing ->lazytime_expired (or equivalently I_DIRTY_TIME_EXPIRED) kind of useless since it wouldn't actually fix anything.
So I'm tempted to just drop it.
The XFS developers might have a different opinion though, as they were the ones who requested it originally:
https://lore.kernel.org/r/[email protected] https://lore.kernel.org/r/[email protected] https://lore.kernel.org/r/20200325154759.GY29339@magnolia https://lore.kernel.org/r/[email protected]
Any thoughts from anyone about whether we should still introduce a separate notification for lazytime expiration, vs. just using ->dirty_inode(I_DIRTY_SYNC) with I_DIRTY_TIME in i_state?
- Eric
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 11/13] fs: add a lazytime_expired method
Quoted 11 lines · Eric Biggers
The XFS developers might have a different opinion though, as they were the ones who requested it originally:
https://lore.kernel.org/r/[email protected] https://lore.kernel.org/r/[email protected] https://lore.kernel.org/r/20200325154759.GY29339@magnolia https://lore.kernel.org/r/[email protected]Any thoughts from anyone about whether we should still introduce a separate notification for lazytime expiration, vs. just using ->dirty_inode(I_DIRTY_SYNC) with
I_DIRTY_TIMEini_state?
I still find the way ->dirty_inode is used very confusing, but with this series and Jan's first patch I think we have a good enough state for now and don't need to add a method just for XFS. I still think it might make sense to eventually revisit how file systems are notified about dirtying.
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
PATCH 12/13 xfs: remove a stale comment from xfs_file_aio_write_checks()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
The comment in xfs_file_aio_write_checks() about calling file_modified() after dropping the ilock doesn't make sense, because the code that unconditionally acquires and drops the ilock was removed by commit 467f78992a07 ("xfs: reduce ilock hold times in xfs_file_aio_write_checks").
Remove this outdated comment.
--- fs/xfs/xfs_file.c | 6 ------ 1 file changed, 6 deletions(-)
fs/xfs/xfs_file.c +0 -6
@@ -389,12 +389,6 @@ xfs_file_aio_write_checks( }else spin_unlock (&ip->i_flags_lock); -/* -* Updating the timestamps will grab the ilock again from -* xfs_fs_dirty_inode, so we have to call it after dropping the -* lock above. Eventually we should look into a way to avoid -* the pointless lock roundtrip. -*/ return file_modified (file); }
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Re: [PATCH 12/13] xfs: remove a stale comment from xfs_file_aio_write_checks()
Quoted 9 lines · Eric Biggers
From: Eric Biggers <[email protected]>
The comment in
xfs_file_aio_write_checks()about callingfile_modified()after dropping the ilock doesn't make sense, because the code that unconditionally acquires and drops the ilock was removed by commit467f78992a07("xfs: reduce ilock hold times inxfs_file_aio_write_checks").Remove this outdated comment.
Yes, this looks good, I actually have the removal included in a WIP patch as well, but splitting it out like this look good to me:
_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
PATCH 13/13 xfs: implement lazytime_expired
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Implement the new ->lazytime_expired method to get notified of lazytime timestamp expirations, instead of relying on ->dirty_inode(inode, I_DIRTY_SYNC) which is potentially ambiguous.
This fixes a bug where XFS didn't write lazytime timestamps to disk upon a sync(), or after 24 hours (dirtytime_expire_interval * 2). This is because it only wrote the timestamps if I_DIRTY_TIME was set in i_state. But actually when an inode's timestamps expire without the inode being marked I_DIRTY_SYNC first, then ->dirty_inode isn't called until __writeback_single_inode() has already cleared I_DIRTY_TIME in i_state.
The new ->lazytime_expired method is unambiguous, so it removes any need to check for I_DIRTY_TIME, which avoids this bug.
I've written an xfstest which reproduces this bug.
--- fs/xfs/xfs_super.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-)
fs/xfs/xfs_super.c +3 -9
@@ -666,19 +666,13 @@ xfs_fs_destroy_inode( }static void -xfs_fs_dirty_inode ( -struct inode *inode, -int flag) +xfs_fs_lazytime_expired ( +struct inode *inode) {struct xfs_inode *ip =XFS_I (inode);struct xfs_mount *mp = ip->i_mount;struct xfs_trans *tp; -if (!(inode->i_sb->s_flags &SB_LAZYTIME )) -return ; -if (flag !=I_DIRTY_SYNC || !(inode->i_state &I_DIRTY_TIME )) -return ; -if (xfs_trans_alloc (mp, &M_RES (mp)->tr_fsyncts,0 ,0 ,0 , &tp))return ;xfs_ilock (ip,XFS_ILOCK_EXCL );
@@ -1108,7 +1102,7 @@ xfs_fs_free_cached_objects(static const struct super_operations xfs_super_operations = { .alloc_inode = xfs_fs_alloc_inode, .destroy_inode = xfs_fs_destroy_inode, - .dirty_inode = xfs_fs_dirty_inode, + .lazytime_expired = xfs_fs_lazytime_expired, .drop_inode = xfs_fs_drop_inode, .put_super = xfs_fs_put_super, .sync_fs = xfs_fs_sync_fs,
Signature · 8 lines
2.30.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel