diff options
author | Fabian Frederick <fabf@skynet.be> | 2014-06-04 19:06:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-04 19:53:55 -0400 |
commit | ac13a829f6adb674015ab399594c089990104af7 (patch) | |
tree | 29f0b78758080bac01307e705371283a406b41c4 | |
parent | fd2916bd77109b69891573fd1e1205ecc619893e (diff) |
fs/libfs.c: add generic data flush to fsync
Description by Jan Kara:
"A lot of older filesystems don't properly flush volatile disk caches
on fsync(2) which can lead to loss of fsynced data after power failure.
This patch makes generic_file_fsync() issue proper cache flush to fix the
problem. Sysadmin can use /sys/devices/.../cache_type to tell the system
it should not send the cache flush."
[akpm@linux-foundation.org: nuke ifdef]
[akpm@linux-foundation.org: fix warning]
Signed-off-by: Fabian Frederick <fabf@skynet.be>
Suggested-by: Jan Kara <jack@suse.cz>
Suggested-by: Christoph Hellwig <hch@infradead.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: "Theodore Ts'o" <tytso@mit.edu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/libfs.c | 34 | ||||
-rw-r--r-- | include/linux/blkdev.h | 9 | ||||
-rw-r--r-- | include/linux/fs.h | 1 |
3 files changed, 41 insertions, 3 deletions
diff --git a/fs/libfs.c b/fs/libfs.c index a1844244246f..88e3e00e2eca 100644 --- a/fs/libfs.c +++ b/fs/libfs.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * Library for filesystems writers. | 3 | * Library for filesystems writers. |
4 | */ | 4 | */ |
5 | 5 | ||
6 | #include <linux/blkdev.h> | ||
6 | #include <linux/export.h> | 7 | #include <linux/export.h> |
7 | #include <linux/pagemap.h> | 8 | #include <linux/pagemap.h> |
8 | #include <linux/slab.h> | 9 | #include <linux/slab.h> |
@@ -923,16 +924,19 @@ struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid, | |||
923 | EXPORT_SYMBOL_GPL(generic_fh_to_parent); | 924 | EXPORT_SYMBOL_GPL(generic_fh_to_parent); |
924 | 925 | ||
925 | /** | 926 | /** |
926 | * generic_file_fsync - generic fsync implementation for simple filesystems | 927 | * __generic_file_fsync - generic fsync implementation for simple filesystems |
928 | * | ||
927 | * @file: file to synchronize | 929 | * @file: file to synchronize |
930 | * @start: start offset in bytes | ||
931 | * @end: end offset in bytes (inclusive) | ||
928 | * @datasync: only synchronize essential metadata if true | 932 | * @datasync: only synchronize essential metadata if true |
929 | * | 933 | * |
930 | * This is a generic implementation of the fsync method for simple | 934 | * This is a generic implementation of the fsync method for simple |
931 | * filesystems which track all non-inode metadata in the buffers list | 935 | * filesystems which track all non-inode metadata in the buffers list |
932 | * hanging off the address_space structure. | 936 | * hanging off the address_space structure. |
933 | */ | 937 | */ |
934 | int generic_file_fsync(struct file *file, loff_t start, loff_t end, | 938 | int __generic_file_fsync(struct file *file, loff_t start, loff_t end, |
935 | int datasync) | 939 | int datasync) |
936 | { | 940 | { |
937 | struct inode *inode = file->f_mapping->host; | 941 | struct inode *inode = file->f_mapping->host; |
938 | int err; | 942 | int err; |
@@ -952,10 +956,34 @@ int generic_file_fsync(struct file *file, loff_t start, loff_t end, | |||
952 | err = sync_inode_metadata(inode, 1); | 956 | err = sync_inode_metadata(inode, 1); |
953 | if (ret == 0) | 957 | if (ret == 0) |
954 | ret = err; | 958 | ret = err; |
959 | |||
955 | out: | 960 | out: |
956 | mutex_unlock(&inode->i_mutex); | 961 | mutex_unlock(&inode->i_mutex); |
957 | return ret; | 962 | return ret; |
958 | } | 963 | } |
964 | EXPORT_SYMBOL(__generic_file_fsync); | ||
965 | |||
966 | /** | ||
967 | * generic_file_fsync - generic fsync implementation for simple filesystems | ||
968 | * with flush | ||
969 | * @file: file to synchronize | ||
970 | * @start: start offset in bytes | ||
971 | * @end: end offset in bytes (inclusive) | ||
972 | * @datasync: only synchronize essential metadata if true | ||
973 | * | ||
974 | */ | ||
975 | |||
976 | int generic_file_fsync(struct file *file, loff_t start, loff_t end, | ||
977 | int datasync) | ||
978 | { | ||
979 | struct inode *inode = file->f_mapping->host; | ||
980 | int err; | ||
981 | |||
982 | err = __generic_file_fsync(file, start, end, datasync); | ||
983 | if (err) | ||
984 | return err; | ||
985 | return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); | ||
986 | } | ||
959 | EXPORT_SYMBOL(generic_file_fsync); | 987 | EXPORT_SYMBOL(generic_file_fsync); |
960 | 988 | ||
961 | /** | 989 | /** |
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 8aba35f46f87..45cf6e537c83 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h | |||
@@ -1607,6 +1607,9 @@ struct block_device_operations { | |||
1607 | extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int, | 1607 | extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int, |
1608 | unsigned long); | 1608 | unsigned long); |
1609 | #else /* CONFIG_BLOCK */ | 1609 | #else /* CONFIG_BLOCK */ |
1610 | |||
1611 | struct block_device; | ||
1612 | |||
1610 | /* | 1613 | /* |
1611 | * stubs for when the block layer is configured out | 1614 | * stubs for when the block layer is configured out |
1612 | */ | 1615 | */ |
@@ -1642,6 +1645,12 @@ static inline bool blk_needs_flush_plug(struct task_struct *tsk) | |||
1642 | return false; | 1645 | return false; |
1643 | } | 1646 | } |
1644 | 1647 | ||
1648 | static inline int blkdev_issue_flush(struct block_device *bdev, gfp_t gfp_mask, | ||
1649 | sector_t *error_sector) | ||
1650 | { | ||
1651 | return 0; | ||
1652 | } | ||
1653 | |||
1645 | #endif /* CONFIG_BLOCK */ | 1654 | #endif /* CONFIG_BLOCK */ |
1646 | 1655 | ||
1647 | #endif | 1656 | #endif |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 878031227c57..c3f46e499dd0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -2590,6 +2590,7 @@ extern ssize_t simple_read_from_buffer(void __user *to, size_t count, | |||
2590 | extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos, | 2590 | extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos, |
2591 | const void __user *from, size_t count); | 2591 | const void __user *from, size_t count); |
2592 | 2592 | ||
2593 | extern int __generic_file_fsync(struct file *, loff_t, loff_t, int); | ||
2593 | extern int generic_file_fsync(struct file *, loff_t, loff_t, int); | 2594 | extern int generic_file_fsync(struct file *, loff_t, loff_t, int); |
2594 | 2595 | ||
2595 | extern int generic_check_addressable(unsigned, u64); | 2596 | extern int generic_check_addressable(unsigned, u64); |