This patch implements iomap for block mapping, and switches the block_map function to use it under the covers.
Signed-off-by: Bob Peterson <[email protected]> --- fs/gfs2/bmap.c | 244 ++++++++++++++++++++++++++++++++++++++++++--------------- fs/gfs2/bmap.h | 4 + 2 files changed, 185 insertions(+), 63 deletions(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 774bdb8..b1bcdd6 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -13,6 +13,7 @@ #include <linux/blkdev.h> #include <linux/gfs2_ondisk.h> #include <linux/crc32.h> +#include <linux/iomap.h> #include "gfs2.h" #include "incore.h" @@ -594,104 +595,221 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, } /** - * gfs2_block_map - Map a block from an inode to a disk block - * @inode: The inode - * @lblock: The logical block number - * @bh_map: The bh to be mapped - * @create: True if its ok to alloc blocks to satify the request + * hole_size - figure out the size of a hole + * @ip: The inode + * @lblock: The logical starting block number + * @mp: The metapath * - * Sets buffer_mapped() if successful, sets buffer_boundary() if a - * read of metadata will be required before the next block can be - * mapped. Sets buffer_new() if new blocks were allocated. + * Returns: The hole size in bytes * - * Returns: errno */ +static u64 hole_size(struct inode *inode, sector_t lblock, struct metapath *mp) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct metapath mp_eof; + unsigned int end_of_metadata = ip->i_height - 1; + u64 factor = 1; + int hgt = end_of_metadata; + u64 holesz = 0, holestep; + const __be64 *first, *end, *ptr; + const struct buffer_head *bh; + u64 isize_blks = (i_size_read(inode) - 1) >> inode->i_blkbits; + int zeroptrs; + bool done = false; + + /* Get another metapath, to the very last byte */ + find_metapath(sdp, isize_blks, &mp_eof, ip->i_height); + for (hgt = end_of_metadata; hgt >= 0 && !done; hgt--) { + bh = mp->mp_bh[hgt]; + if (bh) { + zeroptrs = 0; + first = metapointer(hgt, mp); + end = (const __be64 *)(bh->b_data + bh->b_size); + + for (ptr = first; ptr < end; ptr++) { + if (*ptr) { + done = true; + break; + } else { + zeroptrs++; + } + } + } else { + zeroptrs = sdp->sd_inptrs; + } + holestep = min(factor * zeroptrs, + isize_blks - (lblock + (zeroptrs * holesz))); + holesz += holestep; + if (lblock + holesz >= isize_blks) { + holesz = isize_blks - lblock; + break; + } -int gfs2_block_map(struct inode *inode, sector_t lblock, - struct buffer_head *bh_map, int create) + factor *= sdp->sd_inptrs; + if (hgt && (mp->mp_list[hgt - 1] < mp_eof.mp_list[hgt - 1])) + (mp->mp_list[hgt - 1])++; + } + return holesz << inode->i_blkbits; +} + +/** + * gfs2_get_iomap - Map blocks from an inode to disk blocks + * @mapping: The address space + * @pos: Starting position in bytes + * @length: Length to map, in bytes + * @iomap: The iomap structure + * @mp: An uninitialized metapath structure + * @release_mpath: if true, we need to release the metapath when done + * + * Returns: errno + */ +static int _gfs2_get_iomap(struct inode *inode, loff_t pos, ssize_t length, + struct iomap *iomap, struct metapath *mp, + bool release_mpath) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); unsigned int bsize = sdp->sd_sb.sb_bsize; - const size_t maxlen = bh_map->b_size >> inode->i_blkbits; const u64 *arr = sdp->sd_heightsize; __be64 *ptr; - u64 size; - struct metapath mp; + sector_t lblock = pos >> sdp->sd_sb.sb_bsize_shift; int ret; int eob; unsigned int len; struct buffer_head *bh; u8 height; - bool zero_new = false; - sector_t dblock; - unsigned int dblks = 0; - - BUG_ON(maxlen == 0); - - memset(mp.mp_bh, 0, sizeof(mp.mp_bh)); - bmap_lock(ip, create); - clear_buffer_mapped(bh_map); - clear_buffer_new(bh_map); - clear_buffer_boundary(bh_map); - trace_gfs2_bmap(ip, bh_map, lblock, create, 1); + loff_t isize = i_size_read(inode); + + if (length == 0) + return -EINVAL; + + iomap->offset = pos; + iomap->blkno = IOMAP_NULL_BLOCK; + iomap->type = IOMAP_HOLE; + iomap->length = length; + mp->mp_aheight = 1; + memset(&mp->mp_bh, 0, sizeof(mp->mp_bh)); + bmap_lock(ip, 0); if (gfs2_is_dir(ip)) { bsize = sdp->sd_jbsize; arr = sdp->sd_jheightsize; } - ret = gfs2_meta_inode_buffer(ip, &mp.mp_bh[0]); + ret = gfs2_meta_inode_buffer(ip, &(mp->mp_bh[0])); if (ret) goto out; height = ip->i_height; - size = (lblock + 1) * bsize; - while (size > arr[height]) + while ((lblock + 1) * bsize > arr[height]) height++; - find_metapath(sdp, lblock, &mp, height); - mp.mp_aheight = 1; + find_metapath(sdp, lblock, mp, height); if (height > ip->i_height || gfs2_is_stuffed(ip)) - goto do_alloc; - ret = lookup_metapath(ip, &mp); + goto out; + + ret = lookup_metapath(ip, mp); if (ret < 0) goto out; - if (mp.mp_aheight != ip->i_height) - goto do_alloc; - ptr = metapointer(ip->i_height - 1, &mp); - if (*ptr == 0) - goto do_alloc; - map_bh(bh_map, inode->i_sb, be64_to_cpu(*ptr)); - bh = mp.mp_bh[ip->i_height - 1]; - len = gfs2_extent_length(bh->b_data, bh->b_size, ptr, maxlen, &eob); - bh_map->b_size = (len << inode->i_blkbits); - if (eob) - set_buffer_boundary(bh_map); + ret = 0; + if (mp->mp_aheight != ip->i_height) { + iomap->length = hole_size(inode, lblock, mp); + goto out; + } + + ptr = metapointer(ip->i_height - 1, mp); + if (*ptr) { + iomap->type = IOMAP_MAPPED; + iomap->blkno = be64_to_cpu(*ptr); + } + + bh = mp->mp_bh[ip->i_height - 1]; + len = gfs2_extent_length(bh->b_data, bh->b_size, ptr, + length >> inode->i_blkbits, &eob); + iomap->length = len << sdp->sd_sb.sb_bsize_shift; + /* If we go past eof, round up to the nearest block */ + if (iomap->offset + iomap->length >= isize) + iomap->length = round_up((isize - iomap->offset), bsize); + out: - release_metapath(&mp); - trace_gfs2_bmap(ip, bh_map, lblock, create, ret); - bmap_unlock(ip, create); + if (ret || release_mpath) + release_metapath(mp); + bmap_unlock(ip, 0); return ret; +} + +int gfs2_get_iomap(struct inode *inode, loff_t pos, ssize_t length, + struct iomap *iomap) +{ + struct metapath mp; + + return _gfs2_get_iomap(inode, pos, length, iomap, &mp, true); +} + +/** + * gfs2_block_map - Map a block from an inode to a disk block + * @inode: The inode + * @lblock: The logical block number + * @bh_map: The bh to be mapped + * @create: True if its ok to alloc blocks to satify the request + * + * Sets buffer_mapped() if successful, sets buffer_boundary() if a + * read of metadata will be required before the next block can be + * mapped. Sets buffer_new() if new blocks were allocated. + * + * Returns: errno + */ -do_alloc: - /* All allocations are done here, firstly check create flag */ - if (!create) { - BUG_ON(gfs2_is_stuffed(ip)); - ret = 0; +int gfs2_block_map(struct inode *inode, sector_t lblock, + struct buffer_head *bh_map, int create) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + const size_t maxlen = bh_map->b_size >> inode->i_blkbits; + struct metapath mp; + struct iomap iomap; + sector_t dblock; + unsigned int dblks; + bool zero_new = false; + int ret; + + BUG_ON(maxlen == 0); + + clear_buffer_mapped(bh_map); + clear_buffer_new(bh_map); + clear_buffer_boundary(bh_map); + trace_gfs2_bmap(ip, bh_map, lblock, create, 1); + + /* If we're asked to alloc any missing blocks, we need to preserve + * the metapath buffers and release them ourselves. */ + ret = _gfs2_get_iomap(inode, lblock << sdp->sd_sb.sb_bsize_shift, + bh_map->b_size, &iomap, &mp, false); + + bh_map->b_size = iomap.length; + if (maxlen >= iomap.length) + set_buffer_boundary(bh_map); + + if (ret) goto out; - } - /* At this point ret is the tree depth of already allocated blocks */ - if (buffer_zeronew(bh_map)) - zero_new = true; - ret = gfs2_bmap_alloc(inode, lblock, zero_new, &mp, maxlen, &dblock, - &dblks); - if (ret == 0) { - map_bh(bh_map, inode->i_sb, dblock); - bh_map->b_size = dblks << inode->i_blkbits; - set_buffer_new(bh_map); + if (iomap.type == IOMAP_MAPPED) { + map_bh(bh_map, inode->i_sb, iomap.blkno); + bh_map->b_size = iomap.length; + } else if (create) { + if (buffer_zeronew(bh_map)) + zero_new = true; + ret = gfs2_bmap_alloc(inode, lblock, zero_new, &mp, maxlen, + &dblock, &dblks); + if (ret == 0) { + map_bh(bh_map, inode->i_sb, dblock); + bh_map->b_size = dblks << inode->i_blkbits; + set_buffer_new(bh_map); + } } - goto out; + release_metapath(&mp); +out: + trace_gfs2_bmap(ip, bh_map, lblock, create, ret); + return ret; } /* diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index 81ded5e..8da2429 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -10,6 +10,8 @@ #ifndef __BMAP_DOT_H__ #define __BMAP_DOT_H__ +#include <linux/iomap.h> + #include "inode.h" struct inode; @@ -47,6 +49,8 @@ static inline void gfs2_write_calc_reserv(const struct gfs2_inode *ip, extern int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page); extern int gfs2_block_map(struct inode *inode, sector_t lblock, struct buffer_head *bh, int create); +extern int gfs2_get_iomap(struct inode *inode, loff_t pos, + ssize_t length, struct iomap *iomap); extern int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen); extern int gfs2_setattr_size(struct inode *inode, u64 size); -- 2.7.4
