]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fs: add support for non-blocking timestamp updates
authorChristoph Hellwig <hch@lst.de>
Thu, 8 Jan 2026 14:19:08 +0000 (15:19 +0100)
committerChristian Brauner <brauner@kernel.org>
Mon, 12 Jan 2026 13:01:33 +0000 (14:01 +0100)
Currently file_update_time_flags unconditionally returns -EAGAIN if any
timestamp needs to be updated and IOCB_NOWAIT is passed.  This makes
non-blocking direct writes impossible on file systems with granular
enough timestamps.

Pass IOCB_NOWAIT to ->update_time and return -EAGAIN if it could block.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20260108141934.2052404-9-hch@lst.de
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/btrfs/inode.c
fs/gfs2/inode.c
fs/inode.c
fs/orangefs/inode.c
fs/overlayfs/inode.c
fs/ubifs/file.c
fs/xfs/xfs_iops.c

index 753c31a12a54ec11a80361df708aa19424fa7c41..adc9107fb412b1a3f3bd7b7ff66a30bb1bb12493 100644 (file)
@@ -6353,6 +6353,8 @@ static int btrfs_update_time(struct inode *inode, enum fs_update_time type,
 
        if (btrfs_root_readonly(root))
                return -EROFS;
+       if (flags & IOCB_NOWAIT)
+               return -EAGAIN;
 
        dirty = inode_update_time(inode, type, flags);
        if (dirty <= 0)
index 4ef39ff6889dece3455e6b56537eef7e87509539..c02ebf0ca6250dba2ac2ff88afb4e25619ab8225 100644 (file)
@@ -2250,6 +2250,9 @@ static int gfs2_update_time(struct inode *inode, enum fs_update_time type,
        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);
index 0cafe74bff2d4a75304178bcfb6a9b7565f1a689..cd3ca98e8355b5f4371cde54d90b039db28a468f 100644 (file)
@@ -2090,7 +2090,7 @@ static int inode_update_atime(struct inode *inode)
        return inode_time_dirty_flag(inode);
 }
 
-static int inode_update_cmtime(struct inode *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);
@@ -2101,12 +2101,27 @@ static int inode_update_cmtime(struct inode *inode)
        mtime_changed = !timespec64_equal(&now, &mtime);
        if (mtime_changed || !timespec64_equal(&now, &ctime))
                dirty = inode_time_dirty_flag(inode);
-       if (mtime_changed)
-               inode_set_mtime_to_ts(inode, now);
 
-       if (IS_I_VERSION(inode) && inode_maybe_inc_iversion(inode, !!dirty))
-               dirty |= I_DIRTY_SYNC;
+       /*
+        * 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;
+               }
+       }
 
+       if (mtime_changed)
+               inode_set_mtime_to_ts(inode, now);
        return dirty;
 }
 
@@ -2131,7 +2146,7 @@ int inode_update_time(struct inode *inode, enum fs_update_time type,
        case FS_UPD_ATIME:
                return inode_update_atime(inode);
        case FS_UPD_CMTIME:
-               return inode_update_cmtime(inode);
+               return inode_update_cmtime(inode, flags);
        default:
                WARN_ON_ONCE(1);
                return -EIO;
@@ -2152,6 +2167,16 @@ int generic_update_time(struct inode *inode, enum fs_update_time type,
 {
        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;
@@ -2380,15 +2405,13 @@ static int file_update_time_flags(struct file *file, unsigned int flags)
        if (!need_update)
                return 0;
 
-       if (flags & IOCB_NOWAIT)
-               return -EAGAIN;
-
+       flags &= IOCB_NOWAIT;
        if (mnt_get_write_access_file(file))
                return 0;
        if (inode->i_op->update_time)
-               ret = inode->i_op->update_time(inode, FS_UPD_CMTIME, 0);
+               ret = inode->i_op->update_time(inode, FS_UPD_CMTIME, flags);
        else
-               ret = generic_update_time(inode, FS_UPD_CMTIME, 0);
+               ret = generic_update_time(inode, FS_UPD_CMTIME, flags);
        mnt_put_write_access_file(file);
        return ret;
 }
index eab16afb5b8af2ddfe19583368771e94e94eb3a5..f420f48fc069caff8c7520a3e571bf9a0acd5f30 100644 (file)
@@ -878,6 +878,9 @@ int orangefs_update_time(struct inode *inode, enum fs_update_time type,
        struct iattr iattr = { };
        int dirty;
 
+       if (flags & IOCB_NOWAIT)
+               return -EAGAIN;
+
        switch (type) {
        case FS_UPD_ATIME:
                iattr.ia_valid = ATTR_ATIME;
index c0ce3519e4af70fec6fd670ea289260114fba583..00c69707bda9846d8d8b9b7647f4135f0aa953ca 100644 (file)
@@ -566,6 +566,8 @@ int ovl_update_time(struct inode *inode, enum fs_update_time type,
                };
 
                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)));
index 0cc44ad142de3cd02aba467884eae701d59fe26c..3dc3ca1cd80324fb4627393a4a81670756873929 100644 (file)
@@ -1377,6 +1377,9 @@ int ubifs_update_time(struct inode *inode, enum fs_update_time type,
        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;
index d9eae1af14a864ba463158612dab03b04804e030..aef5b05c1b769d5cea45fbd9a3d4860a7ccc292b 100644 (file)
@@ -1195,6 +1195,9 @@ xfs_vn_update_time(
 
        trace_xfs_update_time(ip);
 
+       if (flags & IOCB_NOWAIT)
+               return -EAGAIN;
+
        if (inode->i_sb->s_flags & SB_LAZYTIME) {
                if (type == FS_UPD_ATIME ||
                    !inode_maybe_inc_iversion(inode, false))