[GFS2] Journal extent mapping
authorBob Peterson <rpeterso@redhat.com>
Wed, 12 Dec 2007 00:49:21 +0000 (18:49 -0600)
committerSteven Whitehouse <swhiteho@redhat.com>
Fri, 25 Jan 2008 08:11:46 +0000 (08:11 +0000)
This patch saves a little time when gfs2 writes to the journals by
keeping a mapping between logical and physical blocks on disk.
That's better than constantly looking up indirect pointers in
buffers, when the journals are several levels of indirection
(which they typically are).

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
fs/gfs2/incore.h
fs/gfs2/log.c
fs/gfs2/ops_fstype.c
fs/gfs2/super.c

index 330f4c73d0e758e91d3e47ab2d9d59c359720847..51166c12c5d7a85ae554d797543a60fc90c0774e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
  *
  * This copyrighted material is made available to anyone wishing to use,
  * modify, copy, or redistribute it subject to the terms and conditions
@@ -360,8 +360,17 @@ struct gfs2_ail {
        u64 ai_sync_gen;
 };
 
+struct gfs2_journal_extent {
+       struct list_head extent_list;
+
+       unsigned int lblock; /* First logical block */
+       u64 dblock; /* First disk block */
+       u64 blocks;
+};
+
 struct gfs2_jdesc {
        struct list_head jd_list;
+       struct list_head extent_list;
 
        struct inode *jd_inode;
        unsigned int jd_jid;
index 14333d81cf7daa3273e45f445847982efb87cfbe..69a583ec43c7cf2f0237d50892cbb94b0754a88f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
  *
  * This copyrighted material is made available to anyone wishing to use,
  * modify, copy, or redistribute it subject to the terms and conditions
@@ -339,18 +339,14 @@ void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
 
 static u64 log_bmap(struct gfs2_sbd *sdp, unsigned int lbn)
 {
-       struct inode *inode = sdp->sd_jdesc->jd_inode;
-       int error;
-       struct buffer_head bh_map = { .b_state = 0, .b_blocknr = 0 };
-
-       bh_map.b_size = 1 << inode->i_blkbits;
-       error = gfs2_block_map(inode, lbn, &bh_map, 0);
-       if (error || !bh_map.b_blocknr)
-               printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error,
-                      (unsigned long long)bh_map.b_blocknr, lbn);
-       gfs2_assert_withdraw(sdp, !error && bh_map.b_blocknr);
-
-       return bh_map.b_blocknr;
+       struct gfs2_journal_extent *je;
+
+       list_for_each_entry(je, &sdp->sd_jdesc->extent_list, extent_list) {
+               if (lbn >= je->lblock && lbn < je->lblock + je->blocks)
+                       return je->dblock + lbn;
+       }
+
+       return -1;
 }
 
 /**
index 1bba6ac0bcace881e4177618d7e58b71f9fe0532..0921f17a164cee9c047c35546ec21ea584893d00 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
  *
  * This copyrighted material is made available to anyone wishing to use,
  * modify, copy, or redistribute it subject to the terms and conditions
@@ -21,6 +21,7 @@
 
 #include "gfs2.h"
 #include "incore.h"
+#include "bmap.h"
 #include "daemon.h"
 #include "glock.h"
 #include "glops.h"
@@ -302,6 +303,68 @@ out:
        return error;
 }
 
+/**
+ * map_journal_extents - create a reusable "extent" mapping from all logical
+ * blocks to all physical blocks for the given journal.  This will save
+ * us time when writing journal blocks.  Most journals will have only one
+ * extent that maps all their logical blocks.  That's because gfs2.mkfs
+ * arranges the journal blocks sequentially to maximize performance.
+ * So the extent would map the first block for the entire file length.
+ * However, gfs2_jadd can happen while file activity is happening, so
+ * those journals may not be sequential.  Less likely is the case where
+ * the users created their own journals by mounting the metafs and
+ * laying it out.  But it's still possible.  These journals might have
+ * several extents.
+ *
+ * TODO: This should be done in bigger chunks rather than one block at a time,
+ *       but since it's only done at mount time, I'm not worried about the
+ *       time it takes.
+ */
+static int map_journal_extents(struct gfs2_sbd *sdp)
+{
+       struct gfs2_jdesc *jd = sdp->sd_jdesc;
+       unsigned int lb;
+       u64 db, prev_db; /* logical block, disk block, prev disk block */
+       struct gfs2_inode *ip = GFS2_I(jd->jd_inode);
+       struct gfs2_journal_extent *jext = NULL;
+       struct buffer_head bh;
+       int rc = 0;
+
+       INIT_LIST_HEAD(&jd->extent_list);
+       prev_db = 0;
+
+       for (lb = 0; lb < ip->i_di.di_size / sdp->sd_sb.sb_bsize; lb++) {
+               bh.b_state = 0;
+               bh.b_blocknr = 0;
+               bh.b_size = 1 << ip->i_inode.i_blkbits;
+               rc = gfs2_block_map(jd->jd_inode, lb, &bh, 0);
+               db = bh.b_blocknr;
+               if (rc || !db) {
+                       printk(KERN_INFO "GFS2 journal mapping error %d: lb="
+                              "%u db=%llu\n", rc, lb, (unsigned long long)db);
+                       break;
+               }
+               if (!prev_db || db != prev_db + 1) {
+                       jext = kzalloc(sizeof(struct gfs2_journal_extent),
+                                      GFP_KERNEL);
+                       if (!jext) {
+                               printk(KERN_INFO "GFS2 error: out of memory "
+                                      "mapping journal extents.\n");
+                               rc = -ENOMEM;
+                               break;
+                       }
+                       jext->dblock = db;
+                       jext->lblock = lb;
+                       jext->blocks = 1;
+                       list_add_tail(&jext->extent_list, &jd->extent_list);
+               } else {
+                       jext->blocks++;
+               }
+               prev_db = db;
+       }
+       return rc;
+}
+
 static int init_journal(struct gfs2_sbd *sdp, int undo)
 {
        struct gfs2_holder ji_gh;
@@ -377,6 +440,9 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
                        goto fail_jinode_gh;
                }
                atomic_set(&sdp->sd_log_blks_free, sdp->sd_jdesc->jd_blocks);
+
+               /* Map the extents for this journal's blocks */
+               map_journal_extents(sdp);
        }
 
        if (sdp->sd_lockstruct.ls_first) {
index 2e74792ee487d0750f0aba162c333e25b2b1044c..22e09660d648e65244b2c36c201672c5f2e238d0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
  *
  * This copyrighted material is made available to anyone wishing to use,
  * modify, copy, or redistribute it subject to the terms and conditions
@@ -416,8 +416,9 @@ int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
 
 void gfs2_jindex_free(struct gfs2_sbd *sdp)
 {
-       struct list_head list;
+       struct list_head list, *head;
        struct gfs2_jdesc *jd;
+       struct gfs2_journal_extent *jext;
 
        spin_lock(&sdp->sd_jindex_spin);
        list_add(&list, &sdp->sd_jindex_list);
@@ -427,6 +428,14 @@ void gfs2_jindex_free(struct gfs2_sbd *sdp)
 
        while (!list_empty(&list)) {
                jd = list_entry(list.next, struct gfs2_jdesc, jd_list);
+               head = &jd->extent_list;
+               while (!list_empty(head)) {
+                       jext = list_entry(head->next,
+                                         struct gfs2_journal_extent,
+                                         extent_list);
+                       list_del(&jext->extent_list);
+                       kfree(jext);
+               }
                list_del(&jd->jd_list);
                iput(jd->jd_inode);
                kfree(jd);