[PATCH] ext3_readdir: use generic readahead
authorAndrew Morton <akpm@osdl.org>
Thu, 23 Mar 2006 11:00:11 +0000 (03:00 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 23 Mar 2006 15:38:09 +0000 (07:38 -0800)
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: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/ext3/dir.c
fs/ext3/inode.c
include/linux/ext3_fs.h
mm/readahead.c

index 832867aef3dca61d8d2376e9ca7e14a24dbafd69..773459164bb2cbd1b1641fd367b563d252ad3016 100644 (file)
@@ -95,11 +95,10 @@ 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 ext3_dir_entry_2 *de;
+       struct super_block *sb;
        int err;
        struct inode *inode = filp->f_dentry->d_inode;
        int ret = 0;
@@ -124,12 +123,29 @@ static int ext3_readdir(struct file * filp,
        }
 #endif
        stored = 0;
-       bh = NULL;
        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);
+               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);
+               }
+
+               /*
+                * We ignore I/O errors on directories so users have a chance
+                * of recovering data when there's a bad sector
+                */
                if (!bh) {
                        ext3_error (sb, "ext3_readdir",
                                "directory #%lu contains a hole at offset %lu",
@@ -138,26 +154,6 @@ static int ext3_readdir(struct file * filp,
                        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
index 0384e539b88f47a18c32c0b6dce63c8f0e4558e0..d59d5a667b0bf04a7f8e1bc794556dae4db2fde4 100644 (file)
@@ -671,7 +671,7 @@ err_out:
  * 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,
                struct buffer_head *bh_result, int create, int extend_disksize)
 {
index c0272d73ab20a576dfc96c2291e7ab7c74076c48..e7239f2f97a15c1c8b112aa03c7fb87779836231 100644 (file)
@@ -772,9 +772,12 @@ extern unsigned long ext3_count_free (struct buffer_head *, unsigned);
 
 
 /* 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 *);
+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 int  ext3_write_inode (struct inode *, int);
index 301b36c4a0ce10a006c651637cb2597040ec1413..0f142a40984b1674228e067f491159d42b13f32d 100644 (file)
@@ -555,6 +555,7 @@ recheck:
 out:
        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