[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
This commit is contained in:
Steven Whitehouse
2006-05-05 16:59:11 -04:00
parent 5bb76af1e0
commit fd88de569b
13 changed files with 254 additions and 129 deletions

View File

@@ -314,13 +314,17 @@ static void find_metapath(struct gfs2_inode *ip, uint64_t block,
* 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 inline uint64_t *metapointer(struct buffer_head *bh,
*
*/
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 @@ static void lookup_block(struct gfs2_inode *ip, struct buffer_head *bh,
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 @@ static void lookup_block(struct gfs2_inode *ip, struct buffer_head *bh,
* 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 @@ int gfs2_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
*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 @@ int gfs2_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
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 @@ int gfs2_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
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 @@ int gfs2_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
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 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, uint64_t offset,
}
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;