]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/gfs2/rgrp.c
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux...
[linux-2.6-omap-h63xx.git] / fs / gfs2 / rgrp.c
index 3f10b1fafd663a6186b1102ce9f36b4db5e05ea8..7e8f0b1d6c6ea2d39cb18811a7762c1b8e15ee6c 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/fs.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/lm_interface.h>
+#include <linux/prefetch.h>
 
 #include "gfs2.h"
 #include "incore.h"
 #define BFITNOENT ((u32)~0)
 #define NO_BLOCK ((u64)~0)
 
+#if BITS_PER_LONG == 32
+#define LBITMASK   (0x55555555UL)
+#define LBITSKIP55 (0x55555555UL)
+#define LBITSKIP00 (0x00000000UL)
+#else
+#define LBITMASK   (0x5555555555555555UL)
+#define LBITSKIP55 (0x5555555555555555UL)
+#define LBITSKIP00 (0x0000000000000000UL)
+#endif
+
 /*
  * These routines are used by the resource group routines (rgrp.c)
  * to keep track of block allocation.  Each block is represented by two
@@ -53,7 +64,8 @@ static const char valid_change[16] = {
 };
 
 static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
-                        unsigned char old_state, unsigned char new_state);
+                        unsigned char old_state, unsigned char new_state,
+                       unsigned int *n);
 
 /**
  * gfs2_setbit - Set a bit in the bitmaps
@@ -64,26 +76,32 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
  *
  */
 
-static void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
-                       unsigned int buflen, u32 block,
-                       unsigned char new_state)
+static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf1,
+                              unsigned char *buf2, unsigned int offset,
+                              unsigned int buflen, u32 block,
+                              unsigned char new_state)
 {
-       unsigned char *byte, *end, cur_state;
-       unsigned int bit;
+       unsigned char *byte1, *byte2, *end, cur_state;
+       const unsigned int bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
 
-       byte = buffer + (block / GFS2_NBBY);
-       bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
-       end = buffer + buflen;
+       byte1 = buf1 + offset + (block / GFS2_NBBY);
+       end = buf1 + offset + buflen;
 
-       gfs2_assert(rgd->rd_sbd, byte < end);
+       BUG_ON(byte1 >= end);
 
-       cur_state = (*byte >> bit) & GFS2_BIT_MASK;
+       cur_state = (*byte1 >> bit) & GFS2_BIT_MASK;
 
-       if (valid_change[new_state * 4 + cur_state]) {
-               *byte ^= cur_state << bit;
-               *byte |= new_state << bit;
-       } else
+       if (unlikely(!valid_change[new_state * 4 + cur_state])) {
                gfs2_consist_rgrpd(rgd);
+               return;
+       }
+       *byte1 ^= (cur_state ^ new_state) << bit;
+
+       if (buf2) {
+               byte2 = buf2 + offset + (block / GFS2_NBBY);
+               cur_state = (*byte2 >> bit) & GFS2_BIT_MASK;
+               *byte2 ^= (cur_state ^ new_state) << bit;
+       }
 }
 
 /**
@@ -94,10 +112,12 @@ static void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
  *
  */
 
-static unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
-                                 unsigned int buflen, u32 block)
+static inline unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd,
+                                        const unsigned char *buffer,
+                                        unsigned int buflen, u32 block)
 {
-       unsigned char *byte, *end, cur_state;
+       const unsigned char *byte, *end;
+       unsigned char cur_state;
        unsigned int bit;
 
        byte = buffer + (block / GFS2_NBBY);
@@ -129,45 +149,63 @@ static unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
 static u32 gfs2_bitfit(const u8 *buffer, unsigned int buflen, u32 goal,
                       u8 old_state)
 {
-       const u8 *byte;
-       u32 blk = goal;
-       unsigned int bit, bitlong;
-       const unsigned long *plong;
-#if BITS_PER_LONG == 32
-       const unsigned long plong55 = 0x55555555;
-#else
-       const unsigned long plong55 = 0x5555555555555555;
-#endif
-
-       byte = buffer + (goal / GFS2_NBBY);
-       plong = (const unsigned long *)(buffer + (goal / GFS2_NBBY));
-       bit = (goal % GFS2_NBBY) * GFS2_BIT_SIZE;
-       bitlong = bit;
-
-       while (byte < buffer + buflen) {
-
-               if (bitlong == 0 && old_state == 0 && *plong == plong55) {
-                       plong++;
-                       byte += sizeof(unsigned long);
-                       blk += sizeof(unsigned long) * GFS2_NBBY;
-                       continue;
+       const u8 *byte, *start, *end;
+       int bit, startbit;
+       u32 g1, g2, misaligned;
+       unsigned long *plong;
+       unsigned long lskipval;
+
+       lskipval = (old_state & GFS2_BLKST_USED) ? LBITSKIP00 : LBITSKIP55;
+       g1 = (goal / GFS2_NBBY);
+       start = buffer + g1;
+       byte = start;
+        end = buffer + buflen;
+       g2 = ALIGN(g1, sizeof(unsigned long));
+       plong = (unsigned long *)(buffer + g2);
+       startbit = bit = (goal % GFS2_NBBY) * GFS2_BIT_SIZE;
+       misaligned = g2 - g1;
+       if (!misaligned)
+               goto ulong_aligned;
+/* parse the bitmap a byte at a time */
+misaligned:
+       while (byte < end) {
+               if (((*byte >> bit) & GFS2_BIT_MASK) == old_state) {
+                       return goal +
+                               (((byte - start) * GFS2_NBBY) +
+                                ((bit - startbit) >> 1));
                }
-               if (((*byte >> bit) & GFS2_BIT_MASK) == old_state)
-                       return blk;
                bit += GFS2_BIT_SIZE;
-               if (bit >= 8) {
+               if (bit >= GFS2_NBBY * GFS2_BIT_SIZE) {
                        bit = 0;
                        byte++;
+                       misaligned--;
+                       if (!misaligned) {
+                               plong = (unsigned long *)byte;
+                               goto ulong_aligned;
+                       }
                }
-               bitlong += GFS2_BIT_SIZE;
-               if (bitlong >= sizeof(unsigned long) * 8) {
-                       bitlong = 0;
-                       plong++;
-               }
-
-               blk++;
        }
+       return BFITNOENT;
 
+/* parse the bitmap a unsigned long at a time */
+ulong_aligned:
+       /* Stop at "end - 1" or else prefetch can go past the end and segfault.
+          We could "if" it but we'd lose some of the performance gained.
+          This way will only slow down searching the very last 4/8 bytes
+          depending on architecture.  I've experimented with several ways
+          of writing this section such as using an else before the goto
+          but this one seems to be the fastest. */
+       while ((unsigned char *)plong < end - 1) {
+               prefetch(plong + 1);
+               if (((*plong) & LBITMASK) != lskipval)
+                       break;
+               plong++;
+       }
+       if ((unsigned char *)plong < end) {
+               byte = (const u8 *)plong;
+               misaligned += sizeof(unsigned long) - 1;
+               goto misaligned;
+       }
        return BFITNOENT;
 }
 
@@ -877,13 +915,15 @@ static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked)
        u32 goal = 0, block;
        u64 no_addr;
        struct gfs2_sbd *sdp = rgd->rd_sbd;
+       unsigned int n;
 
        for(;;) {
                if (goal >= rgd->rd_data)
                        break;
                down_write(&sdp->sd_log_flush_lock);
+               n = 1;
                block = rgblk_search(rgd, goal, GFS2_BLKST_UNLINKED,
-                                    GFS2_BLKST_UNLINKED);
+                                    GFS2_BLKST_UNLINKED, &n);
                up_write(&sdp->sd_log_flush_lock);
                if (block == BFITNOENT)
                        break;
@@ -915,24 +955,20 @@ static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked)
 static struct gfs2_rgrpd *recent_rgrp_first(struct gfs2_sbd *sdp,
                                            u64 rglast)
 {
-       struct gfs2_rgrpd *rgd = NULL;
+       struct gfs2_rgrpd *rgd;
 
        spin_lock(&sdp->sd_rindex_spin);
 
-       if (list_empty(&sdp->sd_rindex_recent_list))
-               goto out;
-
-       if (!rglast)
-               goto first;
-
-       list_for_each_entry(rgd, &sdp->sd_rindex_recent_list, rd_recent) {
-               if (rgd->rd_addr == rglast)
-                       goto out;
+       if (rglast) {
+               list_for_each_entry(rgd, &sdp->sd_rindex_recent_list, rd_recent) {
+                       if (rgrp_contains_block(rgd, rglast))
+                               goto out;
+               }
        }
-
-first:
-       rgd = list_entry(sdp->sd_rindex_recent_list.next, struct gfs2_rgrpd,
-                        rd_recent);
+       rgd = NULL;
+       if (!list_empty(&sdp->sd_rindex_recent_list))
+               rgd = list_entry(sdp->sd_rindex_recent_list.next,
+                                struct gfs2_rgrpd, rd_recent);
 out:
        spin_unlock(&sdp->sd_rindex_spin);
        return rgd;
@@ -1078,7 +1114,7 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
 
        /* Try recently successful rgrps */
 
-       rgd = recent_rgrp_first(sdp, ip->i_last_rg_alloc);
+       rgd = recent_rgrp_first(sdp, ip->i_goal);
 
        while (rgd) {
                rg_locked = 0;
@@ -1162,8 +1198,6 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
        }
 
 out:
-       ip->i_last_rg_alloc = rgd->rd_addr;
-
        if (begin) {
                recent_rgrp_add(rgd);
                rgd = gfs2_rgrpd_get_next(rgd);
@@ -1286,6 +1320,7 @@ unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block)
  * @goal: the goal block within the RG (start here to search for avail block)
  * @old_state: GFS2_BLKST_XXX the before-allocation state to find
  * @new_state: GFS2_BLKST_XXX the after-allocation block state
+ * @n: The extent length
  *
  * Walk rgrp's bitmap to find bits that represent a block in @old_state.
  * Add the found bitmap buffer to the transaction.
@@ -1301,13 +1336,17 @@ unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block)
  */
 
 static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
-                       unsigned char old_state, unsigned char new_state)
+                       unsigned char old_state, unsigned char new_state,
+                       unsigned int *n)
 {
        struct gfs2_bitmap *bi = NULL;
-       u32 length = rgd->rd_length;
+       const u32 length = rgd->rd_length;
        u32 blk = 0;
        unsigned int buf, x;
+       const unsigned int elen = *n;
+       const u8 *buffer;
 
+       *n = 0;
        /* Find bitmap block that contains bits for goal block */
        for (buf = 0; buf < length; buf++) {
                bi = rgd->rd_bits + buf;
@@ -1328,7 +1367,7 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
        for (x = 0; x <= length; x++) {
                /* The GFS2_BLKST_UNLINKED state doesn't apply to the clone
                   bitmaps, so we must search the originals for that. */
-               const u8 *buffer = bi->bi_bh->b_data + bi->bi_offset;
+               buffer = bi->bi_bh->b_data + bi->bi_offset;
                if (old_state != GFS2_BLKST_UNLINKED && bi->bi_clone)
                        buffer = bi->bi_clone + bi->bi_offset;
 
@@ -1343,12 +1382,23 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
        }
 
        if (blk != BFITNOENT && old_state != new_state) {
+               *n = 1;
                gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
-               gfs2_setbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
+               gfs2_setbit(rgd, bi->bi_bh->b_data, bi->bi_clone, bi->bi_offset,
                            bi->bi_len, blk, new_state);
-               if (bi->bi_clone)
-                       gfs2_setbit(rgd, bi->bi_clone + bi->bi_offset,
-                                   bi->bi_len, blk, new_state);
+               goal = blk;
+               while (*n < elen) {
+                       goal++;
+                       if (goal >= (bi->bi_len * GFS2_NBBY))
+                               break;
+                       if (gfs2_testbit(rgd, buffer, bi->bi_len, goal) !=
+                           GFS2_BLKST_FREE)
+                               break;
+                       gfs2_setbit(rgd, bi->bi_bh->b_data, bi->bi_clone,
+                                   bi->bi_offset, bi->bi_len, goal,
+                                   new_state);
+                       (*n)++;
+               }
        }
 
        return (blk == BFITNOENT) ? blk : (bi->bi_start * GFS2_NBBY) + blk;
@@ -1403,7 +1453,7 @@ static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart,
                               bi->bi_len);
                }
                gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
-               gfs2_setbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
+               gfs2_setbit(rgd, bi->bi_bh->b_data, NULL, bi->bi_offset,
                            bi->bi_len, buf_blk, new_state);
        }
 
@@ -1411,13 +1461,13 @@ static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart,
 }
 
 /**
- * gfs2_alloc_data - Allocate a data block
- * @ip: the inode to allocate the data block for
+ * gfs2_alloc_block - Allocate a block
+ * @ip: the inode to allocate the block for
  *
  * Returns: the allocated block
  */
 
-u64 gfs2_alloc_data(struct gfs2_inode *ip)
+u64 gfs2_alloc_block(struct gfs2_inode *ip, unsigned int *n)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
        struct gfs2_alloc *al = ip->i_alloc;
@@ -1425,77 +1475,31 @@ u64 gfs2_alloc_data(struct gfs2_inode *ip)
        u32 goal, blk;
        u64 block;
 
-       if (rgrp_contains_block(rgd, ip->i_di.di_goal_data))
-               goal = ip->i_di.di_goal_data - rgd->rd_data0;
+       if (rgrp_contains_block(rgd, ip->i_goal))
+               goal = ip->i_goal - rgd->rd_data0;
        else
-               goal = rgd->rd_last_alloc_data;
+               goal = rgd->rd_last_alloc;
 
-       blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, GFS2_BLKST_USED);
+       blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, GFS2_BLKST_USED, n);
        BUG_ON(blk == BFITNOENT);
-       rgd->rd_last_alloc_data = blk;
 
+       rgd->rd_last_alloc = blk;
        block = rgd->rd_data0 + blk;
-       ip->i_di.di_goal_data = block;
+       ip->i_goal = block;
 
-       gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free);
-       rgd->rd_rg.rg_free--;
+       gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free >= *n);
+       rgd->rd_rg.rg_free -= *n;
 
        gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
        gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
 
-       al->al_alloced++;
+       al->al_alloced += *n;
 
-       gfs2_statfs_change(sdp, 0, -1, 0);
-       gfs2_quota_change(ip, +1, ip->i_inode.i_uid, ip->i_inode.i_gid);
+       gfs2_statfs_change(sdp, 0, -*n, 0);
+       gfs2_quota_change(ip, *n, ip->i_inode.i_uid, ip->i_inode.i_gid);
 
        spin_lock(&sdp->sd_rindex_spin);
-       rgd->rd_free_clone--;
-       spin_unlock(&sdp->sd_rindex_spin);
-
-       return block;
-}
-
-/**
- * gfs2_alloc_meta - Allocate a metadata block
- * @ip: the inode to allocate the metadata block for
- *
- * Returns: the allocated block
- */
-
-u64 gfs2_alloc_meta(struct gfs2_inode *ip)
-{
-       struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
-       struct gfs2_alloc *al = ip->i_alloc;
-       struct gfs2_rgrpd *rgd = al->al_rgd;
-       u32 goal, blk;
-       u64 block;
-
-       if (rgrp_contains_block(rgd, ip->i_di.di_goal_meta))
-               goal = ip->i_di.di_goal_meta - rgd->rd_data0;
-       else
-               goal = rgd->rd_last_alloc_meta;
-
-       blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, GFS2_BLKST_USED);
-       BUG_ON(blk == BFITNOENT);
-       rgd->rd_last_alloc_meta = blk;
-
-       block = rgd->rd_data0 + blk;
-       ip->i_di.di_goal_meta = block;
-
-       gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free);
-       rgd->rd_rg.rg_free--;
-
-       gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
-       gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
-
-       al->al_alloced++;
-
-       gfs2_statfs_change(sdp, 0, -1, 0);
-       gfs2_quota_change(ip, +1, ip->i_inode.i_uid, ip->i_inode.i_gid);
-       gfs2_trans_add_unrevoke(sdp, block);
-
-       spin_lock(&sdp->sd_rindex_spin);
-       rgd->rd_free_clone--;
+       rgd->rd_free_clone -= *n;
        spin_unlock(&sdp->sd_rindex_spin);
 
        return block;
@@ -1515,12 +1519,13 @@ u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation)
        struct gfs2_rgrpd *rgd = al->al_rgd;
        u32 blk;
        u64 block;
+       unsigned int n = 1;
 
-       blk = rgblk_search(rgd, rgd->rd_last_alloc_meta,
-                          GFS2_BLKST_FREE, GFS2_BLKST_DINODE);
+       blk = rgblk_search(rgd, rgd->rd_last_alloc,
+                          GFS2_BLKST_FREE, GFS2_BLKST_DINODE, &n);
        BUG_ON(blk == BFITNOENT);
 
-       rgd->rd_last_alloc_meta = blk;
+       rgd->rd_last_alloc = blk;
 
        block = rgd->rd_data0 + blk;
 
@@ -1534,7 +1539,7 @@ u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation)
        al->al_alloced++;
 
        gfs2_statfs_change(sdp, 0, -1, +1);
-       gfs2_trans_add_unrevoke(sdp, block);
+       gfs2_trans_add_unrevoke(sdp, block, 1);
 
        spin_lock(&sdp->sd_rindex_spin);
        rgd->rd_free_clone--;