Commit ac13a829 authored by Fabian Frederick's avatar Fabian Frederick Committed by Linus Torvalds

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: default avatarFabian Frederick <fabf@skynet.be>
Suggested-by: default avatarJan Kara <jack@suse.cz>
Suggested-by: default avatarChristoph 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent fd2916bd
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Library for filesystems writers. * Library for filesystems writers.
*/ */
#include <linux/blkdev.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -923,16 +924,19 @@ struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid, ...@@ -923,16 +924,19 @@ struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid,
EXPORT_SYMBOL_GPL(generic_fh_to_parent); EXPORT_SYMBOL_GPL(generic_fh_to_parent);
/** /**
* generic_file_fsync - generic fsync implementation for simple filesystems * __generic_file_fsync - generic fsync implementation for simple filesystems
*
* @file: file to synchronize * @file: file to synchronize
* @start: start offset in bytes
* @end: end offset in bytes (inclusive)
* @datasync: only synchronize essential metadata if true * @datasync: only synchronize essential metadata if true
* *
* This is a generic implementation of the fsync method for simple * This is a generic implementation of the fsync method for simple
* filesystems which track all non-inode metadata in the buffers list * filesystems which track all non-inode metadata in the buffers list
* hanging off the address_space structure. * hanging off the address_space structure.
*/ */
int generic_file_fsync(struct file *file, loff_t start, loff_t end, int __generic_file_fsync(struct file *file, loff_t start, loff_t end,
int datasync) int datasync)
{ {
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
int err; int err;
...@@ -952,10 +956,34 @@ int generic_file_fsync(struct file *file, loff_t start, loff_t end, ...@@ -952,10 +956,34 @@ int generic_file_fsync(struct file *file, loff_t start, loff_t end,
err = sync_inode_metadata(inode, 1); err = sync_inode_metadata(inode, 1);
if (ret == 0) if (ret == 0)
ret = err; ret = err;
out: out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return ret; return ret;
} }
EXPORT_SYMBOL(__generic_file_fsync);
/**
* generic_file_fsync - generic fsync implementation for simple filesystems
* with flush
* @file: file to synchronize
* @start: start offset in bytes
* @end: end offset in bytes (inclusive)
* @datasync: only synchronize essential metadata if true
*
*/
int generic_file_fsync(struct file *file, loff_t start, loff_t end,
int datasync)
{
struct inode *inode = file->f_mapping->host;
int err;
err = __generic_file_fsync(file, start, end, datasync);
if (err)
return err;
return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
}
EXPORT_SYMBOL(generic_file_fsync); EXPORT_SYMBOL(generic_file_fsync);
/** /**
......
...@@ -1607,6 +1607,9 @@ struct block_device_operations { ...@@ -1607,6 +1607,9 @@ struct block_device_operations {
extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int, extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int,
unsigned long); unsigned long);
#else /* CONFIG_BLOCK */ #else /* CONFIG_BLOCK */
struct block_device;
/* /*
* stubs for when the block layer is configured out * stubs for when the block layer is configured out
*/ */
...@@ -1642,6 +1645,12 @@ static inline bool blk_needs_flush_plug(struct task_struct *tsk) ...@@ -1642,6 +1645,12 @@ static inline bool blk_needs_flush_plug(struct task_struct *tsk)
return false; return false;
} }
static inline int blkdev_issue_flush(struct block_device *bdev, gfp_t gfp_mask,
sector_t *error_sector)
{
return 0;
}
#endif /* CONFIG_BLOCK */ #endif /* CONFIG_BLOCK */
#endif #endif
...@@ -2590,6 +2590,7 @@ extern ssize_t simple_read_from_buffer(void __user *to, size_t count, ...@@ -2590,6 +2590,7 @@ extern ssize_t simple_read_from_buffer(void __user *to, size_t count,
extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos, extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos,
const void __user *from, size_t count); const void __user *from, size_t count);
extern int __generic_file_fsync(struct file *, loff_t, loff_t, int);
extern int generic_file_fsync(struct file *, loff_t, loff_t, int); extern int generic_file_fsync(struct file *, loff_t, loff_t, int);
extern int generic_check_addressable(unsigned, u64); extern int generic_check_addressable(unsigned, u64);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment