[GFS2] Map multiple blocks at once where possible
authorSteven Whitehouse <swhiteho@redhat.com>
Mon, 18 Sep 2006 21:18:23 +0000 (17:18 -0400)
committerSteven Whitehouse <swhiteho@redhat.com>
Mon, 18 Sep 2006 21:18:23 +0000 (17:18 -0400)
This is a tidy up of the GFS2 bmap code. The main change is that the
bh is passed to gfs2_block_map allowing the flags to be set directly
rather than having to repeat that code several times in ops_address.c.

At the same time, the extent mapping code from gfs2_extent_map has
been moved into gfs2_block_map. This allows all calls to gfs2_block_map
to map extents in the case that no allocation is taking place. As a
result reads and non-allocating writes should be faster. A quick test
with postmark appears to support this.

There is a limit on the number of blocks mapped in a single bmap
call in that it will only ever map blocks which are pointed to
from a single pointer block. So in other words, it will never try
to do additional i/o in order to satisfy read-ahead. The maximum
number of blocks is thus somewhat less than 512 (the GFS2 4k block
size minus the header divided by sizeof(u64)). I've further limited
the mapping of "normal" blocks to 32 blocks (to avoid extra work)
since readpages() will currently read a maximum of 32 blocks ahead (128k).

Some further work will probably be needed to set a suitable value
for DIO as well, but for now thats left at the maximum 512 (see
ops_address.c:gfs2_get_block_direct).

There is probably a lot more that can be done to improve bmap for GFS2,
but this is a good first step.

Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
fs/gfs2/bmap.c
fs/gfs2/bmap.h
fs/gfs2/log.c
fs/gfs2/ops_address.c
fs/gfs2/quota.c
fs/gfs2/recovery.c

index 57caad7..cc91e48 100644 (file)
@@ -423,8 +423,7 @@ static int lookup_block(struct gfs2_inode *ip, struct buffer_head *bh,
  * gfs2_block_pointers - Map a block from an inode to a disk block
  * @inode: The inode
  * @lblock: The logical block number
- * @new: Value/Result argument (1 = may create/did create new blocks)
- * @boundary: gets set if we've hit a block boundary
+ * @map_bh: The bh to be mapped
  * @mp: metapath to use
  *
  * Find the block number on the current device which corresponds to an
@@ -433,37 +432,35 @@ static int lookup_block(struct gfs2_inode *ip, struct buffer_head *bh,
  * Returns: errno
  */
 
-static struct buffer_head *gfs2_block_pointers(struct inode *inode, u64 lblock,
-                                              int *new, u64 *dblock,
-                                              int *boundary,
-                                              struct metapath *mp)
+static int gfs2_block_pointers(struct inode *inode, u64 lblock, int create,
+                              struct buffer_head *bh_map, struct metapath *mp,
+                              unsigned int maxlen)
 {
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_sbd *sdp = GFS2_SB(inode);
        struct buffer_head *bh;
-       int create = *new;
        unsigned int bsize;
        unsigned int height;
        unsigned int end_of_metadata;
        unsigned int x;
        int error = 0;
-
-       *new = 0;
-       *dblock = 0;
+       int new = 0;
+       u64 dblock = 0;
+       int boundary;
 
        if (gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip)))
-               goto out;
+               return 0;
 
        bsize = gfs2_is_dir(ip) ? sdp->sd_jbsize : sdp->sd_sb.sb_bsize;
 
        height = calc_tree_height(ip, (lblock + 1) * bsize);
        if (ip->i_di.di_height < height) {
                if (!create)
-                       goto out;
+                       return 0;
 
                error = build_height(inode, height);
                if (error)
-                       goto out;
+                       return error;
        }
 
        find_metapath(ip, lblock, mp);
@@ -471,32 +468,54 @@ static struct buffer_head *gfs2_block_pointers(struct inode *inode, u64 lblock,
 
        error = gfs2_meta_inode_buffer(ip, &bh);
        if (error)
-               goto out;
+               return error;
 
        for (x = 0; x < end_of_metadata; x++) {
-               lookup_block(ip, bh, x, mp, create, new, dblock);
+               lookup_block(ip, bh, x, mp, create, &new, &dblock);
                brelse(bh);
-               if (!*dblock)
-                       goto out;
+               if (!dblock)
+                       return 0;
 
-               error = gfs2_meta_indirect_buffer(ip, x+1, *dblock, *new, &bh);
+               error = gfs2_meta_indirect_buffer(ip, x+1, dblock, new, &bh);
                if (error)
-                       goto out;
+                       return error;
        }
 
-       *boundary = lookup_block(ip, bh, end_of_metadata, mp, create, new, dblock);
-       if (*new) {
-               struct buffer_head *dibh;
-               error = gfs2_meta_inode_buffer(ip, &dibh);
-               if (!error) {
-                       gfs2_trans_add_bh(ip->i_gl, dibh, 1);
-                       gfs2_dinode_out(&ip->i_di, dibh->b_data);
-                       brelse(dibh);
+       boundary = lookup_block(ip, bh, end_of_metadata, mp, create, &new, &dblock);
+       clear_buffer_mapped(bh_map);
+       clear_buffer_new(bh_map);
+       clear_buffer_boundary(bh_map);
+
+       if (dblock) {
+               map_bh(bh_map, inode->i_sb, dblock);
+               if (boundary)
+                       set_buffer_boundary(bh);
+               if (new) {
+                       struct buffer_head *dibh;
+                       error = gfs2_meta_inode_buffer(ip, &dibh);
+                       if (!error) {
+                               gfs2_trans_add_bh(ip->i_gl, dibh, 1);
+                               gfs2_dinode_out(&ip->i_di, dibh->b_data);
+                               brelse(dibh);
+                       }
+                       set_buffer_new(bh_map);
+                       goto out_brelse;
+               }
+               while(--maxlen && !buffer_boundary(bh_map)) {
+                       u64 eblock;
+
+                       mp->mp_list[end_of_metadata]++;
+                       boundary = lookup_block(ip, bh, end_of_metadata, mp, 0, &new, &eblock);
+                       if (eblock != ++dblock)
+                               break;
+                       bh_map->b_size += inode->i_blksize;
+                       if (boundary)
+                               set_buffer_boundary(bh_map);
                }
        }
-       return bh;
-out:
-       return ERR_PTR(error);
+out_brelse:
+       brelse(bh);
+       return 0;
 }
 
 
@@ -518,30 +537,23 @@ static inline void bmap_unlock(struct inode *inode, int create)
                up_read(&ip->i_rw_mutex);
 }
 
-int gfs2_block_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, int *boundary)
+int gfs2_block_map(struct inode *inode, u64 lblock, int create,
+                  struct buffer_head *bh, unsigned int maxlen)
 {
        struct metapath mp;
-       struct buffer_head *bh;
-       int create = *new;
+       int ret;
 
        bmap_lock(inode, create);
-       bh = gfs2_block_pointers(inode, lblock, new, dblock, boundary, &mp);
+       ret = gfs2_block_pointers(inode, lblock, create, bh, &mp, maxlen);
        bmap_unlock(inode, create);
-       if (!bh)
-               return 0;
-       if (IS_ERR(bh))
-               return PTR_ERR(bh);
-       brelse(bh);
-       return 0;
+       return ret;
 }
 
 int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen)
 {
-       struct gfs2_inode *ip = GFS2_I(inode);
-       struct gfs2_sbd *sdp = GFS2_SB(inode);
        struct metapath mp;
-       struct buffer_head *bh;
-       int boundary;
+       struct buffer_head bh = { .b_state = 0, .b_blocknr = 0, .b_size = 0 };
+       int ret;
        int create = *new;
 
        BUG_ON(!extlen);
@@ -549,30 +561,15 @@ int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsi
        BUG_ON(!new);
 
        bmap_lock(inode, create);
-       bh = gfs2_block_pointers(inode, lblock, new, dblock, &boundary, &mp);
-       *extlen = 1;
-
-       if (bh != NULL && !IS_ERR(bh) && *dblock != 0 && *new == 0) {
-               u64 tmp_dblock;
-               int tmp_new;
-               unsigned int nptrs;
-               unsigned end_of_metadata = ip->i_di.di_height - 1;
-               
-               nptrs = (end_of_metadata) ? sdp->sd_inptrs : sdp->sd_diptrs;
-               while (++mp.mp_list[end_of_metadata] < nptrs) {
-                       lookup_block(ip, bh, end_of_metadata, &mp, 0, &tmp_new, &tmp_dblock);
-                       if (*dblock + *extlen != tmp_dblock)
-                               break;
-                       ++*extlen;
-               }
-       }
+       ret = gfs2_block_pointers(inode, lblock, create, &bh, &mp, *extlen);
        bmap_unlock(inode, create);
-       if (!bh)
-               return 0;
-       if (IS_ERR(bh))
-               return PTR_ERR(bh);
-       brelse(bh);
-       return 0;
+       *extlen = bh.b_size >> inode->i_blkbits;
+       *dblock = bh.b_blocknr;
+       if (buffer_new(&bh))
+               *new = 1;
+       else
+               *new = 0;
+       return ret;
 }
 
 /**
index 503f1cd..0fd379b 100644 (file)
@@ -15,7 +15,7 @@ struct gfs2_inode;
 struct page;
 
 int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page);
-int gfs2_block_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, int *boundary);
+int gfs2_block_map(struct inode *inode, u64 lblock, int create, struct buffer_head *bh, unsigned int maxlen);
 int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen);
 
 int gfs2_truncatei(struct gfs2_inode *ip, u64 size);
index 50f8805..ab341cd 100644 (file)
@@ -204,17 +204,15 @@ void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
 
 static u64 log_bmap(struct gfs2_sbd *sdp, unsigned int lbn)
 {
-       int new = 0;
-       u64 dbn;
        int error;
-       int bdy;
+       struct buffer_head bh_map;
 
-       error = gfs2_block_map(sdp->sd_jdesc->jd_inode, lbn, &new, &dbn, &bdy);
-       if (error || !dbn)
-               printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error, (unsigned long long)dbn, lbn);
-       gfs2_assert_withdraw(sdp, !error && dbn);
+       error = gfs2_block_map(sdp->sd_jdesc->jd_inode, lbn, 0, &bh_map, 1);
+       if (error || !bh_map.b_blocknr)
+               printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error, bh_map.b_blocknr, lbn);
+       gfs2_assert_withdraw(sdp, !error && bh_map.b_blocknr);
 
-       return dbn;
+       return bh_map.b_blocknr;
 }
 
 /**
index d44d42f..6f9ac5e 100644 (file)
@@ -65,29 +65,11 @@ static void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page,
 int gfs2_get_block(struct inode *inode, sector_t lblock,
                   struct buffer_head *bh_result, int create)
 {
-       int new = create;
-       u64 dblock;
-       int error;
-       int boundary;
-
-       error = gfs2_block_map(inode, lblock, &new, &dblock, &boundary);
-       if (error)
-               return error;
-
-       if (!dblock)
-               return 0;
-
-       map_bh(bh_result, inode->i_sb, dblock);
-       if (new)
-               set_buffer_new(bh_result);
-       if (boundary)
-               set_buffer_boundary(bh_result);
-
-       return 0;
+       return gfs2_block_map(inode, lblock, create, bh_result, 32);
 }
 
 /**
- * get_block_noalloc - Fills in a buffer head with details about a block
+ * gfs2_get_block_noalloc - Fills in a buffer head with details about a block
  * @inode: The inode
  * @lblock: The block number to look up
  * @bh_result: The buffer head to return the result in
@@ -96,47 +78,25 @@ int gfs2_get_block(struct inode *inode, sector_t lblock,
  * Returns: errno
  */
 
-static int get_block_noalloc(struct inode *inode, sector_t lblock,
-                            struct buffer_head *bh_result, int create)
+static int gfs2_get_block_noalloc(struct inode *inode, sector_t lblock,
+                                 struct buffer_head *bh_result, int create)
 {
-       int new = 0;
-       u64 dblock;
        int error;
-       int boundary;
 
-       error = gfs2_block_map(inode, lblock, &new, &dblock, &boundary);
+       error = gfs2_block_map(inode, lblock, 0, bh_result, 1);
        if (error)
                return error;
-
-       if (dblock)
-               map_bh(bh_result, inode->i_sb, dblock);
-       else if (gfs2_assert_withdraw(GFS2_SB(inode), !create))
-               error = -EIO;
-       if (boundary)
-               set_buffer_boundary(bh_result);
-
-       return error;
+       if (bh_result->b_blocknr == 0)
+               return -EIO;
+       return 0;
 }
 
-static int get_block_direct(struct inode *inode, sector_t lblock,
-                           struct buffer_head *bh_result, int create)
+static int gfs2_get_block_direct(struct inode *inode, sector_t lblock,
+                                struct buffer_head *bh_result, int create)
 {
-       int new = 0;
-       u64 dblock;
-       int error, boundary;
-
-       error = gfs2_block_map(inode, lblock, &new, &dblock, &boundary);
-       if (error)
-               return error;
-
-       if (dblock) {
-               map_bh(bh_result, inode->i_sb, dblock);
-               if (boundary)
-                       set_buffer_boundary(bh_result);
-       }
-
-       return 0;
+       return gfs2_block_map(inode, lblock, 0, bh_result, 512);
 }
+
 /**
  * gfs2_writepage - Write complete page
  * @page: Page to write
@@ -184,7 +144,7 @@ static int gfs2_writepage(struct page *page, struct writeback_control *wbc)
                gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize-1);
                done_trans = 1;
        }
-       error = block_write_full_page(page, get_block_noalloc, wbc);
+       error = block_write_full_page(page, gfs2_get_block_noalloc, wbc);
        if (done_trans)
                gfs2_trans_end(sdp);
        gfs2_meta_cache_flush(ip);
@@ -680,7 +640,7 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
        rv = blockdev_direct_IO_own_locking(rw, iocb, inode,
                                            inode->i_sb->s_bdev,
                                            iov, offset, nr_segs,
-                                           get_block_direct, NULL);
+                                           gfs2_get_block_direct, NULL);
 out:
        gfs2_glock_dq_m(1, &gh);
        gfs2_holder_uninit(&gh);
index fe1828f..bc9ad05 100644 (file)
@@ -248,11 +248,9 @@ static int bh_get(struct gfs2_quota_data *qd)
        struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
        struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode);
        unsigned int block, offset;
-       u64 dblock;
-       int new = 0;
        struct buffer_head *bh;
        int error;
-       int boundary;
+       struct buffer_head bh_map;
 
        mutex_lock(&sdp->sd_quota_mutex);
 
@@ -264,10 +262,10 @@ static int bh_get(struct gfs2_quota_data *qd)
        block = qd->qd_slot / sdp->sd_qc_per_block;
        offset = qd->qd_slot % sdp->sd_qc_per_block;;
 
-       error = gfs2_block_map(&ip->i_inode, block, &new, &dblock, &boundary);
+       error = gfs2_block_map(&ip->i_inode, block, 0, &bh_map, 1);
        if (error)
                goto fail;
-       error = gfs2_meta_read(ip->i_gl, dblock, DIO_START | DIO_WAIT, &bh);
+       error = gfs2_meta_read(ip->i_gl, bh_map.b_blocknr, DIO_START | DIO_WAIT, &bh);
        if (error)
                goto fail;
        error = -EIO;
index a27569c..130e9fb 100644 (file)
@@ -369,25 +369,23 @@ static int clean_journal(struct gfs2_jdesc *jd, struct gfs2_log_header *head)
        struct gfs2_inode *ip = GFS2_I(jd->jd_inode);
        struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
        unsigned int lblock;
-       int new = 0;
-       u64 dblock;
        struct gfs2_log_header *lh;
        u32 hash;
        struct buffer_head *bh;
        int error;
-       int boundary;
+       struct buffer_head bh_map;
 
        lblock = head->lh_blkno;
        gfs2_replay_incr_blk(sdp, &lblock);
-       error = gfs2_block_map(&ip->i_inode, lblock, &new, &dblock, &boundary);
+       error = gfs2_block_map(&ip->i_inode, lblock, 0, &bh_map, 1);
        if (error)
                return error;
-       if (!dblock) {
+       if (!bh_map.b_blocknr) {
                gfs2_consist_inode(ip);
                return -EIO;
        }
 
-       bh = sb_getblk(sdp->sd_vfs, dblock);
+       bh = sb_getblk(sdp->sd_vfs, bh_map.b_blocknr);
        lock_buffer(bh);
        memset(bh->b_data, 0, bh->b_size);
        set_buffer_uptodate(bh);