Before this patch, when locking a resource group, gfs2 would read in the
resource group header and all the bitmap buffers of the resource group.
Those buffers would then be locked into memory until the resource group
is unlocked, which will happen when the filesystem is unmounted or when
transferring the resource group lock to another node, but not due to
memory pressure.  Larger resource groups lock more buffers into memory,
and cause more unnecessary I/O when resource group locks are transferred
between nodes.

With this patch, when locking a resource group, only the resource group
header is read in.  The other bitmap buffers (the resource group header
contains part of the bitmap) are only read in on demand.

It would probably make sense to also only read in the resource group
header on demand, when the resource group is modified, but since the
header contains the number of free blocks in the resource group, there
is a higher incentive to keep the header locked in memory after that.

Signed-off-by: Andreas Gruenbacher <agrue...@redhat.com>
---
 fs/gfs2/incore.h |   1 -
 fs/gfs2/rgrp.c   | 382 ++++++++++++++++++++++++++++++-----------------
 2 files changed, 241 insertions(+), 142 deletions(-)

diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 51eb41484e9af..d39c26b950121 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -87,7 +87,6 @@ struct gfs2_log_operations {
  * be reallocated in that same transaction.
  */
 struct gfs2_bitmap {
-       struct buffer_head *bi_bh;
        char *bi_clone;
        unsigned long bi_flags;
        u32 bi_offset;
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index 339d6b064f1fc..503ea6f18ed74 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -62,6 +62,7 @@
 
 struct gfs2_rbm {
        struct gfs2_rgrpd *rgd;
+       struct buffer_head *bh;
        u32 offset;             /* The offset is bitmap relative */
        int bii;                /* Bitmap index */
 };
@@ -112,8 +113,8 @@ static inline void gfs2_setbit(const struct gfs2_rbm *rbm, 
bool do_clone,
        unsigned int buflen = bi->bi_bytes;
        const unsigned int bit = (rbm->offset % GFS2_NBBY) * GFS2_BIT_SIZE;
 
-       byte1 = bi->bi_bh->b_data + bi->bi_offset + (rbm->offset / GFS2_NBBY);
-       end = bi->bi_bh->b_data + bi->bi_offset + buflen;
+       byte1 = rbm->bh->b_data + bi->bi_offset + (rbm->offset / GFS2_NBBY);
+       end = rbm->bh->b_data + bi->bi_offset + buflen;
 
        BUG_ON(byte1 >= end);
 
@@ -126,7 +127,7 @@ static inline void gfs2_setbit(const struct gfs2_rbm *rbm, 
bool do_clone,
                        rbm->offset, cur_state, new_state);
                fs_warn(sdp, "rgrp=0x%llx bi_start=0x%x biblk: 0x%llx\n",
                        (unsigned long long)rbm->rgd->rd_addr, bi->bi_start,
-                       (unsigned long long)bi->bi_bh->b_blocknr);
+                       (unsigned long long)rbm->bh->b_blocknr);
                fs_warn(sdp, "bi_offset=0x%x bi_bytes=0x%x block=0x%llx\n",
                        bi->bi_offset, bi->bi_bytes,
                        (unsigned long long)gfs2_rbm_to_block(rbm));
@@ -164,7 +165,7 @@ static inline u8 gfs2_testbit(const struct gfs2_rbm *rbm, 
bool use_clone)
        if (use_clone && bi->bi_clone)
                buffer = bi->bi_clone;
        else
-               buffer = bi->bi_bh->b_data;
+               buffer = rbm->bh->b_data;
        buffer += bi->bi_offset;
        byte = buffer + (rbm->offset / GFS2_NBBY);
        bit = (rbm->offset % GFS2_NBBY) * GFS2_BIT_SIZE;
@@ -276,62 +277,152 @@ static u32 gfs2_bitfit(const u8 *buf, const unsigned int 
len,
 }
 
 /**
- * gfs2_rbm_from_block - Set the rbm based upon rgd and block number
- * @rbm: The rbm with rgd already set correctly
+ * __gfs2_rbm_get - Get the buffer head of @rbm
+ * @rbm: The rbm
+ *
+ * Get the buffer head of the bitmap block the rbm points at.
+ *
+ * Returns: 0 on success, or an error code
+ */
+static int __gfs2_rbm_get(struct gfs2_rbm *rbm) {
+       struct gfs2_rgrpd *rgd = rbm->rgd;
+       struct buffer_head *bh;
+       int error;
+
+       if (rbm->bii == 0) {
+               rbm->bh = rgd->rd_bh;
+               return 0;
+       }
+
+       /* FIXME: Might want to do read-ahead here. */
+       error = gfs2_meta_read(rgd->rd_gl, rgd->rd_addr + rbm->bii,
+                              DIO_WAIT, 0, &bh);
+       if (error)
+               goto out;
+       if (gfs2_metatype_check(rgd->rd_sbd, bh, GFS2_METATYPE_RB)) {
+               brelse(bh);
+               error = -EIO;
+               goto out;
+       }
+       rbm->bh = bh;
+out:
+       return error;
+}
+
+/**
+ * gfs2_rbm_get - Set up @rbm to point at @block
+ * @rbm: The rbm
+ * @rgd: The resource group of @block
  * @block: The block number (filesystem relative)
  *
- * This sets the bi and offset members of an rbm based on a
- * resource group and a filesystem relative block number. The
- * resource group must be set in the rbm on entry, the bi and
- * offset members will be set by this function.
+ * This sets the bii and offset of an rbm based on a resource group and a
+ * filesystem relative block number.  The rbm may hold a reference to the
+ * bitmap buffer it points to, and must be put with gfs2_rbm_put.
  *
  * Returns: 0 on success, or an error code
  */
 
-static int gfs2_rbm_from_block(struct gfs2_rbm *rbm, u64 block)
+static int gfs2_rbm_get(struct gfs2_rbm *rbm, struct gfs2_rgrpd *rgd, u64 
block)
 {
-       if (!rgrp_contains_block(rbm->rgd, block))
+       if (!rgrp_contains_block(rgd, block))
                return -E2BIG;
+
+       rbm->rgd = rgd;
        rbm->bii = 0;
-       rbm->offset = block - rbm->rgd->rd_data0;
+       rbm->offset = block - rgd->rd_data0;
        /* Check if the block is within the first block */
-       if (rbm->offset < rbm_bi(rbm)->bi_blocks)
-               return 0;
+       if (rbm->offset >= rgd->rd_bits[0].bi_blocks) {
+               u32 blocks_per_bitmap = rgd->rd_sbd->sd_blocks_per_bitmap;
 
-       /* Adjust for the size diff between gfs2_meta_header and gfs2_rgrp */
-       rbm->offset += (sizeof(struct gfs2_rgrp) -
-                       sizeof(struct gfs2_meta_header)) * GFS2_NBBY;
-       rbm->bii = rbm->offset / rbm->rgd->rd_sbd->sd_blocks_per_bitmap;
-       rbm->offset -= rbm->bii * rbm->rgd->rd_sbd->sd_blocks_per_bitmap;
-       return 0;
+               /* Adjust for the size diff between gfs2_meta_header and 
gfs2_rgrp */
+               rbm->offset += (sizeof(struct gfs2_rgrp) -
+                               sizeof(struct gfs2_meta_header)) * GFS2_NBBY;
+               rbm->bii = rbm->offset / blocks_per_bitmap;
+               rbm->offset = rbm->offset % blocks_per_bitmap;
+       }
+       return __gfs2_rbm_get(rbm);
 }
 
 /**
- * gfs2_rbm_incr - increment an rbm structure
- * @rbm: The rbm with rgd already set correctly
+ * gfs2_rbm_put - Put an rbm
+ * @rbm: The rbm
  *
- * This function takes an existing rbm structure and increments it to the next
- * viable block offset.
+ * If @rbm holds a reference on a bitmap buffer, drop that reference.
+ */
+static void gfs2_rbm_put(struct gfs2_rbm *rbm)
+{
+       if (rbm->bh != rbm->rgd->rd_bh)
+               brelse(rbm->bh);
+       rbm->bh = NULL;
+}
+
+/**
+ * gfs2_rbm_seek - Seek to a specific bitmap and offset
+ * @rbm: The rbm
+ * @bii: The bitmap index
+ * @offset: Offset within the bitmap
  *
- * Returns: If incrementing the offset would cause the rbm to go past the
- *          end of the rgrp, true is returned, otherwise false.
+ * Seek to a specific bitmap and offset in @rbm.
  *
+ * Returns: 0 on success, or an error code
  */
+static int gfs2_rbm_seek(struct gfs2_rbm *rbm, unsigned int bii, u32 offset)
+{
+       rbm->offset = offset;
+       if (rbm->bii == bii)
+               return 0;
+       gfs2_rbm_put(rbm);
+       rbm->bii = bii;
+       return __gfs2_rbm_get(rbm);
+}
 
-static bool gfs2_rbm_incr(struct gfs2_rbm *rbm)
+/**
+ * gfs2_rbm_skip - seek ahead in an rbm structure
+ * @rbm: The rbm
+ * @skip: The number of bytes to seek ahead
+ *
+ * Returns: If incrementing the offset would cause the rbm to go past the
+ *          end of the rgrp, 1 is returned.  Otherwise, 0 or an error code is
+ *          returned.
+ */
+static int gfs2_rbm_skip(struct gfs2_rbm *rbm, unsigned int skip)
 {
-       if (rbm->offset + 1 < rbm_bi(rbm)->bi_blocks) { /* in the same bitmap */
-               rbm->offset++;
-               return false;
+       struct gfs2_bitmap *bi = &rbm->rgd->rd_bits[rbm->bii];
+       u32 rest = bi->bi_blocks - rbm->offset;
+       int bii;
+
+       if (skip < rest) {
+               /* in the same bitmap */
+               rbm->offset += skip;
+               return 0;
        }
-       if (rbm->bii == rbm->rgd->rd_length - 1) /* at the last bitmap */
-               return true;
 
-       rbm->offset = 0;
-       rbm->bii++;
-       return false;
+       skip -= rest;
+       for (bii = rbm->bii + 1, bi++;
+            bii < rbm->rgd->rd_length;
+            bii++, bi++) {
+               if (skip < bi->bi_blocks)
+                       return gfs2_rbm_seek(rbm, bii, skip);
+               skip -= bi->bi_blocks;
+       }
+       return 1;
 }
 
+/**
+ * gfs2_rbm_clone - Make a copy of an rbm
+ * @to: The rbm to initialize
+ * @from: The rbm to copy from
+ *
+ * Make an independent copy of @from.  The copy must be put with gfs2_rbm_put.
+ */
+static void gfs2_rbm_clone(struct gfs2_rbm *to, const struct gfs2_rbm *from)
+{
+       memcpy(to, from, sizeof(*to));
+       if (to->bh != to->rgd->rd_bh)
+               get_bh(to->bh);
+}
+
+
 static struct gfs2_bitmap *gfs2_block_to_bitmap(struct gfs2_rgrpd *rgd,
                                                u64 block)
 {
@@ -366,7 +457,7 @@ static bool gfs2_unaligned_extlen(struct gfs2_rbm *rbm, u32 
n_unaligned, u32 *le
                if (res != GFS2_BLKST_FREE)
                        return true;
                (*len)--;
-               if (gfs2_rbm_incr(rbm))
+               if (gfs2_rbm_skip(rbm, 1))
                        return true;
        }
 
@@ -390,14 +481,15 @@ static bool gfs2_unaligned_extlen(struct gfs2_rbm *rbm, 
u32 n_unaligned, u32 *le
 
 static u32 gfs2_free_extlen(const struct gfs2_rbm *rrbm, u32 len)
 {
-       struct gfs2_rbm rbm = *rrbm;
+       struct gfs2_rbm rbm;
        u32 size = len;
        u32 bytes;
        u32 chunk_size;
        u8 *ptr, *start, *end;
-       u64 block;
        struct gfs2_bitmap *bi;
 
+       gfs2_rbm_clone(&rbm, rrbm);
+
        /* Deal with unaligned bits at the front */
        if (rbm.offset & 3) {
                u32 n_unaligned = min(4 - (rbm.offset & 3), len);
@@ -409,7 +501,7 @@ static u32 gfs2_free_extlen(const struct gfs2_rbm *rrbm, 
u32 len)
        /* Start is now byte aligned */
        while (len > 3) {
                bi = rbm_bi(&rbm);
-               start = bi->bi_bh->b_data;
+               start = rbm.bh->b_data;
                if (bi->bi_clone)
                        start = bi->bi_clone;
                start += bi->bi_offset;
@@ -422,8 +514,7 @@ static u32 gfs2_free_extlen(const struct gfs2_rbm *rrbm, 
u32 len)
                chunk_size *= GFS2_NBBY;
                BUG_ON(len < chunk_size);
                len -= chunk_size;
-               block = gfs2_rbm_to_block(&rbm);
-               if (gfs2_rbm_from_block(&rbm, block + chunk_size))
+               if (gfs2_rbm_skip(&rbm, chunk_size))
                        goto out;
                if (ptr)
                        break;
@@ -434,6 +525,7 @@ static u32 gfs2_free_extlen(const struct gfs2_rbm *rrbm, 
u32 len)
                gfs2_unaligned_extlen(&rbm, len, &len);
 
 out:
+       gfs2_rbm_put(&rbm);
        return size - len;
 }
 
@@ -480,6 +572,7 @@ static u32 gfs2_bitcount(struct gfs2_rgrpd *rgd, const u8 
*buffer,
 void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd)
 {
        struct gfs2_sbd *sdp = rgd->rd_sbd;
+       struct gfs2_rbm rbm;
        struct gfs2_bitmap *bi = NULL;
        u32 length = rgd->rd_length;
        u32 count[4], tmp;
@@ -487,16 +580,23 @@ void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd)
 
        memset(count, 0, 4 * sizeof(u32));
 
+       if (WARN_ON_ONCE(gfs2_rbm_get(&rbm, rgd, rgd->rd_data0)))
+               return;
+
        /* Count # blocks in each of 4 possible allocation states */
        for (buf = 0; buf < length; buf++) {
                bi = rgd->rd_bits + buf;
                for (x = 0; x < 4; x++)
                        count[x] += gfs2_bitcount(rgd,
-                                                 bi->bi_bh->b_data +
+                                                 rbm.bh->b_data +
                                                  bi->bi_offset,
                                                  bi->bi_bytes, x);
+               if (WARN_ON_ONCE(gfs2_rbm_skip(&rbm, bi->bi_blocks) < 0))
+                       return;
        }
 
+       gfs2_rbm_put(&rbm);
+
        if (count[0] != rgd->rd_free) {
                if (gfs2_consist_rgrpd(rgd))
                        fs_err(sdp, "free data mismatch:  %u != %u\n",
@@ -1173,15 +1273,18 @@ static int gfs2_rgrp_lvb_valid(struct gfs2_rgrpd *rgd)
 
 static u32 count_unlinked(struct gfs2_rgrpd *rgd)
 {
+       struct gfs2_rbm rbm;
        struct gfs2_bitmap *bi;
        const u32 length = rgd->rd_length;
        const u8 *buffer = NULL;
        u32 i, goal, count = 0;
 
+       if (WARN_ON_ONCE(gfs2_rbm_get(&rbm, rgd, rgd->rd_data0)))
+               goto out;
        for (i = 0, bi = rgd->rd_bits; i < length; i++, bi++) {
                goal = 0;
-               buffer = bi->bi_bh->b_data + bi->bi_offset;
-               WARN_ON(!buffer_uptodate(bi->bi_bh));
+               buffer = rbm.bh->b_data + bi->bi_offset;
+               WARN_ON(!buffer_uptodate(rbm.bh));
                while (goal < bi->bi_blocks) {
                        goal = gfs2_bitfit(buffer, bi->bi_bytes, goal,
                                           GFS2_BLKST_UNLINKED);
@@ -1190,18 +1293,23 @@ static u32 count_unlinked(struct gfs2_rgrpd *rgd)
                        count++;
                        goal++;
                }
+
+               if (WARN_ON_ONCE(gfs2_rbm_skip(&rbm, bi->bi_blocks) < 0))
+                       break;
        }
+       gfs2_rbm_put(&rbm);
 
+out:
        return count;
 }
 
 
 /**
- * gfs2_rgrp_bh_get - Read in a RG's header and bitmaps
+ * gfs2_rgrp_bh_get - Read in a RG's header
  * @rgd: the struct gfs2_rgrpd describing the RG to read in
  *
- * Read in all of a Resource Group's header and bitmap blocks.
- * Caller must eventually call gfs2_rgrp_brelse() to free the bitmaps.
+ * Read in a Resource Group's header.
+ * Caller must eventually call gfs2_rgrp_brelse.
  *
  * Returns: errno
  */
@@ -1211,8 +1319,7 @@ static int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd)
        struct gfs2_sbd *sdp = rgd->rd_sbd;
        struct gfs2_glock *gl = rgd->rd_gl;
        unsigned int length = rgd->rd_length;
-       struct gfs2_bitmap *bi;
-       unsigned int x = 0, y;
+       unsigned int x;
        int error;
 
        if (rgd->rd_bh != NULL)
@@ -1226,25 +1333,6 @@ static int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd)
                goto fail;
        }
 
-       for (x = 0; x < length; x++) {
-               bi = rgd->rd_bits + x;
-               error = gfs2_meta_read(gl, rgd->rd_addr + x, 0, 0, &bi->bi_bh);
-               if (error)
-                       goto fail;
-       }
-
-       for (y = length; y--;) {
-               bi = rgd->rd_bits + y;
-               error = gfs2_meta_wait(sdp, bi->bi_bh);
-               if (error)
-                       goto fail;
-               if (gfs2_metatype_check(sdp, bi->bi_bh, y ? GFS2_METATYPE_RB :
-                                             GFS2_METATYPE_RG)) {
-                       error = -EIO;
-                       goto fail;
-               }
-       }
-
        if (!(rgd->rd_flags & GFS2_RDF_UPTODATE)) {
                for (x = 0; x < length; x++)
                        clear_bit(GBF_FULL, &rgd->rd_bits[x].bi_flags);
@@ -1270,12 +1358,6 @@ static int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd)
        return 0;
 
 fail:
-       while (x--) {
-               bi = rgd->rd_bits + x;
-               brelse(bi->bi_bh);
-               bi->bi_bh = NULL;
-               gfs2_assert_warn(sdp, !bi->bi_clone);
-       }
        brelse(rgd->rd_bh);
        rgd->rd_bh = NULL;
 
@@ -1316,22 +1398,13 @@ int gfs2_rgrp_go_lock(struct gfs2_holder *gh)
 }
 
 /**
- * gfs2_rgrp_brelse - Release RG bitmaps read in with gfs2_rgrp_bh_get()
+ * gfs2_rgrp_brelse - Release RG header read in with gfs2_rgrp_bh_get()
  * @rgd: The resource group
  *
  */
 
 void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd)
 {
-       int x, length = rgd->rd_length;
-
-       for (x = 0; x < length; x++) {
-               struct gfs2_bitmap *bi = rgd->rd_bits + x;
-               if (bi->bi_bh) {
-                       brelse(bi->bi_bh);
-                       bi->bi_bh = NULL;
-               }
-       }
        brelse(rgd->rd_bh);
        rgd->rd_bh = NULL;
 }
@@ -1356,7 +1429,11 @@ int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 
offset,
                            struct buffer_head *bh, struct gfs2_rgrpd *rgd,
                            unsigned int bii, unsigned minlen, u64 *ptrimmed)
 {
-       struct gfs2_bitmap *bi = rgd->rd_bits + bii;
+       struct gfs2_rbm rbm = {
+               .rgd = rgd,
+               .bii = bii,
+       };
+       struct gfs2_bitmap *bi;
        struct super_block *sb = sdp->sd_vfs;
        u64 blk;
        sector_t start = 0;
@@ -1366,8 +1443,13 @@ int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 
offset,
        u32 trimmed = 0;
        u8 diff;
 
+       rv = __gfs2_rbm_get(&rbm);
+       if (rv)
+               goto fail;
+       bi = rbm_bi(&rbm);
+
        for (x = 0; x < bi->bi_bytes; x++) {
-               const u8 *clone = bi->bi_clone ? bi->bi_clone : 
bi->bi_bh->b_data;
+               const u8 *clone = bi->bi_clone ? bi->bi_clone : rbm.bh->b_data;
                clone += bi->bi_offset;
                clone += x;
                if (bh) {
@@ -1411,13 +1493,16 @@ int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 
offset,
        }
        if (ptrimmed)
                *ptrimmed = trimmed;
-       return 0;
+
+out:
+       gfs2_rbm_put(&rbm);
+       return rv;
 
 fail:
        if (sdp->sd_args.ar_discard)
                fs_warn(sdp, "error %d on discard request, turning discards off 
for this filesystem\n", rv);
        sdp->sd_args.ar_discard = 0;
-       return -EIO;
+       goto out;
 }
 
 /**
@@ -1597,7 +1682,7 @@ static inline u32 rgd_free(struct gfs2_rgrpd *rgd, struct 
gfs2_blkreserv *rs)
 static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip,
                           const struct gfs2_alloc_parms *ap)
 {
-       struct gfs2_rbm rbm = { .rgd = rgd, };
+       struct gfs2_rbm rbm;
        u64 goal;
        struct gfs2_blkreserv *rs = &ip->i_res;
        u32 extlen;
@@ -1620,7 +1705,7 @@ static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct 
gfs2_inode *ip,
        else
                goal = rgd->rd_last_alloc + rgd->rd_data0;
 
-       if (WARN_ON(gfs2_rbm_from_block(&rbm, goal)))
+       if (WARN_ON(gfs2_rbm_get(&rbm, rgd, goal)))
                return;
 
        ret = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, &extlen, ip, true);
@@ -1632,6 +1717,8 @@ static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct 
gfs2_inode *ip,
                if (goal == rgd->rd_last_alloc + rgd->rd_data0)
                        rgd->rd_last_alloc = 0;
        }
+
+       gfs2_rbm_put(&rbm);
 }
 
 /**
@@ -1735,9 +1822,11 @@ static int gfs2_reservation_check_and_update(struct 
gfs2_rbm *rbm,
 fail:
                nblock = block + extlen;
        }
-       ret = gfs2_rbm_from_block(rbm, nblock);
+       ret = gfs2_rbm_skip(rbm, nblock - block);
        if (ret < 0)
                return ret;
+       if (ret > 0)
+               return -E2BIG;
        return 1;
 }
 
@@ -1764,7 +1853,7 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, 
u32 *minlen,
                         const struct gfs2_inode *ip, bool nowrap)
 {
        struct buffer_head *bh;
-       int initial_bii;
+       int initial_bii, bii;
        int first_bii = rbm->bii;
        u32 first_offset = rbm->offset;
        u32 offset;
@@ -1789,7 +1878,7 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, 
u32 *minlen,
                    (state == GFS2_BLKST_FREE))
                        goto next_bitmap;
 
-               bh = bi->bi_bh;
+               bh = rbm->bh;
                buffer = bh->b_data + bi->bi_offset;
                WARN_ON(!buffer_uptodate(bh));
                if (state != GFS2_BLKST_UNLINKED && bi->bi_clone)
@@ -1811,25 +1900,26 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 
state, u32 *minlen,
                if (ret == 0)
                        return 0;
                if (ret > 0) {
-                       n += (rbm->bii - initial_bii);
+                       n += rbm->bii - initial_bii;
                        goto next_iter;
                }
                if (ret == -E2BIG) {
                        n += rbm->bii - initial_bii;
-                       rbm->bii = 0;
-                       rbm->offset = 0;
-                       goto res_covered_end_of_rgrp;
+                       bii = 0;
+                       goto this_bitmap;
                }
                return ret;
 
 next_bitmap:   /* Find next bitmap in the rgrp */
-               rbm->offset = 0;
-               rbm->bii++;
-               if (rbm->bii == rbm->rgd->rd_length)
-                       rbm->bii = 0;
-res_covered_end_of_rgrp:
-               if ((rbm->bii == 0) && nowrap)
+               bii = rbm->bii + 1;
+               if (bii == rbm->rgd->rd_length)
+                       bii = 0;
+this_bitmap:
+               if (bii == 0 && nowrap)
                        break;
+               ret = gfs2_rbm_seek(rbm, bii, 0);
+               if (ret)
+                       return ret;
                n++;
 next_iter:
                if (n >= iters)
@@ -1849,10 +1939,8 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, 
u32 *minlen,
        /* If the maximum extent we found is big enough to fulfill the
           minimum requirements, use it anyway. */
        if (maxext.len) {
-               rbm->bii = maxext.bii;
-               rbm->offset = maxext.offset;
                *minlen = maxext.len;
-               return 0;
+               return gfs2_rbm_seek(rbm, maxext.bii, maxext.offset);
        }
 
        return -ENOSPC;
@@ -1876,7 +1964,11 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 
*last_unlinked, u64 skip
        struct gfs2_inode *ip;
        int error;
        int found = 0;
-       struct gfs2_rbm rbm = { .rgd = rgd, .bii = 0, .offset = 0 };
+       struct gfs2_rbm rbm;
+
+       error = gfs2_rbm_get(&rbm, rgd, rgd->rd_data0);
+       if (error)
+               return;
 
        while (1) {
                down_write(&sdp->sd_log_flush_lock);
@@ -1889,17 +1981,15 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 
*last_unlinked, u64 skip
                        break;
 
                block = gfs2_rbm_to_block(&rbm);
-               if (gfs2_rbm_from_block(&rbm, block + 1))
-                       break;
                if (*last_unlinked != NO_BLOCK && block <= *last_unlinked)
-                       continue;
+                       goto next;
                if (block == skip)
-                       continue;
+                       goto next;
                *last_unlinked = block;
 
                error = gfs2_glock_get(sdp, block, &gfs2_iopen_glops, CREATE, 
&gl);
                if (error)
-                       continue;
+                       goto next;
 
                /* If the inode is already in cache, we can ignore it here
                 * because the existing inode disposal code will deal with
@@ -1917,11 +2007,17 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 
*last_unlinked, u64 skip
 
                /* Limit reclaim to sensible number of tasks */
                if (found > NR_CPUS)
-                       return;
+                       goto out;
+
+next:
+               if (gfs2_rbm_skip(&rbm, 1))
+                       break;
        }
 
        rgd->rd_flags &= ~GFS2_RDF_CHECK;
-       return;
+
+out:
+       gfs2_rbm_put(&rbm);
 }
 
 /**
@@ -2231,13 +2327,13 @@ static void gfs2_alloc_extent(struct gfs2_rbm *rbm, 
bool dinode,
        int ret;
 
        *n = 1;
-       gfs2_trans_add_meta(rbm->rgd->rd_gl, rbm_bi(rbm)->bi_bh);
+       gfs2_trans_add_meta(rbm->rgd->rd_gl, rbm->bh);
        gfs2_setbit(rbm, true, dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
        while (*n < elen) {
-               ret = gfs2_rbm_incr(rbm);
+               ret = gfs2_rbm_skip(rbm, 1);
                if (ret || gfs2_testbit(rbm, true) != GFS2_BLKST_FREE)
                        break;
-               gfs2_trans_add_meta(rbm->rgd->rd_gl, rbm_bi(rbm)->bi_bh);
+               gfs2_trans_add_meta(rbm->rgd->rd_gl, rbm->bh);
                gfs2_setbit(rbm, true, GFS2_BLKST_USED);
                (*n)++;
        }
@@ -2258,25 +2354,27 @@ static void rgblk_free(struct gfs2_sbd *sdp, struct 
gfs2_rgrpd *rgd,
        struct gfs2_rbm rbm;
        struct gfs2_bitmap *bi, *bi_prev = NULL;
 
-       rbm.rgd = rgd;
-       if (WARN_ON_ONCE(gfs2_rbm_from_block(&rbm, bstart)))
+       if (WARN_ON_ONCE(gfs2_rbm_get(&rbm, rgd, bstart)))
                return;
+
        while (blen--) {
                bi = rbm_bi(&rbm);
                if (bi != bi_prev) {
                        if (!bi->bi_clone) {
-                               bi->bi_clone = kmalloc(bi->bi_bh->b_size,
+                               bi->bi_clone = kmalloc(rbm.bh->b_size,
                                                      GFP_NOFS | __GFP_NOFAIL);
                                memcpy(bi->bi_clone + bi->bi_offset,
-                                      bi->bi_bh->b_data + bi->bi_offset,
+                                      rbm.bh->b_data + bi->bi_offset,
                                       bi->bi_bytes);
                        }
-                       gfs2_trans_add_meta(rbm.rgd->rd_gl, bi->bi_bh);
+                       gfs2_trans_add_meta(rbm.rgd->rd_gl, rbm.bh);
                        bi_prev = bi;
                }
                gfs2_setbit(&rbm, false, new_state);
-               gfs2_rbm_incr(&rbm);
+               gfs2_rbm_skip(&rbm, 1);
        }
+
+       gfs2_rbm_put(&rbm);
 }
 
 /**
@@ -2376,22 +2474,17 @@ static void gfs2_adjust_reservation(struct gfs2_inode 
*ip,
  * inode's goal block or the last allocation point in the rgrp.
  */
 
-static void gfs2_set_alloc_start(struct gfs2_rbm *rbm,
-                                const struct gfs2_inode *ip, bool dinode)
+static int gfs2_alloc_goal(const struct gfs2_inode *ip, bool dinode)
 {
-       u64 goal;
+       struct gfs2_rgrpd *rgd = ip->i_res.rs_rgd;
 
        if (gfs2_rs_active(&ip->i_res)) {
-               goal = ip->i_res.rs_start;
+               return ip->i_res.rs_start;
        } else {
-               if (!dinode && rgrp_contains_block(rbm->rgd, ip->i_goal))
-                       goal = ip->i_goal;
+               if (!dinode && rgrp_contains_block(rgd, ip->i_goal))
+                       return ip->i_goal;
                else
-                       goal = rbm->rgd->rd_last_alloc + rbm->rgd->rd_data0;
-       }
-       if (WARN_ON_ONCE(gfs2_rbm_from_block(rbm, goal))) {
-               rbm->bii = 0;
-               rbm->offset = 0;
+                       return rgd->rd_last_alloc + rgd->rd_data0;
        }
 }
 
@@ -2411,16 +2504,21 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, 
unsigned int *nblocks,
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
        struct buffer_head *dibh;
-       struct gfs2_rbm rbm = { .rgd = ip->i_res.rs_rgd, };
+       struct gfs2_rbm rbm;
        unsigned int ndata;
+       u64 goal;
        u64 block; /* block, within the file system scope */
        int error;
 
-       gfs2_set_alloc_start(&rbm, ip, dinode);
+       goal = gfs2_alloc_goal(ip, dinode);
+       if (WARN_ON_ONCE(gfs2_rbm_get(&rbm, ip->i_res.rs_rgd, goal)))
+               return -EIO;
        error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, NULL, ip, false);
-
        if (error == -ENOSPC) {
-               gfs2_set_alloc_start(&rbm, ip, dinode);
+               gfs2_rbm_put(&rbm);
+               goal = gfs2_alloc_goal(ip, dinode);
+               if (WARN_ON_ONCE(gfs2_rbm_get(&rbm, ip->i_res.rs_rgd, goal)))
+                       return -EIO;
                error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, NULL, NULL, false);
        }
 
@@ -2480,10 +2578,12 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, 
unsigned int *nblocks,
        trace_gfs2_block_alloc(ip, rbm.rgd, block, *nblocks,
                               dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
        *bn = block;
+       gfs2_rbm_put(&rbm);
        return 0;
 
 rgrp_error:
        gfs2_rgrp_error(rbm.rgd);
+       gfs2_rbm_put(&rbm);
        return -EIO;
 }
 
@@ -2596,8 +2696,7 @@ int gfs2_check_blk_type(struct gfs2_sbd *sdp, u64 
no_addr, unsigned int type)
        if (error)
                goto fail;
 
-       rbm.rgd = rgd;
-       error = gfs2_rbm_from_block(&rbm, no_addr);
+       error = gfs2_rbm_get(&rbm, rgd, no_addr);
        if (WARN_ON_ONCE(error))
                goto fail;
 
@@ -2605,6 +2704,7 @@ int gfs2_check_blk_type(struct gfs2_sbd *sdp, u64 
no_addr, unsigned int type)
                error = -ESTALE;
 
        gfs2_glock_dq_uninit(&rgd_gh);
+       gfs2_rbm_put(&rbm);
 fail:
        return error;
 }
-- 
2.20.1

Reply via email to