diff options
Diffstat (limited to 'fs/gfs2/bmap.c')
-rw-r--r-- | fs/gfs2/bmap.c | 170 |
1 files changed, 108 insertions, 62 deletions
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 @@ 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; |