Skip to content

Commit

Permalink
fs: introduce inode operation ->update_time
Browse files Browse the repository at this point in the history
Btrfs has to make sure we have space to allocate new blocks in order to modify
the inode, so updating time can fail.  We've gotten around this by having our
own file_update_time but this is kind of a pain, and Christoph has indicated he
would like to make xfs do something different with atime updates.  So introduce
->update_time, where we will deal with i_version an a/m/c time updates and
indicate which changes need to be made.  The normal version just does what it
has always done, updates the time and marks the inode dirty, and then
filesystems can choose to do something different.

I've gone through all of the users of file_update_time and made them check for
errors with the exception of the fault code since it's complicated and I wasn't
quite sure what to do there, also Jan is going to be pushing the file time
updates into page_mkwrite for those who have it so that should satisfy btrfs and
make it not a big deal to check the file_update_time() return code in the
generic fault path. Thanks,

Signed-off-by: Josef Bacik <[email protected]>
  • Loading branch information
Josef Bacik authored and Al Viro committed Jun 1, 2012
1 parent 033369d commit c3b2da3
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 29 deletions.
3 changes: 3 additions & 0 deletions Documentation/filesystems/Locking
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ ata *);
int (*removexattr) (struct dentry *, const char *);
void (*truncate_range)(struct inode *, loff_t, loff_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
void (*update_time)(struct inode *, struct timespec *, int);

locking rules:
all may block
Expand Down Expand Up @@ -89,6 +90,8 @@ listxattr: no
removexattr: yes
truncate_range: yes
fiemap: no
update_time: no

Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
victim.
cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem.
Expand Down
4 changes: 4 additions & 0 deletions Documentation/filesystems/vfs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ struct inode_operations {
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*removexattr) (struct dentry *, const char *);
void (*truncate_range)(struct inode *, loff_t, loff_t);
void (*update_time)(struct inode *, struct timespec *, int);
};

Again, all methods are called without any locks being held, unless
Expand Down Expand Up @@ -475,6 +476,9 @@ otherwise noted.
truncate_range: a method provided by the underlying filesystem to truncate a
range of blocks , i.e. punch a hole somewhere in a file.

update_time: called by the VFS to update a specific time or the i_version of
an inode. If this is not defined the VFS will update the inode itself
and call mark_inode_dirty_sync.

The Address Space Object
========================
Expand Down
4 changes: 3 additions & 1 deletion fs/fuse/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,9 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (err)
goto out;

file_update_time(file);
err = file_update_time(file);
if (err)
goto out;

if (file->f_flags & O_DIRECT) {
written = generic_file_direct_write(iocb, iov, &nr_segs,
Expand Down
56 changes: 40 additions & 16 deletions fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,27 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,
return 0;
}

/*
* This does the actual work of updating an inodes time or version. Must have
* had called mnt_want_write() before calling this.
*/
static int update_time(struct inode *inode, struct timespec *time, int flags)
{
if (inode->i_op->update_time)
return inode->i_op->update_time(inode, time, flags);

if (flags & S_ATIME)
inode->i_atime = *time;
if (flags & S_VERSION)
inode_inc_iversion(inode);
if (flags & S_CTIME)
inode->i_ctime = *time;
if (flags & S_MTIME)
inode->i_mtime = *time;
mark_inode_dirty_sync(inode);
return 0;
}

/**
* touch_atime - update the access time
* @path: the &struct path to update
Expand Down Expand Up @@ -1524,8 +1545,14 @@ void touch_atime(struct path *path)
if (mnt_want_write(mnt))
return;

inode->i_atime = now;
mark_inode_dirty_sync(inode);
/*
* File systems can error out when updating inodes if they need to
* allocate new space to modify an inode (such is the case for
* Btrfs), but since we touch atime while walking down the path we
* really don't care if we failed to update the atime of the file,
* so just ignore the return value.
*/
update_time(inode, &now, S_ATIME);
mnt_drop_write(mnt);
}
EXPORT_SYMBOL(touch_atime);
Expand Down Expand Up @@ -1604,18 +1631,20 @@ EXPORT_SYMBOL(file_remove_suid);
* usage in the file write path of filesystems, and filesystems may
* choose to explicitly ignore update via this function with the
* S_NOCMTIME inode flag, e.g. for network filesystem where these
* timestamps are handled by the server.
* timestamps are handled by the server. This can return an error for
* file systems who need to allocate space in order to update an inode.
*/

void file_update_time(struct file *file)
int file_update_time(struct file *file)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct timespec now;
enum { S_MTIME = 1, S_CTIME = 2, S_VERSION = 4 } sync_it = 0;
int sync_it = 0;
int ret;

/* First try to exhaust all avenues to not sync */
if (IS_NOCMTIME(inode))
return;
return 0;

now = current_fs_time(inode->i_sb);
if (!timespec_equal(&inode->i_mtime, &now))
Expand All @@ -1628,21 +1657,16 @@ void file_update_time(struct file *file)
sync_it |= S_VERSION;

if (!sync_it)
return;
return 0;

/* Finally allowed to write? Takes lock. */
if (mnt_want_write_file(file))
return;
return 0;

/* Only change inode inside the lock region */
if (sync_it & S_VERSION)
inode_inc_iversion(inode);
if (sync_it & S_CTIME)
inode->i_ctime = now;
if (sync_it & S_MTIME)
inode->i_mtime = now;
mark_inode_dirty_sync(inode);
ret = update_time(inode, &now, sync_it);
mnt_drop_write_file(file);

return ret;
}
EXPORT_SYMBOL(file_update_time);

Expand Down
6 changes: 4 additions & 2 deletions fs/ncpfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *

already_written = 0;

errno = file_update_time(file);
if (errno)
goto outrel;

bouncebuffer = vmalloc(bufsize);
if (!bouncebuffer) {
errno = -EIO; /* -ENOMEM */
Expand Down Expand Up @@ -252,8 +256,6 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *
}
vfree(bouncebuffer);

file_update_time(file);

*ppos = pos;

if (pos > i_size_read(inode)) {
Expand Down
4 changes: 3 additions & 1 deletion fs/ntfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -2096,7 +2096,9 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
err = file_remove_suid(file);
if (err)
goto out;
file_update_time(file);
err = file_update_time(file);
if (err)
goto out;
written = ntfs_file_buffered_write(iocb, iov, nr_segs, pos, ppos,
count);
out:
Expand Down
7 changes: 5 additions & 2 deletions fs/pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,11 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov,
wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLRDNORM);
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
}
if (ret > 0)
file_update_time(filp);
if (ret > 0) {
int err = file_update_time(filp);
if (err)
ret = err;
}
return ret;
}

Expand Down
6 changes: 4 additions & 2 deletions fs/splice.c
Original file line number Diff line number Diff line change
Expand Up @@ -1003,8 +1003,10 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
ret = file_remove_suid(out);
if (!ret) {
file_update_time(out);
ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file);
ret = file_update_time(out);
if (!ret)
ret = splice_from_pipe_feed(pipe, &sd,
pipe_to_file);
}
mutex_unlock(&inode->i_mutex);
} while (ret > 0);
Expand Down
7 changes: 5 additions & 2 deletions fs/xfs/xfs_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,11 @@ xfs_file_aio_write_checks(
* lock above. Eventually we should look into a way to avoid
* the pointless lock roundtrip.
*/
if (likely(!(file->f_mode & FMODE_NOCMTIME)))
file_update_time(file);
if (likely(!(file->f_mode & FMODE_NOCMTIME))) {
error = file_update_time(file);
if (error)
return error;
}

/*
* If we're writing the file then make sure to clear the setuid and
Expand Down
10 changes: 9 additions & 1 deletion include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1684,6 +1684,7 @@ struct inode_operations {
void (*truncate_range)(struct inode *, loff_t, loff_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
int (*update_time)(struct inode *, struct timespec *, int);
} ____cacheline_aligned;

struct seq_file;
Expand Down Expand Up @@ -1843,6 +1844,13 @@ static inline void inode_inc_iversion(struct inode *inode)
spin_unlock(&inode->i_lock);
}

enum file_time_flags {
S_ATIME = 1,
S_MTIME = 2,
S_CTIME = 4,
S_VERSION = 8,
};

extern void touch_atime(struct path *);
static inline void file_accessed(struct file *file)
{
Expand Down Expand Up @@ -2579,7 +2587,7 @@ extern int inode_change_ok(const struct inode *, struct iattr *);
extern int inode_newsize_ok(const struct inode *, loff_t offset);
extern void setattr_copy(struct inode *inode, const struct iattr *attr);

extern void file_update_time(struct file *file);
extern int file_update_time(struct file *file);

extern int generic_show_options(struct seq_file *m, struct dentry *root);
extern void save_mount_options(struct super_block *sb, char *options);
Expand Down
4 changes: 3 additions & 1 deletion mm/filemap.c
Original file line number Diff line number Diff line change
Expand Up @@ -2463,7 +2463,9 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (err)
goto out;

file_update_time(file);
err = file_update_time(file);
if (err)
goto out;

/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
if (unlikely(file->f_flags & O_DIRECT)) {
Expand Down
4 changes: 3 additions & 1 deletion mm/filemap_xip.c
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,9 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
if (ret)
goto out_backing;

file_update_time(filp);
ret = file_update_time(filp);
if (ret)
goto out_backing;

ret = __xip_file_write (filp, buf, count, pos, ppos);

Expand Down

0 comments on commit c3b2da3

Please sign in to comment.