Commit ae78bf9c authored by Chris Mason's avatar Chris Mason Committed by Linus Torvalds

[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: default avatarChris Mason <mason@suse.com>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6b77df08
...@@ -13,6 +13,7 @@ ...@@ -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, ...@@ -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 = { ...@@ -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) ...@@ -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 @@ ...@@ -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 { ...@@ -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 = { ...@@ -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, ...@@ -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 @@ out_fail: ...@@ -1425,6 +1430,56 @@ out_fail:
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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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 { ...@@ -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); ...@@ -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);
......
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