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>
This commit is contained in:
parent
fd2916bd77
commit
ac13a829f6
34
fs/libfs.c
34
fs/libfs.c
@ -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,
|
|||||||
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,
|
|||||||
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 {
|
|||||||
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)
|
|||||||
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,
|
|||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user