[GFS2] Readpages support

This adds readpages support (and also corrects a small bug in
the readpage error path at the same time). Hopefully this will
improve performance by allowing GFS to submit larger lumps of
I/O at a time.

In order to simplify the setting of BH_Boundary, it currently gets
set when we hit the end of a indirect pointer block. There is
always a boundary at this point with the current allocation code.
It doesn't get all the boundaries right though, so there is still
room for improvement in this.

See comments in fs/gfs2/ops_address.c for further information about
readpages with GFS2.

Signed-off-by: Steven Whitehouse
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index cfe1a42..474b9a1 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -314,13 +314,17 @@
  * metadata tree.
  */
 
-static inline uint64_t *metapointer(struct buffer_head *bh,
-				    unsigned int height, struct metapath *mp)
+static inline u64 *metapointer(struct buffer_head *bh, int *boundary,
+			       unsigned int height, const struct metapath *mp)
 {
 	unsigned int head_size = (height > 0) ?
 		sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode);
-
-	return ((uint64_t *)(bh->b_data + head_size)) + mp->mp_list[height];
+	u64 *ptr;
+	*boundary = 0;
+	ptr = ((u64 *)(bh->b_data + head_size)) + mp->mp_list[height];
+	if (ptr + 1 == (u64*)(bh->b_data + bh->b_size))
+		*boundary = 1;
+	return ptr;
 }
 
 /**
@@ -339,24 +343,24 @@
  *
  */
 
-static void lookup_block(struct gfs2_inode *ip, struct buffer_head *bh,
-			 unsigned int height, struct metapath *mp, int create,
-			 int *new, uint64_t *block)
+static int lookup_block(struct gfs2_inode *ip, struct buffer_head *bh,
+			unsigned int height, struct metapath *mp, int create,
+			int *new, uint64_t *block)
 {
-	uint64_t *ptr = metapointer(bh, height, mp);
+	int boundary;
+	uint64_t *ptr = metapointer(bh, &boundary, height, mp);
 
 	if (*ptr) {
 		*block = be64_to_cpu(*ptr);
-		return;
+		return boundary;
 	}
 
 	*block = 0;
 
 	if (!create)
-		return;
+		return 0;
 
-	if (height == ip->i_di.di_height - 1 &&
-	    !gfs2_is_dir(ip))
+	if (height == ip->i_di.di_height - 1 && !gfs2_is_dir(ip))
 		*block = gfs2_alloc_data(ip);
 	else
 		*block = gfs2_alloc_meta(ip);
@@ -367,15 +371,16 @@
 	ip->i_di.di_blocks++;
 
 	*new = 1;
+	return 0;
 }
 
 /**
- * gfs2_block_map - Map a block from an inode to a disk block
- * @ip: The GFS2 inode
+ * 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)
- * @dblock: the disk block number of the start of an extent
- * @extlen: the size of the extent
+ * @boundary: gets set if we've hit a block boundary
+ * @mp: metapath to use
  *
  * Find the block number on the current device which corresponds to an
  * inode's block. If the block had to be created, "new" will be set.
@@ -383,12 +388,14 @@
  * Returns: errno
  */
 
-int gfs2_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
-		   uint64_t *dblock, uint32_t *extlen)
+static struct buffer_head *gfs2_block_pointers(struct inode *inode, u64 lblock,
+					       int *new, u64 *dblock,
+					       int *boundary,
+					       struct metapath *mp)
 {
+	struct gfs2_inode *ip = inode->u.generic_ip;
 	struct gfs2_sbd *sdp = ip->i_sbd;
 	struct buffer_head *bh;
-	struct metapath mp;
 	int create = *new;
 	unsigned int bsize;
 	unsigned int height;
@@ -398,13 +405,6 @@
 
 	*new = 0;
 	*dblock = 0;
-	if (extlen)
-		*extlen = 0;
-
-	if (create)
-		down_write(&ip->i_rw_mutex);
-	else
-		down_read(&ip->i_rw_mutex);
 
 	if (gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip)))
 		goto out;
@@ -421,7 +421,7 @@
 			goto out;
 	}
 
-	find_metapath(ip, lblock, &mp);
+	find_metapath(ip, lblock, mp);
 	end_of_metadata = ip->i_di.di_height - 1;
 
 	error = gfs2_meta_inode_buffer(ip, &bh);
@@ -429,7 +429,7 @@
 		goto out;
 
 	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;
@@ -439,49 +439,95 @@
 			goto out;
 	}
 
-	lookup_block(ip, bh, end_of_metadata, &mp, create, new, dblock);
-
-	if (extlen && *dblock) {
-		*extlen = 1;
-
-		if (!*new) {
-			uint64_t tmp_dblock;
-			int tmp_new;
-			unsigned int nptrs;
-
-			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)++;
-			}
-		}
-	}
-
-	brelse(bh);
-
+	*boundary = lookup_block(ip, bh, end_of_metadata, mp, create, new, dblock);
 	if (*new) {
-		error = gfs2_meta_inode_buffer(ip, &bh);
+		struct buffer_head *dibh;
+		error = gfs2_meta_inode_buffer(ip, &dibh);
 		if (!error) {
-			gfs2_trans_add_bh(ip->i_gl, bh, 1);
-			gfs2_dinode_out(&ip->i_di, bh->b_data);
-			brelse(bh);
+			gfs2_trans_add_bh(ip->i_gl, dibh, 1);
+			gfs2_dinode_out(&ip->i_di, dibh->b_data);
+			brelse(dibh);
 		}
 	}
+	return bh;
+out:
+	return ERR_PTR(error);
+}
 
- out:
+
+static inline void bmap_lock(struct inode *inode, int create)
+{
+	struct gfs2_inode *ip = inode->u.generic_ip;
+	if (create)
+		down_write(&ip->i_rw_mutex);
+	else
+		down_read(&ip->i_rw_mutex);
+}
+
+static inline void bmap_unlock(struct inode *inode, int create)
+{
+	struct gfs2_inode *ip = inode->u.generic_ip;
 	if (create)
 		up_write(&ip->i_rw_mutex);
 	else
 		up_read(&ip->i_rw_mutex);
+}
 
-	return error;
+int gfs2_block_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, int *boundary)
+{
+	struct metapath mp;
+	struct buffer_head *bh;
+	int create = *new;
+
+	bmap_lock(inode, create);
+	bh = gfs2_block_pointers(inode, lblock, new, dblock, boundary, &mp);
+	bmap_unlock(inode, create);
+	if (!bh)
+		return 0;
+	if (IS_ERR(bh))
+		return PTR_ERR(bh);
+	brelse(bh);
+	return 0;
+}
+
+int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen)
+{
+	struct gfs2_inode *ip = inode->u.generic_ip;
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct metapath mp;
+	struct buffer_head *bh;
+	int boundary;
+	int create = *new;
+
+	BUG_ON(!extlen);
+	BUG_ON(!dblock);
+	BUG_ON(!new);
+
+	bmap_lock(inode, create);
+	bh = gfs2_block_pointers(inode, lblock, new, dblock, &boundary, &mp);
+	*extlen = 1;
+
+	if (bh && !IS_ERR(bh) && *dblock && !*new) {
+		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)++;
+		}
+	}
+	bmap_unlock(inode, create);
+	if (!bh)
+		return 0;
+	if (IS_ERR(bh))
+		return PTR_ERR(bh);
+	brelse(bh);
+	return 0;
 }
 
 /**
@@ -1053,7 +1099,7 @@
 	}
 
 	for (; lblock < lblock_stop; lblock += extlen) {
-		error = gfs2_block_map(ip, lblock, &new, &dblock, &extlen);
+		error = gfs2_extent_map(ip->i_vnode, lblock, &new, &dblock, &extlen);
 		if (error)
 			return error;