diff options
author | Chris Mason <mason@suse.com> | 2006-09-29 05:00:03 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-29 12:18:12 -0400 |
commit | ae78bf9c4f5fde3c67e2829505f195d7347ce3e4 (patch) | |
tree | b9376237e596ed7e95db35b9ccf1ad3177b10460 | |
parent | 6b77df08a36d989f7dd00ccb6a026a0e96170d16 (diff) |
[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>
-rw-r--r-- | fs/fat/file.c | 13 | ||||
-rw-r--r-- | fs/fat/inode.c | 59 | ||||
-rw-r--r-- | fs/msdos/namei.c | 11 | ||||
-rw-r--r-- | include/linux/msdos_fs.h | 3 |
4 files changed, 83 insertions, 3 deletions
diff --git a/fs/fat/file.c b/fs/fat/file.c index 1ee25232e6af..d50fc47169c1 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/smp_lock.h> | 13 | #include <linux/smp_lock.h> |
14 | #include <linux/buffer_head.h> | 14 | #include <linux/buffer_head.h> |
15 | #include <linux/writeback.h> | 15 | #include <linux/writeback.h> |
16 | #include <linux/blkdev.h> | ||
16 | 17 | ||
17 | int fat_generic_ioctl(struct inode *inode, struct file *filp, | 18 | int fat_generic_ioctl(struct inode *inode, struct file *filp, |
18 | unsigned int cmd, unsigned long arg) | 19 | unsigned int cmd, unsigned long arg) |
@@ -112,6 +113,16 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp, | |||
112 | } | 113 | } |
113 | } | 114 | } |
114 | 115 | ||
116 | static int fat_file_release(struct inode *inode, struct file *filp) | ||
117 | { | ||
118 | if ((filp->f_mode & FMODE_WRITE) && | ||
119 | MSDOS_SB(inode->i_sb)->options.flush) { | ||
120 | fat_flush_inodes(inode->i_sb, inode, NULL); | ||
121 | blk_congestion_wait(WRITE, HZ/10); | ||
122 | } | ||
123 | return 0; | ||
124 | } | ||
125 | |||
115 | const struct file_operations fat_file_operations = { | 126 | const struct file_operations fat_file_operations = { |
116 | .llseek = generic_file_llseek, | 127 | .llseek = generic_file_llseek, |
117 | .read = do_sync_read, | 128 | .read = do_sync_read, |
@@ -121,6 +132,7 @@ const struct file_operations fat_file_operations = { | |||
121 | .aio_read = generic_file_aio_read, | 132 | .aio_read = generic_file_aio_read, |
122 | .aio_write = generic_file_aio_write, | 133 | .aio_write = generic_file_aio_write, |
123 | .mmap = generic_file_mmap, | 134 | .mmap = generic_file_mmap, |
135 | .release = fat_file_release, | ||
124 | .ioctl = fat_generic_ioctl, | 136 | .ioctl = fat_generic_ioctl, |
125 | .fsync = file_fsync, | 137 | .fsync = file_fsync, |
126 | .sendfile = generic_file_sendfile, | 138 | .sendfile = generic_file_sendfile, |
@@ -289,6 +301,7 @@ void fat_truncate(struct inode *inode) | |||
289 | lock_kernel(); | 301 | lock_kernel(); |
290 | fat_free(inode, nr_clusters); | 302 | fat_free(inode, nr_clusters); |
291 | unlock_kernel(); | 303 | unlock_kernel(); |
304 | fat_flush_inodes(inode->i_sb, inode, NULL); | ||
292 | } | 305 | } |
293 | 306 | ||
294 | struct inode_operations fat_file_inode_operations = { | 307 | struct inode_operations fat_file_inode_operations = { |
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index ab96ae823753..045738032a83 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/vfs.h> | 24 | #include <linux/vfs.h> |
25 | #include <linux/parser.h> | 25 | #include <linux/parser.h> |
26 | #include <linux/uio.h> | 26 | #include <linux/uio.h> |
27 | #include <linux/writeback.h> | ||
27 | #include <asm/unaligned.h> | 28 | #include <asm/unaligned.h> |
28 | 29 | ||
29 | #ifndef CONFIG_FAT_DEFAULT_IOCHARSET | 30 | #ifndef CONFIG_FAT_DEFAULT_IOCHARSET |
@@ -853,7 +854,7 @@ enum { | |||
853 | Opt_charset, Opt_shortname_lower, Opt_shortname_win95, | 854 | Opt_charset, Opt_shortname_lower, Opt_shortname_win95, |
854 | Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, | 855 | Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, |
855 | Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, | 856 | Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, |
856 | Opt_obsolate, Opt_err, | 857 | Opt_obsolate, Opt_flush, Opt_err, |
857 | }; | 858 | }; |
858 | 859 | ||
859 | static match_table_t fat_tokens = { | 860 | static match_table_t fat_tokens = { |
@@ -885,7 +886,8 @@ static match_table_t fat_tokens = { | |||
885 | {Opt_obsolate, "cvf_format=%20s"}, | 886 | {Opt_obsolate, "cvf_format=%20s"}, |
886 | {Opt_obsolate, "cvf_options=%100s"}, | 887 | {Opt_obsolate, "cvf_options=%100s"}, |
887 | {Opt_obsolate, "posix"}, | 888 | {Opt_obsolate, "posix"}, |
888 | {Opt_err, NULL} | 889 | {Opt_flush, "flush"}, |
890 | {Opt_err, NULL}, | ||
889 | }; | 891 | }; |
890 | static match_table_t msdos_tokens = { | 892 | static match_table_t msdos_tokens = { |
891 | {Opt_nodots, "nodots"}, | 893 | {Opt_nodots, "nodots"}, |
@@ -1026,6 +1028,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, | |||
1026 | return 0; | 1028 | return 0; |
1027 | opts->codepage = option; | 1029 | opts->codepage = option; |
1028 | break; | 1030 | break; |
1031 | case Opt_flush: | ||
1032 | opts->flush = 1; | ||
1033 | break; | ||
1029 | 1034 | ||
1030 | /* msdos specific */ | 1035 | /* msdos specific */ |
1031 | case Opt_dots: | 1036 | case Opt_dots: |
@@ -1425,6 +1430,56 @@ out_fail: | |||
1425 | 1430 | ||
1426 | EXPORT_SYMBOL_GPL(fat_fill_super); | 1431 | EXPORT_SYMBOL_GPL(fat_fill_super); |
1427 | 1432 | ||
1433 | /* | ||
1434 | * helper function for fat_flush_inodes. This writes both the inode | ||
1435 | * and the file data blocks, waiting for in flight data blocks before | ||
1436 | * the start of the call. It does not wait for any io started | ||
1437 | * during the call | ||
1438 | */ | ||
1439 | static int writeback_inode(struct inode *inode) | ||
1440 | { | ||
1441 | |||
1442 | int ret; | ||
1443 | struct address_space *mapping = inode->i_mapping; | ||
1444 | struct writeback_control wbc = { | ||
1445 | .sync_mode = WB_SYNC_NONE, | ||
1446 | .nr_to_write = 0, | ||
1447 | }; | ||
1448 | /* if we used WB_SYNC_ALL, sync_inode waits for the io for the | ||
1449 | * inode to finish. So WB_SYNC_NONE is sent down to sync_inode | ||
1450 | * and filemap_fdatawrite is used for the data blocks | ||
1451 | */ | ||
1452 | ret = sync_inode(inode, &wbc); | ||
1453 | if (!ret) | ||
1454 | ret = filemap_fdatawrite(mapping); | ||
1455 | return ret; | ||
1456 | } | ||
1457 | |||
1458 | /* | ||
1459 | * write data and metadata corresponding to i1 and i2. The io is | ||
1460 | * started but we do not wait for any of it to finish. | ||
1461 | * | ||
1462 | * filemap_flush is used for the block device, so if there is a dirty | ||
1463 | * page for a block already in flight, we will not wait and start the | ||
1464 | * io over again | ||
1465 | */ | ||
1466 | int fat_flush_inodes(struct super_block *sb, struct inode *i1, struct inode *i2) | ||
1467 | { | ||
1468 | int ret = 0; | ||
1469 | if (!MSDOS_SB(sb)->options.flush) | ||
1470 | return 0; | ||
1471 | if (i1) | ||
1472 | ret = writeback_inode(i1); | ||
1473 | if (!ret && i2) | ||
1474 | ret = writeback_inode(i2); | ||
1475 | if (!ret && sb) { | ||
1476 | struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; | ||
1477 | ret = filemap_flush(mapping); | ||
1478 | } | ||
1479 | return ret; | ||
1480 | } | ||
1481 | EXPORT_SYMBOL_GPL(fat_flush_inodes); | ||
1482 | |||
1428 | static int __init init_fat_fs(void) | 1483 | static int __init init_fat_fs(void) |
1429 | { | 1484 | { |
1430 | int err; | 1485 | int err; |
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 9e44158a7540..d220165d4918 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c | |||
@@ -280,7 +280,7 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, int mode, | |||
280 | struct nameidata *nd) | 280 | struct nameidata *nd) |
281 | { | 281 | { |
282 | struct super_block *sb = dir->i_sb; | 282 | struct super_block *sb = dir->i_sb; |
283 | struct inode *inode; | 283 | struct inode *inode = NULL; |
284 | struct fat_slot_info sinfo; | 284 | struct fat_slot_info sinfo; |
285 | struct timespec ts; | 285 | struct timespec ts; |
286 | unsigned char msdos_name[MSDOS_NAME]; | 286 | unsigned char msdos_name[MSDOS_NAME]; |
@@ -316,6 +316,8 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, int mode, | |||
316 | d_instantiate(dentry, inode); | 316 | d_instantiate(dentry, inode); |
317 | out: | 317 | out: |
318 | unlock_kernel(); | 318 | unlock_kernel(); |
319 | if (!err) | ||
320 | err = fat_flush_inodes(sb, dir, inode); | ||
319 | return err; | 321 | return err; |
320 | } | 322 | } |
321 | 323 | ||
@@ -348,6 +350,8 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry) | |||
348 | fat_detach(inode); | 350 | fat_detach(inode); |
349 | out: | 351 | out: |
350 | unlock_kernel(); | 352 | unlock_kernel(); |
353 | if (!err) | ||
354 | err = fat_flush_inodes(inode->i_sb, dir, inode); | ||
351 | 355 | ||
352 | return err; | 356 | return err; |
353 | } | 357 | } |
@@ -401,6 +405,7 @@ static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
401 | d_instantiate(dentry, inode); | 405 | d_instantiate(dentry, inode); |
402 | 406 | ||
403 | unlock_kernel(); | 407 | unlock_kernel(); |
408 | fat_flush_inodes(sb, dir, inode); | ||
404 | return 0; | 409 | return 0; |
405 | 410 | ||
406 | out_free: | 411 | out_free: |
@@ -430,6 +435,8 @@ static int msdos_unlink(struct inode *dir, struct dentry *dentry) | |||
430 | fat_detach(inode); | 435 | fat_detach(inode); |
431 | out: | 436 | out: |
432 | unlock_kernel(); | 437 | unlock_kernel(); |
438 | if (!err) | ||
439 | err = fat_flush_inodes(inode->i_sb, dir, inode); | ||
433 | 440 | ||
434 | return err; | 441 | return err; |
435 | } | 442 | } |
@@ -635,6 +642,8 @@ static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
635 | new_dir, new_msdos_name, new_dentry, is_hid); | 642 | new_dir, new_msdos_name, new_dentry, is_hid); |
636 | out: | 643 | out: |
637 | unlock_kernel(); | 644 | unlock_kernel(); |
645 | if (!err) | ||
646 | err = fat_flush_inodes(old_dir->i_sb, old_dir, new_dir); | ||
638 | return err; | 647 | return err; |
639 | } | 648 | } |
640 | 649 | ||
diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index bae62d62dc3e..ce6c85815cbd 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h | |||
@@ -204,6 +204,7 @@ struct fat_mount_options { | |||
204 | unicode_xlate:1, /* create escape sequences for unhandled Unicode */ | 204 | unicode_xlate:1, /* create escape sequences for unhandled Unicode */ |
205 | numtail:1, /* Does first alias have a numeric '~1' type tail? */ | 205 | numtail:1, /* Does first alias have a numeric '~1' type tail? */ |
206 | atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */ | 206 | atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */ |
207 | flush:1, /* write things quickly */ | ||
207 | nocase:1; /* Does this need case conversion? 0=need case conversion*/ | 208 | nocase:1; /* Does this need case conversion? 0=need case conversion*/ |
208 | }; | 209 | }; |
209 | 210 | ||
@@ -412,6 +413,8 @@ extern int fat_sync_inode(struct inode *inode); | |||
412 | extern int fat_fill_super(struct super_block *sb, void *data, int silent, | 413 | extern int fat_fill_super(struct super_block *sb, void *data, int silent, |
413 | struct inode_operations *fs_dir_inode_ops, int isvfat); | 414 | struct inode_operations *fs_dir_inode_ops, int isvfat); |
414 | 415 | ||
416 | extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, | ||
417 | struct inode *i2); | ||
415 | /* fat/misc.c */ | 418 | /* fat/misc.c */ |
416 | extern void fat_fs_panic(struct super_block *s, const char *fmt, ...); | 419 | extern void fat_fs_panic(struct super_block *s, const char *fmt, ...); |
417 | extern void fat_clusters_flush(struct super_block *sb); | 420 | extern void fat_clusters_flush(struct super_block *sb); |