hpfs: remember free space
authorMikulas Patocka <mikulas@artax.karlin.mff.cuni.cz>
Tue, 28 Jan 2014 23:10:44 +0000 (00:10 +0100)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 3 Feb 2014 00:24:07 +0000 (16:24 -0800)
Previously, hpfs scanned all bitmaps each time the user asked for free
space using statfs.  This patch changes it so that hpfs scans the
bitmaps only once, remembes the free space and on next invocation of
statfs it returns the value instantly.

New versions of wine are hammering on the statfs syscall very heavily,
making some games unplayable when they're stored on hpfs, with load
times in minutes.

This should be backported to the stable kernels because it fixes
user-visible problem (excessive level load times in wine).

Signed-off-by: Mikulas Patocka <mikulas@artax.karlin.mff.cuni.cz>
Cc: stable@vger.kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/hpfs/alloc.c
fs/hpfs/hpfs_fn.h
fs/hpfs/super.c

index cdb84a8380682b5f341138cb6f75e2754434e073..58b5106186d09dfc0757c70616fa43b2a7e12aee 100644 (file)
@@ -8,6 +8,58 @@
 
 #include "hpfs_fn.h"
 
+static void hpfs_claim_alloc(struct super_block *s, secno sec)
+{
+       struct hpfs_sb_info *sbi = hpfs_sb(s);
+       if (sbi->sb_n_free != (unsigned)-1) {
+               if (unlikely(!sbi->sb_n_free)) {
+                       hpfs_error(s, "free count underflow, allocating sector %08x", sec);
+                       sbi->sb_n_free = -1;
+                       return;
+               }
+               sbi->sb_n_free--;
+       }
+}
+
+static void hpfs_claim_free(struct super_block *s, secno sec)
+{
+       struct hpfs_sb_info *sbi = hpfs_sb(s);
+       if (sbi->sb_n_free != (unsigned)-1) {
+               if (unlikely(sbi->sb_n_free >= sbi->sb_fs_size)) {
+                       hpfs_error(s, "free count overflow, freeing sector %08x", sec);
+                       sbi->sb_n_free = -1;
+                       return;
+               }
+               sbi->sb_n_free++;
+       }
+}
+
+static void hpfs_claim_dirband_alloc(struct super_block *s, secno sec)
+{
+       struct hpfs_sb_info *sbi = hpfs_sb(s);
+       if (sbi->sb_n_free_dnodes != (unsigned)-1) {
+               if (unlikely(!sbi->sb_n_free_dnodes)) {
+                       hpfs_error(s, "dirband free count underflow, allocating sector %08x", sec);
+                       sbi->sb_n_free_dnodes = -1;
+                       return;
+               }
+               sbi->sb_n_free_dnodes--;
+       }
+}
+
+static void hpfs_claim_dirband_free(struct super_block *s, secno sec)
+{
+       struct hpfs_sb_info *sbi = hpfs_sb(s);
+       if (sbi->sb_n_free_dnodes != (unsigned)-1) {
+               if (unlikely(sbi->sb_n_free_dnodes >= sbi->sb_dirband_size / 4)) {
+                       hpfs_error(s, "dirband free count overflow, freeing sector %08x", sec);
+                       sbi->sb_n_free_dnodes = -1;
+                       return;
+               }
+               sbi->sb_n_free_dnodes++;
+       }
+}
+
 /*
  * Check if a sector is allocated in bitmap
  * This is really slow. Turned on only if chk==2
@@ -203,9 +255,15 @@ secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forwa
        }
        sec = 0;
        ret:
+       if (sec) {
+               i = 0;
+               do
+                       hpfs_claim_alloc(s, sec + i);
+               while (unlikely(++i < n));
+       }
        if (sec && f_p) {
                for (i = 0; i < forward; i++) {
-                       if (!hpfs_alloc_if_possible(s, sec + i + 1)) {
+                       if (!hpfs_alloc_if_possible(s, sec + n + i)) {
                                hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i);
                                sec = 0;
                                break;
@@ -228,6 +286,7 @@ static secno alloc_in_dirband(struct super_block *s, secno near)
        nr >>= 2;
        sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
        if (!sec) return 0;
+       hpfs_claim_dirband_alloc(s, sec);
        return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start;
 }
 
@@ -242,6 +301,7 @@ int hpfs_alloc_if_possible(struct super_block *s, secno sec)
                bmp[(sec & 0x3fff) >> 5] &= cpu_to_le32(~(1 << (sec & 0x1f)));
                hpfs_mark_4buffers_dirty(&qbh);
                hpfs_brelse4(&qbh);
+               hpfs_claim_alloc(s, sec);
                return 1;
        }
        hpfs_brelse4(&qbh);
@@ -275,6 +335,7 @@ void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
                return;
        }
        bmp[(sec & 0x3fff) >> 5] |= cpu_to_le32(1 << (sec & 0x1f));
+       hpfs_claim_free(s, sec);
        if (!--n) {
                hpfs_mark_4buffers_dirty(&qbh);
                hpfs_brelse4(&qbh);
@@ -359,6 +420,7 @@ void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
                bmp[ssec >> 5] |= cpu_to_le32(1 << (ssec & 0x1f));
                hpfs_mark_4buffers_dirty(&qbh);
                hpfs_brelse4(&qbh);
+               hpfs_claim_dirband_free(s, dno);
        }
 }
 
@@ -366,7 +428,7 @@ struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
                         dnode_secno *dno, struct quad_buffer_head *qbh)
 {
        struct dnode *d;
-       if (hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_dmap) > FREE_DNODES_ADD) {
+       if (hpfs_get_free_dnodes(s) > FREE_DNODES_ADD) {
                if (!(*dno = alloc_in_dirband(s, near)))
                        if (!(*dno = hpfs_alloc_sector(s, near, 4, 0))) return NULL;
        } else {
index 6797bf80f6e2e20d30c3a3752dadfee4b7648050..3ba49c080e42b32d7b266acba497d3b7d398ce06 100644 (file)
@@ -312,7 +312,7 @@ static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb)
 __printf(2, 3)
 void hpfs_error(struct super_block *, const char *, ...);
 int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *);
-unsigned hpfs_count_one_bitmap(struct super_block *, secno);
+unsigned hpfs_get_free_dnodes(struct super_block *);
 
 /*
  * local time (HPFS) to GMT (Unix)
index b8d01ef6f531752cc4c97e2164c2c4a9131c533a..4534ff688b767e56b08302b6cedb199cdafeff4d 100644 (file)
@@ -121,7 +121,7 @@ static void hpfs_put_super(struct super_block *s)
        call_rcu(&hpfs_sb(s)->rcu, lazy_free_sbi);
 }
 
-unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
+static unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
 {
        struct quad_buffer_head qbh;
        unsigned long *bits;
@@ -129,7 +129,7 @@ unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
 
        bits = hpfs_map_4sectors(s, secno, &qbh, 0);
        if (!bits)
-               return 0;
+               return (unsigned)-1;
        count = bitmap_weight(bits, 2048 * BITS_PER_BYTE);
        hpfs_brelse4(&qbh);
        return count;
@@ -144,30 +144,45 @@ static unsigned count_bitmaps(struct super_block *s)
                hpfs_prefetch_bitmap(s, n);
        }
        for (n = 0; n < n_bands; n++) {
+               unsigned c;
                hpfs_prefetch_bitmap(s, n + COUNT_RD_AHEAD);
-               count += hpfs_count_one_bitmap(s, le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[n]));
+               c = hpfs_count_one_bitmap(s, le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[n]));
+               if (c != (unsigned)-1)
+                       count += c;
        }
        return count;
 }
 
+unsigned hpfs_get_free_dnodes(struct super_block *s)
+{
+       struct hpfs_sb_info *sbi = hpfs_sb(s);
+       if (sbi->sb_n_free_dnodes == (unsigned)-1) {
+               unsigned c = hpfs_count_one_bitmap(s, sbi->sb_dmap);
+               if (c == (unsigned)-1)
+                       return 0;
+               sbi->sb_n_free_dnodes = c;
+       }
+       return sbi->sb_n_free_dnodes;
+}
+
 static int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
        struct super_block *s = dentry->d_sb;
        struct hpfs_sb_info *sbi = hpfs_sb(s);
        u64 id = huge_encode_dev(s->s_bdev->bd_dev);
+
        hpfs_lock(s);
 
-       /*if (sbi->sb_n_free == -1) {*/
+       if (sbi->sb_n_free == (unsigned)-1)
                sbi->sb_n_free = count_bitmaps(s);
-               sbi->sb_n_free_dnodes = hpfs_count_one_bitmap(s, sbi->sb_dmap);
-       /*}*/
+
        buf->f_type = s->s_magic;
        buf->f_bsize = 512;
        buf->f_blocks = sbi->sb_fs_size;
        buf->f_bfree = sbi->sb_n_free;
        buf->f_bavail = sbi->sb_n_free;
        buf->f_files = sbi->sb_dirband_size / 4;
-       buf->f_ffree = sbi->sb_n_free_dnodes;
+       buf->f_ffree = hpfs_get_free_dnodes(s);
        buf->f_fsid.val[0] = (u32)id;
        buf->f_fsid.val[1] = (u32)(id >> 32);
        buf->f_namelen = 254;