Commit d8733c29 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ext3_readdir: use generic readahead

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 directory'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.

Tested with printk.  It works.  I was struggling to find a real workload which
actually cared.

(The patch also exports page_cache_readahead() to GPL modules)

Cc: "Stephen C. Tweedie" <sct@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b8e31edc
...@@ -95,11 +95,10 @@ static int ext3_readdir(struct file * filp, ...@@ -95,11 +95,10 @@ static int ext3_readdir(struct file * filp,
void * dirent, filldir_t filldir) void * dirent, filldir_t filldir)
{ {
int error = 0; int error = 0;
unsigned long offset, blk; unsigned long offset;
int i, num, stored; int i, stored;
struct buffer_head * bh, * tmp, * bha[16]; struct ext3_dir_entry_2 *de;
struct ext3_dir_entry_2 * de; struct super_block *sb;
struct super_block * sb;
int err; int err;
struct inode *inode = filp->f_dentry->d_inode; struct inode *inode = filp->f_dentry->d_inode;
int ret = 0; int ret = 0;
...@@ -124,12 +123,29 @@ static int ext3_readdir(struct file * filp, ...@@ -124,12 +123,29 @@ static int ext3_readdir(struct file * filp,
} }
#endif #endif
stored = 0; stored = 0;
bh = NULL;
offset = filp->f_pos & (sb->s_blocksize - 1); offset = filp->f_pos & (sb->s_blocksize - 1);
while (!error && !stored && filp->f_pos < inode->i_size) { while (!error && !stored && filp->f_pos < inode->i_size) {
blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb); unsigned long blk = filp->f_pos >> EXT3_BLOCK_SIZE_BITS(sb);
struct buffer_head map_bh;
struct buffer_head *bh = NULL;
map_bh.b_state = 0;
err = ext3_get_block_handle(NULL, inode, blk, &map_bh, 0, 0);
if (!err) {
page_cache_readahead(sb->s_bdev->bd_inode->i_mapping,
&filp->f_ra,
filp,
map_bh.b_blocknr >>
(PAGE_CACHE_SHIFT - inode->i_blkbits),
1);
bh = ext3_bread(NULL, inode, blk, 0, &err); bh = ext3_bread(NULL, inode, blk, 0, &err);
}
/*
* We ignore I/O errors on directories so users have a chance
* of recovering data when there's a bad sector
*/
if (!bh) { if (!bh) {
ext3_error (sb, "ext3_readdir", ext3_error (sb, "ext3_readdir",
"directory #%lu contains a hole at offset %lu", "directory #%lu contains a hole at offset %lu",
...@@ -138,26 +154,6 @@ static int ext3_readdir(struct file * filp, ...@@ -138,26 +154,6 @@ static int ext3_readdir(struct file * filp,
continue; 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: revalidate:
/* If the dir block has changed since the last call to /* If the dir block has changed since the last call to
* readdir(2), then we might be pointing to an invalid * readdir(2), then we might be pointing to an invalid
......
...@@ -671,7 +671,7 @@ static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block, ...@@ -671,7 +671,7 @@ static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block,
* The BKL may not be held on entry here. Be sure to take it early. * The BKL may not be held on entry here. Be sure to take it early.
*/ */
static int int
ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock, ext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create, int extend_disksize) struct buffer_head *bh_result, int create, int extend_disksize)
{ {
......
...@@ -772,9 +772,12 @@ extern unsigned long ext3_count_free (struct buffer_head *, unsigned); ...@@ -772,9 +772,12 @@ extern unsigned long ext3_count_free (struct buffer_head *, unsigned);
/* inode.c */ /* inode.c */
extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int); 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 *); 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 *); struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
int ext3_get_block_handle(handle_t *handle, struct inode *inode,
sector_t iblock, struct buffer_head *bh_result, int create,
int extend_disksize);
extern void ext3_read_inode (struct inode *); extern void ext3_read_inode (struct inode *);
extern int ext3_write_inode (struct inode *, int); extern int ext3_write_inode (struct inode *, int);
......
...@@ -555,6 +555,7 @@ page_cache_readahead(struct address_space *mapping, struct file_ra_state *ra, ...@@ -555,6 +555,7 @@ page_cache_readahead(struct address_space *mapping, struct file_ra_state *ra,
out: out:
return ra->prev_page + 1; return ra->prev_page + 1;
} }
EXPORT_SYMBOL_GPL(page_cache_readahead);
/* /*
* handle_ra_miss() is called when it is known that a page which should have * handle_ra_miss() is called when it is known that a page which should have
......
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