[PATCH] add -o flush for fat
Fat is commonly used on removable media. Mounting with -o flush tells the FS to write things to disk as quickly as possible. It is like -o sync, but much faster (and not as safe). Signed-off-by: Chris Mason <mason@suse.com> Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
6b77df08a3
commit
ae78bf9c4f
@ -13,6 +13,7 @@
|
|||||||
#include <linux/smp_lock.h>
|
#include <linux/smp_lock.h>
|
||||||
#include <linux/buffer_head.h>
|
#include <linux/buffer_head.h>
|
||||||
#include <linux/writeback.h>
|
#include <linux/writeback.h>
|
||||||
|
#include <linux/blkdev.h>
|
||||||
|
|
||||||
int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned int cmd, unsigned long arg)
|
||||||
@ -112,6 +113,16 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fat_file_release(struct inode *inode, struct file *filp)
|
||||||
|
{
|
||||||
|
if ((filp->f_mode & FMODE_WRITE) &&
|
||||||
|
MSDOS_SB(inode->i_sb)->options.flush) {
|
||||||
|
fat_flush_inodes(inode->i_sb, inode, NULL);
|
||||||
|
blk_congestion_wait(WRITE, HZ/10);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const struct file_operations fat_file_operations = {
|
const struct file_operations fat_file_operations = {
|
||||||
.llseek = generic_file_llseek,
|
.llseek = generic_file_llseek,
|
||||||
.read = do_sync_read,
|
.read = do_sync_read,
|
||||||
@ -121,6 +132,7 @@ const struct file_operations fat_file_operations = {
|
|||||||
.aio_read = generic_file_aio_read,
|
.aio_read = generic_file_aio_read,
|
||||||
.aio_write = generic_file_aio_write,
|
.aio_write = generic_file_aio_write,
|
||||||
.mmap = generic_file_mmap,
|
.mmap = generic_file_mmap,
|
||||||
|
.release = fat_file_release,
|
||||||
.ioctl = fat_generic_ioctl,
|
.ioctl = fat_generic_ioctl,
|
||||||
.fsync = file_fsync,
|
.fsync = file_fsync,
|
||||||
.sendfile = generic_file_sendfile,
|
.sendfile = generic_file_sendfile,
|
||||||
@ -289,6 +301,7 @@ void fat_truncate(struct inode *inode)
|
|||||||
lock_kernel();
|
lock_kernel();
|
||||||
fat_free(inode, nr_clusters);
|
fat_free(inode, nr_clusters);
|
||||||
unlock_kernel();
|
unlock_kernel();
|
||||||
|
fat_flush_inodes(inode->i_sb, inode, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct inode_operations fat_file_inode_operations = {
|
struct inode_operations fat_file_inode_operations = {
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <linux/vfs.h>
|
#include <linux/vfs.h>
|
||||||
#include <linux/parser.h>
|
#include <linux/parser.h>
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
|
#include <linux/writeback.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
#ifndef CONFIG_FAT_DEFAULT_IOCHARSET
|
#ifndef CONFIG_FAT_DEFAULT_IOCHARSET
|
||||||
@ -853,7 +854,7 @@ enum {
|
|||||||
Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
|
Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
|
||||||
Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
|
Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
|
||||||
Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
|
Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
|
||||||
Opt_obsolate, Opt_err,
|
Opt_obsolate, Opt_flush, Opt_err,
|
||||||
};
|
};
|
||||||
|
|
||||||
static match_table_t fat_tokens = {
|
static match_table_t fat_tokens = {
|
||||||
@ -885,7 +886,8 @@ static match_table_t fat_tokens = {
|
|||||||
{Opt_obsolate, "cvf_format=%20s"},
|
{Opt_obsolate, "cvf_format=%20s"},
|
||||||
{Opt_obsolate, "cvf_options=%100s"},
|
{Opt_obsolate, "cvf_options=%100s"},
|
||||||
{Opt_obsolate, "posix"},
|
{Opt_obsolate, "posix"},
|
||||||
{Opt_err, NULL}
|
{Opt_flush, "flush"},
|
||||||
|
{Opt_err, NULL},
|
||||||
};
|
};
|
||||||
static match_table_t msdos_tokens = {
|
static match_table_t msdos_tokens = {
|
||||||
{Opt_nodots, "nodots"},
|
{Opt_nodots, "nodots"},
|
||||||
@ -1026,6 +1028,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
|
|||||||
return 0;
|
return 0;
|
||||||
opts->codepage = option;
|
opts->codepage = option;
|
||||||
break;
|
break;
|
||||||
|
case Opt_flush:
|
||||||
|
opts->flush = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
/* msdos specific */
|
/* msdos specific */
|
||||||
case Opt_dots:
|
case Opt_dots:
|
||||||
@ -1425,6 +1430,56 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
|
|||||||
|
|
||||||
EXPORT_SYMBOL_GPL(fat_fill_super);
|
EXPORT_SYMBOL_GPL(fat_fill_super);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* helper function for fat_flush_inodes. This writes both the inode
|
||||||
|
* and the file data blocks, waiting for in flight data blocks before
|
||||||
|
* the start of the call. It does not wait for any io started
|
||||||
|
* during the call
|
||||||
|
*/
|
||||||
|
static int writeback_inode(struct inode *inode)
|
||||||
|
{
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
struct address_space *mapping = inode->i_mapping;
|
||||||
|
struct writeback_control wbc = {
|
||||||
|
.sync_mode = WB_SYNC_NONE,
|
||||||
|
.nr_to_write = 0,
|
||||||
|
};
|
||||||
|
/* if we used WB_SYNC_ALL, sync_inode waits for the io for the
|
||||||
|
* inode to finish. So WB_SYNC_NONE is sent down to sync_inode
|
||||||
|
* and filemap_fdatawrite is used for the data blocks
|
||||||
|
*/
|
||||||
|
ret = sync_inode(inode, &wbc);
|
||||||
|
if (!ret)
|
||||||
|
ret = filemap_fdatawrite(mapping);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* write data and metadata corresponding to i1 and i2. The io is
|
||||||
|
* started but we do not wait for any of it to finish.
|
||||||
|
*
|
||||||
|
* filemap_flush is used for the block device, so if there is a dirty
|
||||||
|
* page for a block already in flight, we will not wait and start the
|
||||||
|
* io over again
|
||||||
|
*/
|
||||||
|
int fat_flush_inodes(struct super_block *sb, struct inode *i1, struct inode *i2)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
if (!MSDOS_SB(sb)->options.flush)
|
||||||
|
return 0;
|
||||||
|
if (i1)
|
||||||
|
ret = writeback_inode(i1);
|
||||||
|
if (!ret && i2)
|
||||||
|
ret = writeback_inode(i2);
|
||||||
|
if (!ret && sb) {
|
||||||
|
struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
|
||||||
|
ret = filemap_flush(mapping);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fat_flush_inodes);
|
||||||
|
|
||||||
static int __init init_fat_fs(void)
|
static int __init init_fat_fs(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -280,7 +280,7 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
|
|||||||
struct nameidata *nd)
|
struct nameidata *nd)
|
||||||
{
|
{
|
||||||
struct super_block *sb = dir->i_sb;
|
struct super_block *sb = dir->i_sb;
|
||||||
struct inode *inode;
|
struct inode *inode = NULL;
|
||||||
struct fat_slot_info sinfo;
|
struct fat_slot_info sinfo;
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
unsigned char msdos_name[MSDOS_NAME];
|
unsigned char msdos_name[MSDOS_NAME];
|
||||||
@ -316,6 +316,8 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
|
|||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
out:
|
out:
|
||||||
unlock_kernel();
|
unlock_kernel();
|
||||||
|
if (!err)
|
||||||
|
err = fat_flush_inodes(sb, dir, inode);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,6 +350,8 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
|
|||||||
fat_detach(inode);
|
fat_detach(inode);
|
||||||
out:
|
out:
|
||||||
unlock_kernel();
|
unlock_kernel();
|
||||||
|
if (!err)
|
||||||
|
err = fat_flush_inodes(inode->i_sb, dir, inode);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -401,6 +405,7 @@ static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
|
|
||||||
unlock_kernel();
|
unlock_kernel();
|
||||||
|
fat_flush_inodes(sb, dir, inode);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
@ -430,6 +435,8 @@ static int msdos_unlink(struct inode *dir, struct dentry *dentry)
|
|||||||
fat_detach(inode);
|
fat_detach(inode);
|
||||||
out:
|
out:
|
||||||
unlock_kernel();
|
unlock_kernel();
|
||||||
|
if (!err)
|
||||||
|
err = fat_flush_inodes(inode->i_sb, dir, inode);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -635,6 +642,8 @@ static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
new_dir, new_msdos_name, new_dentry, is_hid);
|
new_dir, new_msdos_name, new_dentry, is_hid);
|
||||||
out:
|
out:
|
||||||
unlock_kernel();
|
unlock_kernel();
|
||||||
|
if (!err)
|
||||||
|
err = fat_flush_inodes(old_dir->i_sb, old_dir, new_dir);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +204,7 @@ struct fat_mount_options {
|
|||||||
unicode_xlate:1, /* create escape sequences for unhandled Unicode */
|
unicode_xlate:1, /* create escape sequences for unhandled Unicode */
|
||||||
numtail:1, /* Does first alias have a numeric '~1' type tail? */
|
numtail:1, /* Does first alias have a numeric '~1' type tail? */
|
||||||
atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */
|
atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */
|
||||||
|
flush:1, /* write things quickly */
|
||||||
nocase:1; /* Does this need case conversion? 0=need case conversion*/
|
nocase:1; /* Does this need case conversion? 0=need case conversion*/
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -412,6 +413,8 @@ extern int fat_sync_inode(struct inode *inode);
|
|||||||
extern int fat_fill_super(struct super_block *sb, void *data, int silent,
|
extern int fat_fill_super(struct super_block *sb, void *data, int silent,
|
||||||
struct inode_operations *fs_dir_inode_ops, int isvfat);
|
struct inode_operations *fs_dir_inode_ops, int isvfat);
|
||||||
|
|
||||||
|
extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
|
||||||
|
struct inode *i2);
|
||||||
/* fat/misc.c */
|
/* fat/misc.c */
|
||||||
extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
|
extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
|
||||||
extern void fat_clusters_flush(struct super_block *sb);
|
extern void fat_clusters_flush(struct super_block *sb);
|
||||||
|
Loading…
Reference in New Issue
Block a user