From: Andrew Morton Linus points out that ext3_readdir's readahead only cuts in when ext3_readdir() is operating at the very start of the directory. So for large directories we end up performing no readahead at all and we suck. So take it all out and use the core VM's page_cache_readahead(). This means that ext3 directory reads will use all of readahead's dynamic sizing goop. Note that we're using the diretory's filp->f_ra to hold the readahead state, but readahead is actually being performed against the underlying blockdev's address_space. Fortunately the readahead code is all set up to handle this. Signed-off-by: Andrew Morton --- fs/ext3/dir.c | 46 +++++++++++++++++----------------------------- fs/ext3/inode.c | 5 +++++ include/linux/ext3_fs.h | 7 ++++--- 3 files changed, 26 insertions(+), 32 deletions(-) diff -puN fs/ext3/dir.c~ext3_readdir-use-generic-readahead fs/ext3/dir.c --- devel/fs/ext3/dir.c~ext3_readdir-use-generic-readahead 2005-11-05 14:14:06.000000000 -0800 +++ devel-akpm/fs/ext3/dir.c 2005-11-05 14:15:15.000000000 -0800 @@ -91,15 +91,14 @@ int ext3_check_dir_entry (const char * f return error_msg == NULL ? 1 : 0; } -static int ext3_readdir(struct file * filp, - void * dirent, filldir_t filldir) +static int ext3_readdir(struct file *filp, void *dirent, filldir_t filldir) { int error = 0; - unsigned long offset, blk; - int i, num, stored; - struct buffer_head * bh, * tmp, * bha[16]; - struct ext3_dir_entry_2 * de; - struct super_block * sb; + unsigned long offset; + int i, stored; + struct buffer_head *bh; + struct ext3_dir_entry_2 *de; + struct super_block *sb; int err; struct inode *inode = filp->f_dentry->d_inode; int ret = 0; @@ -128,8 +127,17 @@ static int ext3_readdir(struct file * fi offset = filp->f_pos & (sb->s_blocksize - 1); while (!error && !stored && filp->f_pos < inode->i_size) { - blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb); - bh = ext3_bread(NULL, inode, blk, 0, &err); + unsigned long blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb); + bh = ext3_getblk(NULL, inode, blk, 0, &err); + if (bh) { + page_cache_readahead(sb->s_bdev->bd_inode->i_mapping, + &filp->f_ra, + filp, + bh->b_blocknr >> + (PAGE_CACHE_SHIFT - inode->i_blkbits), + sb->s_blocksize); + bh = __ext3_bread(bh, &err); + } if (!bh) { ext3_error (sb, "ext3_readdir", "directory #%lu contains a hole at offset %lu", @@ -138,26 +146,6 @@ static int ext3_readdir(struct file * fi continue; } - /* - * Do the readahead - */ - if (!offset) { - for (i = 16 >> (EXT3_BLOCK_SIZE_BITS(sb) - 9), num = 0; - i > 0; i--) { - tmp = ext3_getblk (NULL, inode, ++blk, 0, &err); - if (tmp && !buffer_uptodate(tmp) && - !buffer_locked(tmp)) - bha[num++] = tmp; - else - brelse (tmp); - } - if (num) { - ll_rw_block (READA, num, bha); - for (i = 0; i < num; i++) - brelse (bha[i]); - } - } - revalidate: /* If the dir block has changed since the last call to * readdir(2), then we might be pointing to an invalid diff -puN fs/ext3/inode.c~ext3_readdir-use-generic-readahead fs/ext3/inode.c --- devel/fs/ext3/inode.c~ext3_readdir-use-generic-readahead 2005-11-05 14:14:06.000000000 -0800 +++ devel-akpm/fs/ext3/inode.c 2005-11-05 14:14:43.000000000 -0800 @@ -914,6 +914,11 @@ struct buffer_head *ext3_bread(handle_t bh = ext3_getblk(handle, inode, block, create, err); if (!bh) return bh; + return __ext3_bread(bh, err); +} + +struct buffer_head *__ext3_bread(struct buffer_head *bh, int *err) +{ if (buffer_uptodate(bh)) return bh; ll_rw_block(READ, 1, &bh); diff -puN include/linux/ext3_fs.h~ext3_readdir-use-generic-readahead include/linux/ext3_fs.h --- devel/include/linux/ext3_fs.h~ext3_readdir-use-generic-readahead 2005-11-05 14:14:06.000000000 -0800 +++ devel-akpm/include/linux/ext3_fs.h 2005-11-05 14:15:01.000000000 -0800 @@ -772,9 +772,10 @@ extern unsigned long ext3_count_free (st /* inode.c */ -extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int); -extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *); -extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *); +int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int); +struct buffer_head *ext3_getblk (handle_t *, struct inode *, long, int, int *); +struct buffer_head *ext3_bread(handle_t *, struct inode *, int, int, int *); +struct buffer_head *__ext3_bread(struct buffer_head *bh, int *err); extern void ext3_read_inode (struct inode *); extern int ext3_write_inode (struct inode *, int); _