From 3ebacb05044f82c5f0bb456a894eb9dc57d0ed90 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 4 Jul 2013 18:42:29 +0200 Subject: hpfs: better test for errors The test if bitmap access is out of bound could errorneously pass if the device size is divisible by 16384 sectors and we are asking for one bitmap after the end. Check for invalid size in the superblock. Invalid size could cause integer overflows in the rest of the code. Signed-off-by: Mikulas Patocka Cc: stable@kernel.org Signed-off-by: Linus Torvalds --- fs/hpfs/super.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'fs/hpfs/super.c') diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index a0617e7069579..962e90c37aec6 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -558,7 +558,13 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) sbi->sb_cp_table = NULL; sbi->sb_c_bitmap = -1; sbi->sb_max_fwd_alloc = 0xffffff; - + + if (sbi->sb_fs_size >= 0x80000000) { + hpfs_error(s, "invalid size in superblock: %08x", + (unsigned)sbi->sb_fs_size); + goto bail4; + } + /* Load bitmap directory */ if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, le32_to_cpu(superblock->bitmaps)))) goto bail4; -- cgit v1.2.3 From 275f495dbe34300d793466a7d96c70f83fbae1bc Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 4 Jul 2013 19:04:01 +0200 Subject: hpfs: implement prefetch to improve performance This patch implements prefetch to improve performance. It helps mostly when scanning the bitmaps to calculate free space. Signed-off-by: Mikulas Patocka Signed-off-by: Linus Torvalds --- fs/hpfs/buffer.c | 33 +++++++++++++++++++++++++++++++++ fs/hpfs/hpfs_fn.h | 7 +++++-- fs/hpfs/map.c | 19 ++++++++++++++++++- fs/hpfs/super.c | 9 +++++++-- 4 files changed, 63 insertions(+), 5 deletions(-) (limited to 'fs/hpfs/super.c') diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c index f49d1498aa2e9..4d0a1afa058c8 100644 --- a/fs/hpfs/buffer.c +++ b/fs/hpfs/buffer.c @@ -7,8 +7,37 @@ */ #include #include +#include #include "hpfs_fn.h" +void hpfs_prefetch_sectors(struct super_block *s, unsigned secno, int n) +{ + struct buffer_head *bh; + struct blk_plug plug; + + if (n <= 0 || unlikely(secno >= hpfs_sb(s)->sb_fs_size)) + return; + + bh = sb_find_get_block(s, secno); + if (bh) { + if (buffer_uptodate(bh)) { + brelse(bh); + return; + } + brelse(bh); + }; + + blk_start_plug(&plug); + while (n > 0) { + if (unlikely(secno >= hpfs_sb(s)->sb_fs_size)) + break; + sb_breadahead(s, secno); + secno++; + n--; + } + blk_finish_plug(&plug); +} + /* Map a sector into a buffer and return pointers to it and to the buffer. */ void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp, @@ -18,6 +47,8 @@ void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head hpfs_lock_assert(s); + hpfs_prefetch_sectors(s, secno, ahead); + cond_resched(); *bhp = bh = sb_bread(s, secno); @@ -67,6 +98,8 @@ void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffe return NULL; } + hpfs_prefetch_sectors(s, secno, 4 + ahead); + qbh->data = data = kmalloc(2048, GFP_NOFS); if (!data) { printk("HPFS: hpfs_map_4sectors: out of memory\n"); diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index b7ae286646b55..1b398636e9902 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -27,8 +27,9 @@ #define ALLOC_FWD_MAX 128 #define ALLOC_M 1 #define FNODE_RD_AHEAD 16 -#define ANODE_RD_AHEAD 16 -#define DNODE_RD_AHEAD 4 +#define ANODE_RD_AHEAD 0 +#define DNODE_RD_AHEAD 72 +#define COUNT_RD_AHEAD 62 #define FREE_DNODES_ADD 58 #define FREE_DNODES_DEL 29 @@ -207,6 +208,7 @@ void hpfs_remove_fnode(struct super_block *, fnode_secno fno); /* buffer.c */ +void hpfs_prefetch_sectors(struct super_block *, unsigned, int); void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int); void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **); void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int); @@ -271,6 +273,7 @@ void hpfs_evict_inode(struct inode *); __le32 *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *); __le32 *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *); +void hpfs_prefetch_bitmap(struct super_block *, unsigned); unsigned char *hpfs_load_code_page(struct super_block *, secno); __le32 *hpfs_load_bitmap_directory(struct super_block *, secno bmp); struct fnode *hpfs_map_fnode(struct super_block *s, ino_t, struct buffer_head **); diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c index 803d3da3a0feb..3aa66ae1031e7 100644 --- a/fs/hpfs/map.c +++ b/fs/hpfs/map.c @@ -17,6 +17,7 @@ __le32 *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block, struct quad_buffer_head *qbh, char *id) { secno sec; + __le32 *ret; unsigned n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; if (hpfs_sb(s)->sb_chk) if (bmp_block >= n_bands) { hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id); @@ -27,7 +28,23 @@ __le32 *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block, hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id); return NULL; } - return hpfs_map_4sectors(s, sec, qbh, 4); + ret = hpfs_map_4sectors(s, sec, qbh, 4); + if (ret) hpfs_prefetch_bitmap(s, bmp_block + 1); + return ret; +} + +void hpfs_prefetch_bitmap(struct super_block *s, unsigned bmp_block) +{ + unsigned to_prefetch, next_prefetch; + unsigned n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; + if (unlikely(bmp_block >= n_bands)) + return; + to_prefetch = le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[bmp_block]); + if (unlikely(bmp_block + 1 >= n_bands)) + next_prefetch = 0; + else + next_prefetch = le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[bmp_block + 1]); + hpfs_prefetch_sectors(s, to_prefetch, 4 + 4 * (to_prefetch + 4 == next_prefetch)); } /* diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 962e90c37aec6..4334cda8dba14 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -121,7 +121,7 @@ unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno) unsigned long *bits; unsigned count; - bits = hpfs_map_4sectors(s, secno, &qbh, 4); + bits = hpfs_map_4sectors(s, secno, &qbh, 0); if (!bits) return 0; count = bitmap_weight(bits, 2048 * BITS_PER_BYTE); @@ -134,8 +134,13 @@ static unsigned count_bitmaps(struct super_block *s) unsigned n, count, n_bands; n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; count = 0; - for (n = 0; n < n_bands; n++) + for (n = 0; n < COUNT_RD_AHEAD; n++) { + hpfs_prefetch_bitmap(s, n); + } + for (n = 0; n < n_bands; n++) { + hpfs_prefetch_bitmap(s, n + COUNT_RD_AHEAD); count += hpfs_count_one_bitmap(s, le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[n])); + } return count; } -- cgit v1.2.3