Skip to content

Commit

Permalink
hpfs: remember free space
Browse files Browse the repository at this point in the history
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 <[email protected]>
Cc: [email protected]
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Mikulas Patocka authored and torvalds committed Feb 3, 2014
1 parent 602456b commit 2cbe5c7
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 10 deletions.
66 changes: 64 additions & 2 deletions fs/hpfs/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -359,14 +420,15 @@ 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);
}
}

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 {
Expand Down
2 changes: 1 addition & 1 deletion fs/hpfs/hpfs_fn.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
29 changes: 22 additions & 7 deletions fs/hpfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ 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;
unsigned count;

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;
Expand All @@ -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;
Expand Down

0 comments on commit 2cbe5c7

Please sign in to comment.