aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <josef@redhat.com>2012-03-26 09:59:21 -0400
committerZiyan <jaraidaniel@gmail.com>2016-04-03 14:55:52 +0200
commitd850b9a178a8d6aba2bc0efd09bd9b9a173b277e (patch)
tree2de3051377075c97db0b4233cf65a2f5451f9d4f /fs
parentbc9600bb91c52252e67c163606ca57fbf0c369b3 (diff)
downloadkernel_samsung_tuna-d850b9a178a8d6aba2bc0efd09bd9b9a173b277e.zip
kernel_samsung_tuna-d850b9a178a8d6aba2bc0efd09bd9b9a173b277e.tar.gz
kernel_samsung_tuna-d850b9a178a8d6aba2bc0efd09bd9b9a173b277e.tar.bz2
fs: introduce inode operation ->update_time
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 <josef@redhat.com> (cherry picked from commit c3b2da314834499f34cba94f7053e55f6d6f92d8) Conflicts: fs/inode.c
Diffstat (limited to 'fs')
-rw-r--r--fs/fuse/file.c4
-rw-r--r--fs/inode.c56
-rw-r--r--fs/ncpfs/file.c6
-rw-r--r--fs/ntfs/file.c4
-rw-r--r--fs/pipe.c7
-rw-r--r--fs/splice.c6
-rw-r--r--fs/xfs/linux-2.6/xfs_file.c7
7 files changed, 64 insertions, 26 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index d4dd953..65eb03f 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1019,7 +1019,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;
iov_iter_init(&i, iov, nr_segs, count, 0);
written = fuse_perform_write(file, mapping, &i, pos);
diff --git a/fs/inode.c b/fs/inode.c
index 5519c9d..8f021d0 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1434,6 +1434,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
* @mnt: mount the inode is accessed on
@@ -1471,8 +1492,14 @@ void touch_atime(struct vfsmount *mnt, struct dentry *dentry)
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);
@@ -1486,18 +1513,20 @@ EXPORT_SYMBOL(touch_atime);
* 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))
@@ -1510,21 +1539,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);
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c
index 64a3264..2b622d9 100644
--- a/fs/ncpfs/file.c
+++ b/fs/ncpfs/file.c
@@ -222,6 +222,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 */
@@ -253,8 +257,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)) {
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index c404ad0..3c23af3 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -2097,7 +2097,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:
diff --git a/fs/pipe.c b/fs/pipe.c
index 342aa86..e051dfb 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -653,8 +653,11 @@ out:
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;
}
diff --git a/fs/splice.c b/fs/splice.c
index 16d0cb4..5dbffc8 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1013,8 +1013,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);
diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c
index 358df1f..af87007 100644
--- a/fs/xfs/linux-2.6/xfs_file.c
+++ b/fs/xfs/linux-2.6/xfs_file.c
@@ -687,8 +687,11 @@ xfs_file_aio_write_checks(
if (new_size > ip->i_size)
ip->i_new_size = new_size;
- 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 the offset is beyond the size of the file, we need to zero any