summaryrefslogtreecommitdiff
path: root/fs/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/inode.c')
-rw-r--r--fs/inode.c45
1 files changed, 34 insertions, 11 deletions
diff --git a/fs/inode.c b/fs/inode.c
index 0cafe74bff2d..cd3ca98e8355 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -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;
}