fat: do not send discards as barriers
[linux-2.6.git] / fs / fat / fatent.c
index a1a9e04..f9a0b7a 100644 (file)
@@ -6,6 +6,8 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/msdos_fs.h>
+#include <linux/blkdev.h>
+#include "fat.h"
 
 struct fatent_operations {
        void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
@@ -17,6 +19,8 @@ struct fatent_operations {
        int (*ent_next)(struct fat_entry *);
 };
 
+static DEFINE_SPINLOCK(fat12_entry_lock);
+
 static void fat12_ent_blocknr(struct super_block *sb, int entry,
                              int *offset, sector_t *blocknr)
 {
@@ -69,6 +73,8 @@ static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
        struct buffer_head **bhs = fatent->bhs;
 
        WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
+       fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
+
        bhs[0] = sb_bread(sb, blocknr);
        if (!bhs[0])
                goto err;
@@ -89,8 +95,7 @@ static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
 err_brelse:
        brelse(bhs[0]);
 err:
-       printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
-              (unsigned long long)blocknr);
+       printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n", (llu)blocknr);
        return -EIO;
 }
 
@@ -100,10 +105,11 @@ static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent,
        struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
 
        WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
+       fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
        fatent->bhs[0] = sb_bread(sb, blocknr);
        if (!fatent->bhs[0]) {
                printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
-                      (unsigned long long)blocknr);
+                      (llu)blocknr);
                return -EIO;
        }
        fatent->nr_bhs = 1;
@@ -116,10 +122,13 @@ static int fat12_ent_get(struct fat_entry *fatent)
        u8 **ent12_p = fatent->u.ent12_p;
        int next;
 
+       spin_lock(&fat12_entry_lock);
        if (fatent->entry & 1)
                next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4);
        else
                next = (*ent12_p[1] << 8) | *ent12_p[0];
+       spin_unlock(&fat12_entry_lock);
+
        next &= 0x0fff;
        if (next >= BAD_FAT12)
                next = FAT_ENT_EOF;
@@ -151,6 +160,7 @@ static void fat12_ent_put(struct fat_entry *fatent, int new)
        if (new == FAT_ENT_EOF)
                new = EOF_FAT12;
 
+       spin_lock(&fat12_entry_lock);
        if (fatent->entry & 1) {
                *ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f);
                *ent12_p[1] = new >> 4;
@@ -158,10 +168,11 @@ static void fat12_ent_put(struct fat_entry *fatent, int new)
                *ent12_p[0] = new & 0xff;
                *ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8);
        }
+       spin_unlock(&fat12_entry_lock);
 
-       mark_buffer_dirty(fatent->bhs[0]);
+       mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
        if (fatent->nr_bhs == 2)
-               mark_buffer_dirty(fatent->bhs[1]);
+               mark_buffer_dirty_inode(fatent->bhs[1], fatent->fat_inode);
 }
 
 static void fat16_ent_put(struct fat_entry *fatent, int new)
@@ -170,7 +181,7 @@ static void fat16_ent_put(struct fat_entry *fatent, int new)
                new = EOF_FAT16;
 
        *fatent->u.ent16_p = cpu_to_le16(new);
-       mark_buffer_dirty(fatent->bhs[0]);
+       mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
 }
 
 static void fat32_ent_put(struct fat_entry *fatent, int new)
@@ -181,7 +192,7 @@ static void fat32_ent_put(struct fat_entry *fatent, int new)
        WARN_ON(new & 0xf0000000);
        new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff;
        *fatent->u.ent32_p = cpu_to_le32(new);
-       mark_buffer_dirty(fatent->bhs[0]);
+       mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
 }
 
 static int fat12_ent_next(struct fat_entry *fatent)
@@ -267,19 +278,19 @@ static struct fatent_operations fat32_ops = {
 
 static inline void lock_fat(struct msdos_sb_info *sbi)
 {
-       down(&sbi->fat_lock);
+       mutex_lock(&sbi->fat_lock);
 }
 
 static inline void unlock_fat(struct msdos_sb_info *sbi)
 {
-       up(&sbi->fat_lock);
+       mutex_unlock(&sbi->fat_lock);
 }
 
 void fat_ent_access_init(struct super_block *sb)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
 
-       init_MUTEX(&sbi->fat_lock);
+       mutex_init(&sbi->fat_lock);
 
        switch (sbi->fat_bits) {
        case 32:
@@ -308,10 +319,20 @@ static inline int fat_ent_update_ptr(struct super_block *sb,
        /* Is this fatent's blocks including this entry? */
        if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr)
                return 0;
-       /* Does this entry need the next block? */
-       if (sbi->fat_bits == 12 && (offset + 1) >= sb->s_blocksize) {
-               if (fatent->nr_bhs != 2 || bhs[1]->b_blocknr != (blocknr + 1))
-                       return 0;
+       if (sbi->fat_bits == 12) {
+               if ((offset + 1) < sb->s_blocksize) {
+                       /* This entry is on bhs[0]. */
+                       if (fatent->nr_bhs == 2) {
+                               brelse(bhs[1]);
+                               fatent->nr_bhs = 1;
+                       }
+               } else {
+                       /* This entry needs the next block. */
+                       if (fatent->nr_bhs != 2)
+                               return 0;
+                       if (bhs[1]->b_blocknr != (blocknr + 1))
+                               return 0;
+               }
        }
        ops->ent_set_ptr(fatent, offset);
        return 1;
@@ -327,7 +348,7 @@ int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry)
 
        if (entry < FAT_START_ENT || sbi->max_cluster <= entry) {
                fatent_brelse(fatent);
-               fat_fs_panic(sb, "invalid access to FAT (entry 0x%08x)", entry);
+               fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry);
                return -EIO;
        }
 
@@ -363,7 +384,7 @@ static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
                        }
                        memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
                        set_buffer_uptodate(c_bh);
-                       mark_buffer_dirty(c_bh);
+                       mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
                        if (sb->s_flags & MS_SYNCHRONOUS)
                                err = sync_dirty_buffer(c_bh);
                        brelse(c_bh);
@@ -443,7 +464,8 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
        BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2));    /* fixed limit */
 
        lock_fat(sbi);
-       if (sbi->free_clusters != -1 && sbi->free_clusters < nr_cluster) {
+       if (sbi->free_clusters != -1 && sbi->free_clus_valid &&
+           sbi->free_clusters < nr_cluster) {
                unlock_fat(sbi);
                return -ENOSPC;
        }
@@ -497,6 +519,7 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
 
        /* Couldn't allocate the free entries */
        sbi->free_clusters = 0;
+       sbi->free_clus_valid = 1;
        sb->s_dirt = 1;
        err = -ENOSPC;
 
@@ -526,6 +549,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
        struct fat_entry fatent;
        struct buffer_head *bhs[MAX_BUF_PER_PAGE];
        int i, err, nr_bhs;
+       int first_cl = cluster;
 
        nr_bhs = 0;
        fatent_init(&fatent);
@@ -536,12 +560,31 @@ int fat_free_clusters(struct inode *inode, int cluster)
                        err = cluster;
                        goto error;
                } else if (cluster == FAT_ENT_FREE) {
-                       fat_fs_panic(sb, "%s: deleting FAT entry beyond EOF",
-                                    __FUNCTION__);
+                       fat_fs_error(sb, "%s: deleting FAT entry beyond EOF",
+                                    __func__);
                        err = -EIO;
                        goto error;
                }
 
+               if (sbi->options.discard) {
+                       /*
+                        * Issue discard for the sectors we no longer
+                        * care about, batching contiguous clusters
+                        * into one request
+                        */
+                       if (cluster != fatent.entry + 1) {
+                               int nr_clus = fatent.entry - first_cl + 1;
+
+                               sb_issue_discard(sb,
+                                       fat_clus_to_blknr(sbi, first_cl),
+                                       nr_clus * sbi->sec_per_clus,
+                                       GFP_NOFS,
+                                       BLKDEV_IFL_WAIT);
+
+                               first_cl = cluster;
+                       }
+               }
+
                ops->ent_put(&fatent, FAT_ENT_FREE);
                if (sbi->free_clusters != -1) {
                        sbi->free_clusters++;
@@ -576,28 +619,54 @@ error:
                brelse(bhs[i]);
        unlock_fat(sbi);
 
-       fat_clusters_flush(sb);
-
        return err;
 }
 
 EXPORT_SYMBOL_GPL(fat_free_clusters);
 
+/* 128kb is the whole sectors for FAT12 and FAT16 */
+#define FAT_READA_SIZE         (128 * 1024)
+
+static void fat_ent_reada(struct super_block *sb, struct fat_entry *fatent,
+                         unsigned long reada_blocks)
+{
+       struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
+       sector_t blocknr;
+       int i, offset;
+
+       ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr);
+
+       for (i = 0; i < reada_blocks; i++)
+               sb_breadahead(sb, blocknr + i);
+}
+
 int fat_count_free_clusters(struct super_block *sb)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        struct fatent_operations *ops = sbi->fatent_ops;
        struct fat_entry fatent;
+       unsigned long reada_blocks, reada_mask, cur_block;
        int err = 0, free;
 
        lock_fat(sbi);
-       if (sbi->free_clusters != -1)
+       if (sbi->free_clusters != -1 && sbi->free_clus_valid)
                goto out;
 
+       reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits;
+       reada_mask = reada_blocks - 1;
+       cur_block = 0;
+
        free = 0;
        fatent_init(&fatent);
        fatent_set_entry(&fatent, FAT_START_ENT);
        while (fatent.entry < sbi->max_cluster) {
+               /* readahead of fat blocks */
+               if ((cur_block & reada_mask) == 0) {
+                       unsigned long rest = sbi->fat_length - cur_block;
+                       fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
+               }
+               cur_block++;
+
                err = fat_ent_read_block(sb, &fatent);
                if (err)
                        goto out;
@@ -608,6 +677,7 @@ int fat_count_free_clusters(struct super_block *sb)
                } while (fat_ent_next(sbi, &fatent));
        }
        sbi->free_clusters = free;
+       sbi->free_clus_valid = 1;
        sb->s_dirt = 1;
        fatent_brelse(&fatent);
 out: