Commit 2cbe5c76 authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Linus Torvalds

hpfs: remember free space

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: default avatarMikulas Patocka <mikulas@artax.karlin.mff.cuni.cz>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 602456bf
...@@ -8,6 +8,58 @@ ...@@ -8,6 +8,58 @@
#include "hpfs_fn.h" #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 * Check if a sector is allocated in bitmap
* This is really slow. Turned on only if chk==2 * 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 ...@@ -203,9 +255,15 @@ secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forwa
} }
sec = 0; sec = 0;
ret: ret:
if (sec) {
i = 0;
do
hpfs_claim_alloc(s, sec + i);
while (unlikely(++i < n));
}
if (sec && f_p) { if (sec && f_p) {
for (i = 0; i < forward; i++) { 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); hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i);
sec = 0; sec = 0;
break; break;
...@@ -228,6 +286,7 @@ static secno alloc_in_dirband(struct super_block *s, secno near) ...@@ -228,6 +286,7 @@ static secno alloc_in_dirband(struct super_block *s, secno near)
nr >>= 2; nr >>= 2;
sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0); sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
if (!sec) return 0; if (!sec) return 0;
hpfs_claim_dirband_alloc(s, sec);
return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start; return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start;
} }
...@@ -242,6 +301,7 @@ int hpfs_alloc_if_possible(struct super_block *s, secno sec) ...@@ -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))); bmp[(sec & 0x3fff) >> 5] &= cpu_to_le32(~(1 << (sec & 0x1f)));
hpfs_mark_4buffers_dirty(&qbh); hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh); hpfs_brelse4(&qbh);
hpfs_claim_alloc(s, sec);
return 1; return 1;
} }
hpfs_brelse4(&qbh); hpfs_brelse4(&qbh);
...@@ -275,6 +335,7 @@ void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n) ...@@ -275,6 +335,7 @@ void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
return; return;
} }
bmp[(sec & 0x3fff) >> 5] |= cpu_to_le32(1 << (sec & 0x1f)); bmp[(sec & 0x3fff) >> 5] |= cpu_to_le32(1 << (sec & 0x1f));
hpfs_claim_free(s, sec);
if (!--n) { if (!--n) {
hpfs_mark_4buffers_dirty(&qbh); hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh); hpfs_brelse4(&qbh);
...@@ -359,6 +420,7 @@ void hpfs_free_dnode(struct super_block *s, dnode_secno dno) ...@@ -359,6 +420,7 @@ void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
bmp[ssec >> 5] |= cpu_to_le32(1 << (ssec & 0x1f)); bmp[ssec >> 5] |= cpu_to_le32(1 << (ssec & 0x1f));
hpfs_mark_4buffers_dirty(&qbh); hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&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, ...@@ -366,7 +428,7 @@ struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
dnode_secno *dno, struct quad_buffer_head *qbh) dnode_secno *dno, struct quad_buffer_head *qbh)
{ {
struct dnode *d; 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 = alloc_in_dirband(s, near)))
if (!(*dno = hpfs_alloc_sector(s, near, 4, 0))) return NULL; if (!(*dno = hpfs_alloc_sector(s, near, 4, 0))) return NULL;
} else { } else {
......
...@@ -312,7 +312,7 @@ static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb) ...@@ -312,7 +312,7 @@ static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb)
__printf(2, 3) __printf(2, 3)
void hpfs_error(struct super_block *, const char *, ...); void hpfs_error(struct super_block *, const char *, ...);
int hpfs_stop_cycles(struct super_block *, int, int *, int *, 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) * local time (HPFS) to GMT (Unix)
......
...@@ -121,7 +121,7 @@ static void hpfs_put_super(struct super_block *s) ...@@ -121,7 +121,7 @@ static void hpfs_put_super(struct super_block *s)
call_rcu(&hpfs_sb(s)->rcu, lazy_free_sbi); 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; struct quad_buffer_head qbh;
unsigned long *bits; unsigned long *bits;
...@@ -129,7 +129,7 @@ unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno) ...@@ -129,7 +129,7 @@ unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
bits = hpfs_map_4sectors(s, secno, &qbh, 0); bits = hpfs_map_4sectors(s, secno, &qbh, 0);
if (!bits) if (!bits)
return 0; return (unsigned)-1;
count = bitmap_weight(bits, 2048 * BITS_PER_BYTE); count = bitmap_weight(bits, 2048 * BITS_PER_BYTE);
hpfs_brelse4(&qbh); hpfs_brelse4(&qbh);
return count; return count;
...@@ -144,30 +144,45 @@ static unsigned count_bitmaps(struct super_block *s) ...@@ -144,30 +144,45 @@ static unsigned count_bitmaps(struct super_block *s)
hpfs_prefetch_bitmap(s, n); hpfs_prefetch_bitmap(s, n);
} }
for (n = 0; n < n_bands; n++) { for (n = 0; n < n_bands; n++) {
unsigned c;
hpfs_prefetch_bitmap(s, n + COUNT_RD_AHEAD); 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; 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) static int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{ {
struct super_block *s = dentry->d_sb; struct super_block *s = dentry->d_sb;
struct hpfs_sb_info *sbi = hpfs_sb(s); struct hpfs_sb_info *sbi = hpfs_sb(s);
u64 id = huge_encode_dev(s->s_bdev->bd_dev); u64 id = huge_encode_dev(s->s_bdev->bd_dev);
hpfs_lock(s); 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 = count_bitmaps(s);
sbi->sb_n_free_dnodes = hpfs_count_one_bitmap(s, sbi->sb_dmap);
/*}*/
buf->f_type = s->s_magic; buf->f_type = s->s_magic;
buf->f_bsize = 512; buf->f_bsize = 512;
buf->f_blocks = sbi->sb_fs_size; buf->f_blocks = sbi->sb_fs_size;
buf->f_bfree = sbi->sb_n_free; buf->f_bfree = sbi->sb_n_free;
buf->f_bavail = sbi->sb_n_free; buf->f_bavail = sbi->sb_n_free;
buf->f_files = sbi->sb_dirband_size / 4; 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[0] = (u32)id;
buf->f_fsid.val[1] = (u32)(id >> 32); buf->f_fsid.val[1] = (u32)(id >> 32);
buf->f_namelen = 254; buf->f_namelen = 254;
......
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