summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 22:25:01 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 22:25:01 +0300
commit74554251dfc9374ebf1a9dfc54d6745d56bb9265 (patch)
tree3b3968d3fbadc6f690d91a2ce7ce20c85f9b568f
parent996812c453cafa042f2e674738dbf8fa495661f3 (diff)
parent77ef2c3ff5916d358c436911ca6a961060709f04 (diff)
downloadlinux-74554251dfc9374ebf1a9dfc54d6745d56bb9265.tar.xz
Merge tag 'vfs-7.0-rc1.nonblocking_timestamps' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull vfs timestamp updates from Christian Brauner: "This contains the changes to support non-blocking timestamp updates. Since commit 66fa3cedf16a ("fs: Add async write file modification handling") file_update_time_flags() unconditionally returns -EAGAIN when any timestamp needs updating and IOCB_NOWAIT is set. This makes non-blocking direct writes impossible on file systems with granular enough timestamps, which in practice means all of them. This reworks the timestamp update path to propagate IOCB_NOWAIT through ->update_time so that file systems which can update timestamps without blocking are no longer penalized. With that groundwork in place, the core change passes IOCB_NOWAIT into ->update_time and returns -EAGAIN only when the file system indicates it would block. XFS implements non-blocking timestamp updates by using the new ->sync_lazytime and open-coding generic_update_time without the S_NOWAIT check, since the lazytime path through the generic helpers can never block in XFS" * tag 'vfs-7.0-rc1.nonblocking_timestamps' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: xfs: enable non-blocking timestamp updates xfs: implement ->sync_lazytime fs: refactor file_update_time_flags fs: add support for non-blocking timestamp updates fs: add a ->sync_lazytime method fs: factor out a sync_lazytime helper fs: refactor ->update_time handling fat: cleanup the flags for fat_truncate_time nfs: split nfs_update_timestamps fs: allow error returns from generic_update_time fs: remove inode_update_time
-rw-r--r--Documentation/filesystems/locking.rst5
-rw-r--r--Documentation/filesystems/vfs.rst9
-rw-r--r--fs/bad_inode.c3
-rw-r--r--fs/btrfs/inode.c13
-rw-r--r--fs/fat/dir.c2
-rw-r--r--fs/fat/fat.h11
-rw-r--r--fs/fat/file.c14
-rw-r--r--fs/fat/inode.c2
-rw-r--r--fs/fat/misc.c59
-rw-r--r--fs/fat/namei_msdos.c13
-rw-r--r--fs/fat/namei_vfat.c9
-rw-r--r--fs/fs-writeback.c33
-rw-r--r--fs/gfs2/inode.c9
-rw-r--r--fs/inode.c202
-rw-r--r--fs/internal.h3
-rw-r--r--fs/nfs/inode.c37
-rw-r--r--fs/orangefs/inode.c29
-rw-r--r--fs/orangefs/orangefs-kernel.h3
-rw-r--r--fs/overlayfs/inode.c7
-rw-r--r--fs/overlayfs/overlayfs.h3
-rw-r--r--fs/sync.c4
-rw-r--r--fs/ubifs/file.c28
-rw-r--r--fs/ubifs/ubifs.h3
-rw-r--r--fs/xfs/xfs_iops.c49
-rw-r--r--fs/xfs/xfs_super.c29
-rw-r--r--include/linux/fs.h30
-rw-r--r--include/trace/events/writeback.h6
27 files changed, 325 insertions, 290 deletions
diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst
index 04c7691e50e0..3837891e933d 100644
--- a/Documentation/filesystems/locking.rst
+++ b/Documentation/filesystems/locking.rst
@@ -80,7 +80,9 @@ prototypes::
int (*getattr) (struct mnt_idmap *, const struct path *, struct kstat *, u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
- void (*update_time)(struct inode *, struct timespec *, int);
+ void (*update_time)(struct inode *inode, enum fs_update_time type,
+ int flags);
+ void (*sync_lazytime)(struct inode *inode);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode);
@@ -117,6 +119,7 @@ getattr: no
listxattr: no
fiemap: no
update_time: no
+sync_lazytime: no
atomic_open: shared (exclusive if O_CREAT is set in open flags)
tmpfile: no
fileattr_get: no or exclusive
diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index 670ba66b60e4..d8cb181f69f8 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -485,7 +485,9 @@ As of kernel 2.6.22, the following members are defined:
int (*setattr) (struct mnt_idmap *, struct dentry *, struct iattr *);
int (*getattr) (struct mnt_idmap *, const struct path *, struct kstat *, u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
- void (*update_time)(struct inode *, struct timespec *, int);
+ void (*update_time)(struct inode *inode, enum fs_update_time type,
+ int flags);
+ void (*sync_lazytime)(struct inode *inode);
int (*atomic_open)(struct inode *, struct dentry *, struct file *,
unsigned open_flag, umode_t create_mode);
int (*tmpfile) (struct mnt_idmap *, struct inode *, struct file *, umode_t);
@@ -642,6 +644,11 @@ otherwise noted.
an inode. If this is not defined the VFS will update the inode
itself and call mark_inode_dirty_sync.
+``sync_lazytime``:
+ called by the writeback code to update the lazy time stamps to
+ regular time stamp updates that get syncing into the on-disk
+ inode.
+
``atomic_open``
called on the last component of an open. Using this optional
method the filesystem can look up, possibly create and open the
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index 0ef9bcb744dd..acf8613f5e36 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -133,7 +133,8 @@ static int bad_inode_fiemap(struct inode *inode,
return -EIO;
}
-static int bad_inode_update_time(struct inode *inode, int flags)
+static int bad_inode_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
return -EIO;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index a2b5b440637e..2a2101cf7677 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6386,16 +6386,21 @@ static int btrfs_dirty_inode(struct btrfs_inode *inode)
* We need our own ->update_time so that we can return error on ENOSPC for
* updating the inode in the case of file write and mmap writes.
*/
-static int btrfs_update_time(struct inode *inode, int flags)
+static int btrfs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
- bool dirty;
+ int dirty;
if (btrfs_root_readonly(root))
return -EROFS;
+ if (flags & IOCB_NOWAIT)
+ return -EAGAIN;
- dirty = inode_update_timestamps(inode, flags);
- return dirty ? btrfs_dirty_inode(BTRFS_I(inode)) : 0;
+ dirty = inode_update_time(inode, type, flags);
+ if (dirty <= 0)
+ return dirty;
+ return btrfs_dirty_inode(BTRFS_I(inode));
}
/*
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 92b091783966..3d03bff40944 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -1080,7 +1080,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)
}
}
- fat_truncate_time(dir, NULL, S_ATIME|S_MTIME);
+ fat_truncate_time(dir, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
if (IS_DIRSYNC(dir))
(void)fat_sync_inode(dir);
else
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index d3e426de5f01..0d269dba897b 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -468,11 +468,12 @@ extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
__le16 *time, __le16 *date, u8 *time_cs);
extern struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi,
const struct timespec64 *ts);
-extern struct timespec64 fat_truncate_mtime(const struct msdos_sb_info *sbi,
- const struct timespec64 *ts);
-extern int fat_truncate_time(struct inode *inode, struct timespec64 *now,
- int flags);
-extern int fat_update_time(struct inode *inode, int flags);
+#define FAT_UPDATE_ATIME (1u << 0)
+#define FAT_UPDATE_CMTIME (1u << 1)
+void fat_truncate_time(struct inode *inode, struct timespec64 *now,
+ unsigned int flags);
+int fat_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs);
int fat_cache_init(void);
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 4fc49a614fb8..f9bc93411aa2 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -224,7 +224,7 @@ static int fat_cont_expand(struct inode *inode, loff_t size)
if (err)
goto out;
- fat_truncate_time(inode, NULL, S_CTIME|S_MTIME);
+ fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME);
mark_inode_dirty(inode);
if (IS_SYNC(inode)) {
int err2;
@@ -327,7 +327,7 @@ static int fat_free(struct inode *inode, int skip)
MSDOS_I(inode)->i_logstart = 0;
}
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
- fat_truncate_time(inode, NULL, S_CTIME|S_MTIME);
+ fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME);
if (wait) {
err = fat_sync_inode(inode);
if (err) {
@@ -553,15 +553,13 @@ int fat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
}
/*
- * setattr_copy can't truncate these appropriately, so we'll
- * copy them ourselves
+ * setattr_copy can't truncate these appropriately, so we'll copy them
+ * ourselves. See fat_truncate_time for the c/mtime logic on fat.
*/
if (attr->ia_valid & ATTR_ATIME)
- fat_truncate_time(inode, &attr->ia_atime, S_ATIME);
- if (attr->ia_valid & ATTR_CTIME)
- fat_truncate_time(inode, &attr->ia_ctime, S_CTIME);
+ fat_truncate_time(inode, &attr->ia_atime, FAT_UPDATE_ATIME);
if (attr->ia_valid & ATTR_MTIME)
- fat_truncate_time(inode, &attr->ia_mtime, S_MTIME);
+ fat_truncate_time(inode, &attr->ia_mtime, FAT_UPDATE_CMTIME);
attr->ia_valid &= ~(ATTR_ATIME|ATTR_CTIME|ATTR_MTIME);
setattr_copy(idmap, inode, attr);
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 0b6009cd1844..59fa90617b5b 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -246,7 +246,7 @@ static int fat_write_end(const struct kiocb *iocb,
if (err < len)
fat_write_failed(mapping, pos + len);
if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) {
- fat_truncate_time(inode, NULL, S_CTIME|S_MTIME);
+ fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME);
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
mark_inode_dirty(inode);
}
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 950da09f0961..b154a5162764 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -299,62 +299,47 @@ struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi,
}
/*
- * truncate mtime to 2 second granularity
- */
-struct timespec64 fat_truncate_mtime(const struct msdos_sb_info *sbi,
- const struct timespec64 *ts)
-{
- return fat_timespec64_trunc_2secs(*ts);
-}
-
-/*
- * truncate the various times with appropriate granularity:
- * all times in root node are always 0
+ * Update the in-inode atime and/or mtime after truncating the timestamp to the
+ * granularity. All timestamps in root inode are always 0.
+ *
+ * ctime and mtime share the same on-disk field, and should be identical in
+ * memory. All mtime updates will be applied to ctime, but ctime updates are
+ * ignored.
*/
-int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags)
+void fat_truncate_time(struct inode *inode, struct timespec64 *now,
+ unsigned int flags)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
struct timespec64 ts;
if (inode->i_ino == MSDOS_ROOT_INO)
- return 0;
+ return;
if (now == NULL) {
now = &ts;
ts = current_time(inode);
}
- if (flags & S_ATIME)
+ if (flags & FAT_UPDATE_ATIME)
inode_set_atime_to_ts(inode, fat_truncate_atime(sbi, now));
- /*
- * ctime and mtime share the same on-disk field, and should be
- * identical in memory. all mtime updates will be applied to ctime,
- * but ctime updates are ignored.
- */
- if (flags & S_MTIME)
- inode_set_mtime_to_ts(inode,
- inode_set_ctime_to_ts(inode, fat_truncate_mtime(sbi, now)));
+ if (flags & FAT_UPDATE_CMTIME) {
+ /* truncate mtime to 2 second granularity */
+ struct timespec64 mtime = fat_timespec64_trunc_2secs(*now);
- return 0;
+ inode_set_mtime_to_ts(inode, mtime);
+ inode_set_ctime_to_ts(inode, mtime);
+ }
}
EXPORT_SYMBOL_GPL(fat_truncate_time);
-int fat_update_time(struct inode *inode, int flags)
+int fat_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- int dirty_flags = 0;
-
- if (inode->i_ino == MSDOS_ROOT_INO)
- return 0;
-
- if (flags & (S_ATIME | S_CTIME | S_MTIME)) {
- fat_truncate_time(inode, NULL, flags);
- if (inode->i_sb->s_flags & SB_LAZYTIME)
- dirty_flags |= I_DIRTY_TIME;
- else
- dirty_flags |= I_DIRTY_SYNC;
+ if (inode->i_ino != MSDOS_ROOT_INO) {
+ fat_truncate_time(inode, NULL, type == FS_UPD_ATIME ?
+ FAT_UPDATE_ATIME : FAT_UPDATE_CMTIME);
+ __mark_inode_dirty(inode, inode_time_dirty_flag(inode));
}
-
- __mark_inode_dirty(inode, dirty_flags);
return 0;
}
EXPORT_SYMBOL_GPL(fat_update_time);
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
index 0b920ee40a7f..ba0152ed0810 100644
--- a/fs/fat/namei_msdos.c
+++ b/fs/fat/namei_msdos.c
@@ -251,7 +251,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name,
if (err)
return err;
- fat_truncate_time(dir, ts, S_CTIME|S_MTIME);
+ fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
if (IS_DIRSYNC(dir))
(void)fat_sync_inode(dir);
else
@@ -295,7 +295,7 @@ static int msdos_create(struct mnt_idmap *idmap, struct inode *dir,
err = PTR_ERR(inode);
goto out;
}
- fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME);
+ fat_truncate_time(inode, &ts, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
d_instantiate(dentry, inode);
@@ -328,7 +328,6 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
drop_nlink(dir);
clear_nlink(inode);
- fat_truncate_time(inode, NULL, S_CTIME);
fat_detach(inode);
out:
mutex_unlock(&MSDOS_SB(sb)->s_lock);
@@ -382,7 +381,7 @@ static struct dentry *msdos_mkdir(struct mnt_idmap *idmap, struct inode *dir,
goto out;
}
set_nlink(inode, 2);
- fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME);
+ fat_truncate_time(inode, &ts, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
d_instantiate(dentry, inode);
@@ -415,7 +414,6 @@ static int msdos_unlink(struct inode *dir, struct dentry *dentry)
if (err)
goto out;
clear_nlink(inode);
- fat_truncate_time(inode, NULL, S_CTIME);
fat_detach(inode);
out:
mutex_unlock(&MSDOS_SB(sb)->s_lock);
@@ -480,7 +478,7 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
mark_inode_dirty(old_inode);
inode_inc_iversion(old_dir);
- fat_truncate_time(old_dir, NULL, S_CTIME|S_MTIME);
+ fat_truncate_time(old_dir, NULL, FAT_UPDATE_CMTIME);
if (IS_DIRSYNC(old_dir))
(void)fat_sync_inode(old_dir);
else
@@ -540,7 +538,7 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
if (err)
goto error_dotdot;
inode_inc_iversion(old_dir);
- fat_truncate_time(old_dir, &ts, S_CTIME|S_MTIME);
+ fat_truncate_time(old_dir, &ts, FAT_UPDATE_CMTIME);
if (IS_DIRSYNC(old_dir))
(void)fat_sync_inode(old_dir);
else
@@ -550,7 +548,6 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
drop_nlink(new_inode);
if (is_dir)
drop_nlink(new_inode);
- fat_truncate_time(new_inode, &ts, S_CTIME);
}
out:
brelse(sinfo.bh);
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index 5dbc4cbb8fce..e46f34cade1a 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -676,7 +676,7 @@ static int vfat_add_entry(struct inode *dir, const struct qstr *qname,
goto cleanup;
/* update timestamp */
- fat_truncate_time(dir, ts, S_CTIME|S_MTIME);
+ fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
if (IS_DIRSYNC(dir))
(void)fat_sync_inode(dir);
else
@@ -806,7 +806,7 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
drop_nlink(dir);
clear_nlink(inode);
- fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
+ fat_truncate_time(inode, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
fat_detach(inode);
vfat_d_version_set(dentry, inode_query_iversion(dir));
out:
@@ -832,7 +832,7 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry)
if (err)
goto out;
clear_nlink(inode);
- fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
+ fat_truncate_time(inode, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
fat_detach(inode);
vfat_d_version_set(dentry, inode_query_iversion(dir));
out:
@@ -918,7 +918,7 @@ static int vfat_update_dotdot_de(struct inode *dir, struct inode *inode,
static void vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts)
{
inode_inc_iversion(dir);
- fat_truncate_time(dir, ts, S_CTIME | S_MTIME);
+ fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
if (IS_DIRSYNC(dir))
(void)fat_sync_inode(dir);
else
@@ -996,7 +996,6 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
drop_nlink(new_inode);
if (is_dir)
drop_nlink(new_inode);
- fat_truncate_time(new_inode, &ts, S_CTIME);
}
out:
brelse(sinfo.bh);
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 5444fc706ac7..bdeee3893470 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -1711,6 +1711,19 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
}
}
+bool sync_lazytime(struct inode *inode)
+{
+ if (!(inode_state_read_once(inode) & I_DIRTY_TIME))
+ return false;
+
+ trace_writeback_lazytime(inode);
+ if (inode->i_op->sync_lazytime)
+ inode->i_op->sync_lazytime(inode);
+ else
+ mark_inode_dirty_sync(inode);
+ return true;
+}
+
/*
* Write out an inode and its dirty pages (or some of its dirty pages, depending
* on @wbc->nr_to_write), and clear the relevant dirty flags from i_state.
@@ -1750,17 +1763,15 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
}
/*
- * If the inode has dirty timestamps and we need to write them, call
- * mark_inode_dirty_sync() to notify the filesystem about it and to
- * change I_DIRTY_TIME into I_DIRTY_SYNC.
+ * For data integrity writeback, or when the dirty interval expired,
+ * ask the file system to propagata lazy timestamp updates into real
+ * dirty state.
*/
if ((inode_state_read_once(inode) & I_DIRTY_TIME) &&
(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);
- }
+ dirtytime_expire_interval * HZ)))
+ sync_lazytime(inode);
/*
* Get and clear the dirty flags from i_state. This needs to be done
@@ -2569,6 +2580,8 @@ void __mark_inode_dirty(struct inode *inode, int flags)
trace_writeback_mark_inode_dirty(inode, flags);
if (flags & I_DIRTY_INODE) {
+ bool was_dirty_time = false;
+
/*
* Inode timestamp update will piggback on this dirtying.
* We tell ->dirty_inode callback that timestamps need to
@@ -2579,6 +2592,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
if (inode_state_read(inode) & I_DIRTY_TIME) {
inode_state_clear(inode, I_DIRTY_TIME);
flags |= I_DIRTY_TIME;
+ was_dirty_time = true;
}
spin_unlock(&inode->i_lock);
}
@@ -2591,9 +2605,12 @@ void __mark_inode_dirty(struct inode *inode, int flags)
* for just I_DIRTY_PAGES or I_DIRTY_TIME.
*/
trace_writeback_dirty_inode_start(inode, flags);
- if (sb->s_op->dirty_inode)
+ if (sb->s_op->dirty_inode) {
sb->s_op->dirty_inode(inode,
flags & (I_DIRTY_INODE | I_DIRTY_TIME));
+ } else if (was_dirty_time && inode->i_op->sync_lazytime) {
+ inode->i_op->sync_lazytime(inode);
+ }
trace_writeback_dirty_inode(inode, flags);
/* I_DIRTY_INODE supersedes I_DIRTY_TIME. */
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 36618e353199..c02ebf0ca625 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -2242,13 +2242,17 @@ loff_t gfs2_seek_hole(struct file *file, loff_t offset)
return vfs_setpos(file, ret, inode->i_sb->s_maxbytes);
}
-static int gfs2_update_time(struct inode *inode, int flags)
+static int gfs2_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_glock *gl = ip->i_gl;
struct gfs2_holder *gh;
int error;
+ if (flags & IOCB_NOWAIT)
+ return -EAGAIN;
+
gh = gfs2_glock_is_locked_by_me(gl);
if (gh && gl->gl_state != LM_ST_EXCLUSIVE) {
gfs2_glock_dq(gh);
@@ -2257,8 +2261,7 @@ static int gfs2_update_time(struct inode *inode, int flags)
if (error)
return error;
}
- generic_update_time(inode, flags);
- return 0;
+ return generic_update_time(inode, type, flags);
}
static const struct inode_operations gfs2_file_iops = {
diff --git a/fs/inode.c b/fs/inode.c
index 379f4c19845c..1d0474745e77 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1982,11 +1982,8 @@ retry:
if (atomic_add_unless(&inode->i_count, -1, 1))
return;
- if ((inode_state_read_once(inode) & I_DIRTY_TIME) && inode->i_nlink) {
- trace_writeback_lazytime_iput(inode);
- mark_inode_dirty_sync(inode);
+ if (inode->i_nlink && sync_lazytime(inode))
goto retry;
- }
spin_lock(&inode->i_lock);
if (unlikely((inode_state_read(inode) & I_DIRTY_TIME) && inode->i_nlink)) {
@@ -2084,94 +2081,112 @@ static bool relatime_need_update(struct vfsmount *mnt, struct inode *inode,
return false;
}
-/**
- * inode_update_timestamps - update the timestamps on the inode
- * @inode: inode to be updated
- * @flags: S_* flags that needed to be updated
- *
- * The update_time function is called when an inode's timestamps need to be
- * updated for a read or write operation. This function handles updating the
- * actual timestamps. It's up to the caller to ensure that the inode is marked
- * dirty appropriately.
- *
- * In the case where any of S_MTIME, S_CTIME, or S_VERSION need to be updated,
- * attempt to update all three of them. S_ATIME updates can be handled
- * independently of the rest.
- *
- * Returns a set of S_* flags indicating which values changed.
- */
-int inode_update_timestamps(struct inode *inode, int flags)
+static int inode_update_atime(struct inode *inode)
{
- int updated = 0;
- struct timespec64 now;
+ struct timespec64 atime = inode_get_atime(inode);
+ struct timespec64 now = current_time(inode);
- if (flags & (S_MTIME|S_CTIME|S_VERSION)) {
- struct timespec64 ctime = inode_get_ctime(inode);
- struct timespec64 mtime = inode_get_mtime(inode);
+ if (timespec64_equal(&now, &atime))
+ return 0;
- now = inode_set_ctime_current(inode);
- if (!timespec64_equal(&now, &ctime))
- updated |= S_CTIME;
- if (!timespec64_equal(&now, &mtime)) {
- inode_set_mtime_to_ts(inode, now);
- updated |= S_MTIME;
- }
- if (IS_I_VERSION(inode) && inode_maybe_inc_iversion(inode, updated))
- updated |= S_VERSION;
- } else {
- now = current_time(inode);
- }
+ inode_set_atime_to_ts(inode, now);
+ return inode_time_dirty_flag(inode);
+}
+
+static int inode_update_cmtime(struct inode *inode, unsigned int flags)
+{
+ struct timespec64 ctime = inode_get_ctime(inode);
+ struct timespec64 mtime = inode_get_mtime(inode);
+ struct timespec64 now = inode_set_ctime_current(inode);
+ unsigned int dirty = 0;
+ bool mtime_changed;
- if (flags & S_ATIME) {
- struct timespec64 atime = inode_get_atime(inode);
+ mtime_changed = !timespec64_equal(&now, &mtime);
+ if (mtime_changed || !timespec64_equal(&now, &ctime))
+ dirty = inode_time_dirty_flag(inode);
- if (!timespec64_equal(&now, &atime)) {
- inode_set_atime_to_ts(inode, now);
- updated |= S_ATIME;
+ /*
+ * Pure timestamp updates can be recorded in the inode without blocking
+ * by not dirtying the inode. But when the file system requires
+ * i_version updates, the update of i_version can still block.
+ * Error out if we'd actually have to update i_version or don't support
+ * lazytime.
+ */
+ if (IS_I_VERSION(inode)) {
+ if (flags & IOCB_NOWAIT) {
+ if (!(inode->i_sb->s_flags & SB_LAZYTIME) ||
+ inode_iversion_need_inc(inode))
+ return -EAGAIN;
+ } else {
+ if (inode_maybe_inc_iversion(inode, !!dirty))
+ dirty |= I_DIRTY_SYNC;
}
}
- return updated;
+
+ if (mtime_changed)
+ inode_set_mtime_to_ts(inode, now);
+ return dirty;
}
-EXPORT_SYMBOL(inode_update_timestamps);
/**
- * generic_update_time - update the timestamps on the inode
+ * inode_update_time - update either atime or c/mtime and i_version on the inode
* @inode: inode to be updated
- * @flags: S_* flags that needed to be updated
+ * @type: timestamp to be updated
+ * @flags: flags for the update
*
- * The update_time function is called when an inode's timestamps need to be
- * updated for a read or write operation. In the case where any of S_MTIME, S_CTIME,
- * or S_VERSION need to be updated we attempt to update all three of them. S_ATIME
- * updates can be handled done independently of the rest.
+ * Update either atime or c/mtime and version in a inode if needed for a file
+ * access or modification. It is up to the caller to mark the inode dirty
+ * appropriately.
*
- * Returns a S_* mask indicating which fields were updated.
+ * Returns the positive I_DIRTY_* flags for __mark_inode_dirty() if the inode
+ * needs to be marked dirty, 0 if it did not, or a negative errno if an error
+ * happened.
*/
-int generic_update_time(struct inode *inode, int flags)
+int inode_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- int updated = inode_update_timestamps(inode, flags);
- int dirty_flags = 0;
-
- if (updated & (S_ATIME|S_MTIME|S_CTIME))
- dirty_flags = inode->i_sb->s_flags & SB_LAZYTIME ? I_DIRTY_TIME : I_DIRTY_SYNC;
- if (updated & S_VERSION)
- dirty_flags |= I_DIRTY_SYNC;
- __mark_inode_dirty(inode, dirty_flags);
- return updated;
+ switch (type) {
+ case FS_UPD_ATIME:
+ return inode_update_atime(inode);
+ case FS_UPD_CMTIME:
+ return inode_update_cmtime(inode, flags);
+ default:
+ WARN_ON_ONCE(1);
+ return -EIO;
+ }
}
-EXPORT_SYMBOL(generic_update_time);
+EXPORT_SYMBOL(inode_update_time);
-/*
- * This does the actual work of updating an inodes time or version. Must have
- * had called mnt_want_write() before calling this.
+/**
+ * generic_update_time - update the timestamps on the inode
+ * @inode: inode to be updated
+ * @type: timestamp to be updated
+ * @flags: flags for the update
+ *
+ * Returns a negative error value on error, else 0.
*/
-int inode_update_time(struct inode *inode, int flags)
+int generic_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- if (inode->i_op->update_time)
- return inode->i_op->update_time(inode, flags);
- generic_update_time(inode, flags);
+ int dirty;
+
+ /*
+ * ->dirty_inode is what could make generic timestamp updates block.
+ * Don't support non-blocking timestamp updates here if it is set.
+ * File systems that implement ->dirty_inode but want to support
+ * non-blocking timestamp updates should call inode_update_time
+ * directly.
+ */
+ if ((flags & IOCB_NOWAIT) && inode->i_sb->s_op->dirty_inode)
+ return -EAGAIN;
+
+ dirty = inode_update_time(inode, type, flags);
+ if (dirty <= 0)
+ return dirty;
+ __mark_inode_dirty(inode, dirty);
return 0;
}
-EXPORT_SYMBOL(inode_update_time);
+EXPORT_SYMBOL(generic_update_time);
/**
* atime_needs_update - update the access time
@@ -2240,7 +2255,10 @@ void touch_atime(const struct path *path)
* We may also fail on filesystems that have the ability to make parts
* of the fs read only, e.g. subvolumes in Btrfs.
*/
- inode_update_time(inode, S_ATIME);
+ if (inode->i_op->update_time)
+ inode->i_op->update_time(inode, FS_UPD_ATIME, 0);
+ else
+ generic_update_time(inode, FS_UPD_ATIME, 0);
mnt_put_write_access(mnt);
skip_update:
sb_end_write(inode->i_sb);
@@ -2363,39 +2381,39 @@ out:
}
EXPORT_SYMBOL(current_time);
+static inline bool need_cmtime_update(struct inode *inode)
+{
+ struct timespec64 now = current_time(inode), ts;
+
+ ts = inode_get_mtime(inode);
+ if (!timespec64_equal(&ts, &now))
+ return true;
+ ts = inode_get_ctime(inode);
+ if (!timespec64_equal(&ts, &now))
+ return true;
+ return IS_I_VERSION(inode) && inode_iversion_need_inc(inode);
+}
+
static int file_update_time_flags(struct file *file, unsigned int flags)
{
struct inode *inode = file_inode(file);
- struct timespec64 now, ts;
- int sync_mode = 0;
- int ret = 0;
+ int ret;
/* First try to exhaust all avenues to not sync */
if (IS_NOCMTIME(inode))
return 0;
if (unlikely(file->f_mode & FMODE_NOCMTIME))
return 0;
-
- now = current_time(inode);
-
- ts = inode_get_mtime(inode);
- if (!timespec64_equal(&ts, &now))
- sync_mode |= S_MTIME;
- ts = inode_get_ctime(inode);
- if (!timespec64_equal(&ts, &now))
- sync_mode |= S_CTIME;
- if (IS_I_VERSION(inode) && inode_iversion_need_inc(inode))
- sync_mode |= S_VERSION;
-
- if (!sync_mode)
+ if (!need_cmtime_update(inode))
return 0;
- if (flags & IOCB_NOWAIT)
- return -EAGAIN;
-
+ flags &= IOCB_NOWAIT;
if (mnt_get_write_access_file(file))
return 0;
- ret = inode_update_time(inode, sync_mode);
+ if (inode->i_op->update_time)
+ ret = inode->i_op->update_time(inode, FS_UPD_CMTIME, flags);
+ else
+ ret = generic_update_time(inode, FS_UPD_CMTIME, flags);
mnt_put_write_access_file(file);
return ret;
}
diff --git a/fs/internal.h b/fs/internal.h
index ab638d41ab81..18a062c1b5b0 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -214,7 +214,8 @@ bool in_group_or_capable(struct mnt_idmap *idmap,
/*
* fs-writeback.c
*/
-extern long get_nr_dirty_inodes(void);
+long get_nr_dirty_inodes(void);
+bool sync_lazytime(struct inode *inode);
/*
* dcache.c
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index de2cce1d08f4..331cdecdd966 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -649,15 +649,15 @@ static void nfs_set_timestamps_to_ts(struct inode *inode, struct iattr *attr)
struct timespec64 ctime = inode_get_ctime(inode);
struct timespec64 mtime = inode_get_mtime(inode);
struct timespec64 now;
- int updated = 0;
+ bool updated = false;
now = inode_set_ctime_current(inode);
if (!timespec64_equal(&now, &ctime))
- updated |= S_CTIME;
+ updated = true;
inode_set_mtime_to_ts(inode, attr->ia_mtime);
if (!timespec64_equal(&now, &mtime))
- updated |= S_MTIME;
+ updated = true;
inode_maybe_inc_iversion(inode, updated);
cache_flags |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
@@ -669,35 +669,31 @@ static void nfs_set_timestamps_to_ts(struct inode *inode, struct iattr *attr)
NFS_I(inode)->cache_validity &= ~cache_flags;
}
-static void nfs_update_timestamps(struct inode *inode, unsigned int ia_valid)
+static void nfs_update_atime(struct inode *inode)
{
- enum file_time_flags time_flags = 0;
- unsigned int cache_flags = 0;
+ inode_update_time(inode, FS_UPD_ATIME, 0);
+ NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ATIME;
+}
- if (ia_valid & ATTR_MTIME) {
- time_flags |= S_MTIME | S_CTIME;
- cache_flags |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
- }
- if (ia_valid & ATTR_ATIME) {
- time_flags |= S_ATIME;
- cache_flags |= NFS_INO_INVALID_ATIME;
- }
- inode_update_timestamps(inode, time_flags);
- NFS_I(inode)->cache_validity &= ~cache_flags;
+static void nfs_update_mtime(struct inode *inode)
+{
+ inode_update_time(inode, FS_UPD_CMTIME, 0);
+ NFS_I(inode)->cache_validity &=
+ ~(NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME);
}
void nfs_update_delegated_atime(struct inode *inode)
{
spin_lock(&inode->i_lock);
if (nfs_have_delegated_atime(inode))
- nfs_update_timestamps(inode, ATTR_ATIME);
+ nfs_update_atime(inode);
spin_unlock(&inode->i_lock);
}
void nfs_update_delegated_mtime_locked(struct inode *inode)
{
if (nfs_have_delegated_mtime(inode))
- nfs_update_timestamps(inode, ATTR_MTIME);
+ nfs_update_mtime(inode);
}
void nfs_update_delegated_mtime(struct inode *inode)
@@ -751,7 +747,10 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
ATTR_ATIME|ATTR_ATIME_SET);
}
} else {
- nfs_update_timestamps(inode, attr->ia_valid);
+ if (attr->ia_valid & ATTR_MTIME)
+ nfs_update_mtime(inode);
+ if (attr->ia_valid & ATTR_ATIME)
+ nfs_update_atime(inode);
attr->ia_valid &= ~(ATTR_MTIME|ATTR_ATIME);
}
spin_unlock(&inode->i_lock);
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index d7275990ffa4..f420f48fc069 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -872,22 +872,27 @@ int orangefs_permission(struct mnt_idmap *idmap,
return generic_permission(&nop_mnt_idmap, inode, mask);
}
-int orangefs_update_time(struct inode *inode, int flags)
+int orangefs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- struct iattr iattr;
+ struct iattr iattr = { };
+ int dirty;
- gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_update_time: %pU\n",
- get_khandle_from_ino(inode));
+ if (flags & IOCB_NOWAIT)
+ return -EAGAIN;
- flags = inode_update_timestamps(inode, flags);
+ switch (type) {
+ case FS_UPD_ATIME:
+ iattr.ia_valid = ATTR_ATIME;
+ break;
+ case FS_UPD_CMTIME:
+ iattr.ia_valid = ATTR_CTIME | ATTR_MTIME;
+ break;
+ }
- memset(&iattr, 0, sizeof iattr);
- if (flags & S_ATIME)
- iattr.ia_valid |= ATTR_ATIME;
- if (flags & S_CTIME)
- iattr.ia_valid |= ATTR_CTIME;
- if (flags & S_MTIME)
- iattr.ia_valid |= ATTR_MTIME;
+ dirty = inode_update_time(inode, type, flags);
+ if (dirty <= 0)
+ return dirty;
return __orangefs_setattr(inode, &iattr);
}
diff --git a/fs/orangefs/orangefs-kernel.h b/fs/orangefs/orangefs-kernel.h
index 29c6da43e396..1451fc2c1917 100644
--- a/fs/orangefs/orangefs-kernel.h
+++ b/fs/orangefs/orangefs-kernel.h
@@ -360,7 +360,8 @@ int orangefs_getattr(struct mnt_idmap *idmap, const struct path *path,
int orangefs_permission(struct mnt_idmap *idmap,
struct inode *inode, int mask);
-int orangefs_update_time(struct inode *, int);
+int orangefs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
/*
* defined in xattr.c
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index bdbf86b56a9b..00c69707bda9 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -555,9 +555,10 @@ int ovl_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
}
#endif
-int ovl_update_time(struct inode *inode, int flags)
+int ovl_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
- if (flags & S_ATIME) {
+ if (type == FS_UPD_ATIME) {
struct ovl_fs *ofs = OVL_FS(inode->i_sb);
struct path upperpath = {
.mnt = ovl_upper_mnt(ofs),
@@ -565,6 +566,8 @@ int ovl_update_time(struct inode *inode, int flags)
};
if (upperpath.dentry) {
+ if (flags & IOCB_NOWAIT)
+ return -EAGAIN;
touch_atime(&upperpath);
inode_set_atime_to_ts(inode,
inode_get_atime(d_inode(upperpath.dentry)));
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index f9ac9bdde830..315882a360ce 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -820,7 +820,8 @@ static inline struct posix_acl *ovl_get_acl_path(const struct path *path,
}
#endif
-int ovl_update_time(struct inode *inode, int flags);
+int ovl_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
bool ovl_is_private_xattr(struct super_block *sb, const char *name);
struct ovl_inode_params {
diff --git a/fs/sync.c b/fs/sync.c
index 431fc5f5be06..4283af7119d1 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -183,8 +183,8 @@ 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_state_read_once(inode) & I_DIRTY_TIME))
- mark_inode_dirty_sync(inode);
+ if (!datasync)
+ sync_lazytime(inode);
return file->f_op->fsync(file, start, end, datasync);
}
EXPORT_SYMBOL(vfs_fsync_range);
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index c3265b8804f5..3dc3ca1cd803 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1361,17 +1361,8 @@ static inline int mctime_update_needed(const struct inode *inode,
return 0;
}
-/**
- * ubifs_update_time - update time of inode.
- * @inode: inode to update
- * @flags: time updating control flag determines updating
- * which time fields of @inode
- *
- * This function updates time of the inode.
- *
- * Returns: %0 for success or a negative error code otherwise.
- */
-int ubifs_update_time(struct inode *inode, int flags)
+int ubifs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags)
{
struct ubifs_inode *ui = ubifs_inode(inode);
struct ubifs_info *c = inode->i_sb->s_fs_info;
@@ -1379,17 +1370,22 @@ int ubifs_update_time(struct inode *inode, int flags)
.dirtied_ino_d = ALIGN(ui->data_len, 8) };
int err, release;
- if (!IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT)) {
- generic_update_time(inode, flags);
- return 0;
- }
+ /* ubifs sets S_NOCMTIME on all inodes, this should not happen. */
+ if (WARN_ON_ONCE(type != FS_UPD_ATIME))
+ return -EIO;
+
+ if (!IS_ENABLED(CONFIG_UBIFS_ATIME_SUPPORT))
+ return generic_update_time(inode, type, flags);
+
+ if (flags & IOCB_NOWAIT)
+ return -EAGAIN;
err = ubifs_budget_space(c, &req);
if (err)
return err;
mutex_lock(&ui->ui_mutex);
- inode_update_timestamps(inode, flags);
+ inode_update_time(inode, type, flags);
release = ui->dirty;
__mark_inode_dirty(inode, I_DIRTY_SYNC);
mutex_unlock(&ui->ui_mutex);
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 118392aa9f2a..b62a154c7bd4 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -2018,7 +2018,8 @@ int ubifs_calc_dark(const struct ubifs_info *c, int spc);
int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
int ubifs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr);
-int ubifs_update_time(struct inode *inode, int flags);
+int ubifs_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
/* dir.c */
struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index ad94fbf55014..1cdd8a360510 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -1184,26 +1184,33 @@ xfs_vn_setattr(
STATIC int
xfs_vn_update_time(
struct inode *inode,
- int flags)
+ enum fs_update_time type,
+ unsigned int flags)
{
struct xfs_inode *ip = XFS_I(inode);
struct xfs_mount *mp = ip->i_mount;
int log_flags = XFS_ILOG_TIMESTAMP;
struct xfs_trans *tp;
int error;
- struct timespec64 now;
trace_xfs_update_time(ip);
if (inode->i_sb->s_flags & SB_LAZYTIME) {
- if (!((flags & S_VERSION) &&
- inode_maybe_inc_iversion(inode, false))) {
- generic_update_time(inode, flags);
+ int dirty;
+
+ dirty = inode_update_time(inode, type, flags);
+ if (dirty <= 0)
+ return dirty;
+ if (dirty == I_DIRTY_TIME) {
+ __mark_inode_dirty(inode, I_DIRTY_TIME);
return 0;
}
/* Capture the iversion update that just occurred */
log_flags |= XFS_ILOG_CORE;
+ } else {
+ if (flags & IOCB_NOWAIT)
+ return -EAGAIN;
}
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
@@ -1211,21 +1218,31 @@ xfs_vn_update_time(
return error;
xfs_ilock(ip, XFS_ILOCK_EXCL);
- if (flags & (S_CTIME|S_MTIME))
- now = inode_set_ctime_current(inode);
+ if (type == FS_UPD_ATIME)
+ inode_set_atime_to_ts(inode, current_time(inode));
else
- now = current_time(inode);
-
- if (flags & S_MTIME)
- inode_set_mtime_to_ts(inode, now);
- if (flags & S_ATIME)
- inode_set_atime_to_ts(inode, now);
-
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
xfs_trans_log_inode(tp, ip, log_flags);
return xfs_trans_commit(tp);
}
+static void
+xfs_vn_sync_lazytime(
+ struct inode *inode)
+{
+ struct xfs_inode *ip = XFS_I(inode);
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_trans *tp;
+
+ if (xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp))
+ return;
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_TIMESTAMP);
+ xfs_trans_commit(tp);
+}
+
STATIC int
xfs_vn_fiemap(
struct inode *inode,
@@ -1269,6 +1286,7 @@ static const struct inode_operations xfs_inode_operations = {
.listxattr = xfs_vn_listxattr,
.fiemap = xfs_vn_fiemap,
.update_time = xfs_vn_update_time,
+ .sync_lazytime = xfs_vn_sync_lazytime,
.fileattr_get = xfs_fileattr_get,
.fileattr_set = xfs_fileattr_set,
};
@@ -1295,6 +1313,7 @@ static const struct inode_operations xfs_dir_inode_operations = {
.setattr = xfs_vn_setattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
+ .sync_lazytime = xfs_vn_sync_lazytime,
.tmpfile = xfs_vn_tmpfile,
.fileattr_get = xfs_fileattr_get,
.fileattr_set = xfs_fileattr_set,
@@ -1322,6 +1341,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
.setattr = xfs_vn_setattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
+ .sync_lazytime = xfs_vn_sync_lazytime,
.tmpfile = xfs_vn_tmpfile,
.fileattr_get = xfs_fileattr_get,
.fileattr_set = xfs_fileattr_set,
@@ -1333,6 +1353,7 @@ static const struct inode_operations xfs_symlink_inode_operations = {
.setattr = xfs_vn_setattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
+ .sync_lazytime = xfs_vn_sync_lazytime,
.fileattr_get = xfs_fileattr_get,
.fileattr_set = xfs_fileattr_set,
};
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index bc71aa9dcee8..094f257eff15 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -712,34 +712,6 @@ xfs_fs_destroy_inode(
xfs_inode_mark_reclaimable(ip);
}
-static void
-xfs_fs_dirty_inode(
- struct inode *inode,
- int flags)
-{
- 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;
-
- /*
- * Only do the timestamp update if the inode is dirty (I_DIRTY_SYNC)
- * and has dirty timestamp (I_DIRTY_TIME). I_DIRTY_TIME can be passed
- * in flags possibly together with I_DIRTY_SYNC.
- */
- if ((flags & ~I_DIRTY_TIME) != I_DIRTY_SYNC || !(flags & 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);
- xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
- xfs_trans_log_inode(tp, ip, XFS_ILOG_TIMESTAMP);
- xfs_trans_commit(tp);
-}
-
/*
* Slab object creation initialisation for the XFS inode.
* This covers only the idempotent fields in the XFS inode;
@@ -1304,7 +1276,6 @@ xfs_fs_show_stats(
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,
.drop_inode = xfs_fs_drop_inode,
.evict_inode = xfs_fs_evict_inode,
.put_super = xfs_fs_put_super,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a01621fa636a..c0bf07cc45e0 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1717,6 +1717,13 @@ static inline struct timespec64 inode_set_ctime(struct inode *inode,
struct timespec64 simple_inode_init_ts(struct inode *inode);
+static inline int inode_time_dirty_flag(struct inode *inode)
+{
+ if (inode->i_sb->s_flags & SB_LAZYTIME)
+ return I_DIRTY_TIME;
+ return I_DIRTY_SYNC;
+}
+
/*
* Snapshotting support.
*/
@@ -1985,6 +1992,11 @@ int wrap_directory_iterator(struct file *, struct dir_context *,
static int shared_##x(struct file *file , struct dir_context *ctx) \
{ return wrap_directory_iterator(file, ctx, x); }
+enum fs_update_time {
+ FS_UPD_ATIME,
+ FS_UPD_CMTIME,
+};
+
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
@@ -2012,7 +2024,9 @@ struct inode_operations {
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
- int (*update_time)(struct inode *, int);
+ int (*update_time)(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
+ void (*sync_lazytime)(struct inode *inode);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode);
@@ -2239,16 +2253,8 @@ static inline void inode_dec_link_count(struct inode *inode)
mark_inode_dirty(inode);
}
-enum file_time_flags {
- S_ATIME = 1,
- S_MTIME = 2,
- S_CTIME = 4,
- S_VERSION = 8,
-};
-
extern bool atime_needs_update(const struct path *, struct inode *);
extern void touch_atime(const struct path *);
-int inode_update_time(struct inode *inode, int flags);
static inline void file_accessed(struct file *file)
{
@@ -2401,8 +2407,10 @@ static inline void super_set_sysfs_name_generic(struct super_block *sb, const ch
extern void ihold(struct inode * inode);
extern void iput(struct inode *);
void iput_not_last(struct inode *);
-int inode_update_timestamps(struct inode *inode, int flags);
-int generic_update_time(struct inode *, int);
+int inode_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
+int generic_update_time(struct inode *inode, enum fs_update_time type,
+ unsigned int flags);
/* /sys/fs */
extern struct kobject *fs_kobj;
diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h
index 311a341e6fe4..7162d03e69a5 100644
--- a/include/trace/events/writeback.h
+++ b/include/trace/events/writeback.h
@@ -856,12 +856,6 @@ DEFINE_EVENT(writeback_inode_template, writeback_lazytime,
TP_ARGS(inode)
);
-DEFINE_EVENT(writeback_inode_template, writeback_lazytime_iput,
- TP_PROTO(struct inode *inode),
-
- TP_ARGS(inode)
-);
-
DEFINE_EVENT(writeback_inode_template, writeback_dirty_inode_enqueue,
TP_PROTO(struct inode *inode),