#include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 #include <linux/writeback.h>
+#include <linux/blkdev.h>
 
 int fat_generic_ioctl(struct inode *inode, struct file *filp,
                      unsigned int cmd, unsigned long arg)
        }
 }
 
+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 = {
        .llseek         = generic_file_llseek,
        .read           = do_sync_read,
        .aio_read       = generic_file_aio_read,
        .aio_write      = generic_file_aio_write,
        .mmap           = generic_file_mmap,
+       .release        = fat_file_release,
        .ioctl          = fat_generic_ioctl,
        .fsync          = file_fsync,
        .sendfile       = generic_file_sendfile,
        lock_kernel();
        fat_free(inode, nr_clusters);
        unlock_kernel();
+       fat_flush_inodes(inode->i_sb, inode, NULL);
 }
 
 struct inode_operations fat_file_inode_operations = {
 
 #include <linux/vfs.h>
 #include <linux/parser.h>
 #include <linux/uio.h>
+#include <linux/writeback.h>
 #include <asm/unaligned.h>
 
 #ifndef CONFIG_FAT_DEFAULT_IOCHARSET
        Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
        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_obsolate, Opt_err,
+       Opt_obsolate, Opt_flush, Opt_err,
 };
 
 static match_table_t fat_tokens = {
        {Opt_obsolate, "cvf_format=%20s"},
        {Opt_obsolate, "cvf_options=%100s"},
        {Opt_obsolate, "posix"},
-       {Opt_err, NULL}
+       {Opt_flush, "flush"},
+       {Opt_err, NULL},
 };
 static match_table_t msdos_tokens = {
        {Opt_nodots, "nodots"},
                                return 0;
                        opts->codepage = option;
                        break;
+               case Opt_flush:
+                       opts->flush = 1;
+                       break;
 
                /* msdos specific */
                case Opt_dots:
 
 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)
 {
        int err;
 
                        struct nameidata *nd)
 {
        struct super_block *sb = dir->i_sb;
-       struct inode *inode;
+       struct inode *inode = NULL;
        struct fat_slot_info sinfo;
        struct timespec ts;
        unsigned char msdos_name[MSDOS_NAME];
        d_instantiate(dentry, inode);
 out:
        unlock_kernel();
+       if (!err)
+               err = fat_flush_inodes(sb, dir, inode);
        return err;
 }
 
        fat_detach(inode);
 out:
        unlock_kernel();
+       if (!err)
+               err = fat_flush_inodes(inode->i_sb, dir, inode);
 
        return err;
 }
        d_instantiate(dentry, inode);
 
        unlock_kernel();
+       fat_flush_inodes(sb, dir, inode);
        return 0;
 
 out_free:
        fat_detach(inode);
 out:
        unlock_kernel();
+       if (!err)
+               err = fat_flush_inodes(inode->i_sb, dir, inode);
 
        return err;
 }
                              new_dir, new_msdos_name, new_dentry, is_hid);
 out:
        unlock_kernel();
+       if (!err)
+               err = fat_flush_inodes(old_dir->i_sb, old_dir, new_dir);
        return err;
 }
 
 
                 unicode_xlate:1, /* create escape sequences for unhandled Unicode */
                 numtail:1,       /* Does first alias have a numeric '~1' type tail? */
                 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*/
 };
 
 extern int fat_fill_super(struct super_block *sb, void *data, int silent,
                        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 */
 extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
 extern void fat_clusters_flush(struct super_block *sb);