fs: add support for non-blocking timestamp updates

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>
master
Christoph Hellwig 2026-01-08 15:19:08 +01:00 committed by Christian Brauner
parent 5cf06ea56e
commit 85c871a02b
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
7 changed files with 50 additions and 11 deletions

View 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)

View 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);

View 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);
/*
* 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);
if (IS_I_VERSION(inode) && inode_maybe_inc_iversion(inode, !!dirty))
dirty |= I_DIRTY_SYNC;
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;
}

View 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;

View 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)));

View 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;

View 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))