/** * lookup_mp_height - helper function for lookup_metapath * @ip: the inode * @mp: the metapath * @h: the height which needs looking up */ static int lookup_mp_height(struct gfs2_inode *ip, struct metapath *mp, int h) { __be64 *ptr = metapointer(h, mp); u64 dblock = be64_to_cpu(*ptr); if (!dblock) return h + 1; return gfs2_meta_indirect_buffer(ip, h + 1, dblock, &mp->mp_bh[h + 1]); }
static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp) { unsigned int end_of_metadata = ip->i_height - 1; unsigned int x; __be64 *ptr; u64 dblock; int ret; for (x = 0; x < end_of_metadata; x++) { ptr = metapointer(x, mp); dblock = be64_to_cpu(*ptr); if (!dblock) return x + 1; ret = gfs2_meta_indirect_buffer(ip, x+1, dblock, 0, &mp->mp_bh[x+1]); if (ret) return ret; } return ip->i_height; }
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); unsigned int bsize = sdp->sd_sb.sb_bsize; const unsigned int maxlen = bh_map->b_size >> inode->i_blkbits; const u64 *arr = sdp->sd_heightsize; __be64 *ptr; u64 size; struct metapath mp; int ret; int eob; unsigned int len; struct buffer_head *bh; u8 height; 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); if (gfs2_is_dir(ip)) { bsize = sdp->sd_jbsize; arr = sdp->sd_jheightsize; } 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]) height++; find_metapath(sdp, lblock, &mp, height); ret = 1; if (height > ip->i_height || gfs2_is_stuffed(ip)) goto do_alloc; ret = lookup_metapath(ip, &mp); if (ret < 0) goto out; if (ret != 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; out: release_metapath(&mp); bmap_unlock(ip, create); return ret; do_alloc: /* All allocations are done here, firstly check create flag */ if (!create) { BUG_ON(gfs2_is_stuffed(ip)); ret = 0; goto out; } /* At this point ret is the tree depth of already allocated blocks */ ret = gfs2_bmap_alloc(inode, lblock, bh_map, &mp, ret, height, maxlen); goto out; }
static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, struct buffer_head *bh_map, struct metapath *mp, const unsigned int sheight, const unsigned int height, const unsigned int maxlen) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); struct buffer_head *dibh = mp->mp_bh[0]; u64 bn, dblock = 0; unsigned n, i, blks, alloced = 0, iblks = 0, branch_start = 0; unsigned dblks = 0; unsigned ptrs_per_blk; const unsigned end_of_metadata = height - 1; int eob = 0; enum alloc_state state; __be64 *ptr; __be64 zero_bn = 0; BUG_ON(sheight < 1); BUG_ON(dibh == NULL); gfs2_trans_add_bh(ip->i_gl, dibh, 1); if (height == sheight) { struct buffer_head *bh; /* Bottom indirect block exists, find unalloced extent size */ ptr = metapointer(end_of_metadata, mp); bh = mp->mp_bh[end_of_metadata]; dblks = gfs2_extent_length(bh->b_data, bh->b_size, ptr, maxlen, &eob); BUG_ON(dblks < 1); state = ALLOC_DATA; } else { /* Need to allocate indirect blocks */ ptrs_per_blk = height > 1 ? sdp->sd_inptrs : sdp->sd_diptrs; dblks = min(maxlen, ptrs_per_blk - mp->mp_list[end_of_metadata]); if (height == ip->i_height) { /* Writing into existing tree, extend tree down */ iblks = height - sheight; state = ALLOC_GROW_DEPTH; } else { /* Building up tree height */ state = ALLOC_GROW_HEIGHT; iblks = height - ip->i_height; branch_start = metapath_branch_start(mp); iblks += (height - branch_start); } } /* start of the second part of the function (state machine) */ blks = dblks + iblks; i = sheight; do { n = blks - alloced; bn = gfs2_alloc_block(ip, &n); alloced += n; if (state != ALLOC_DATA || gfs2_is_jdata(ip)) gfs2_trans_add_unrevoke(sdp, bn, n); switch (state) { /* Growing height of tree */ case ALLOC_GROW_HEIGHT: if (i == 1) { ptr = (__be64 *)(dibh->b_data + sizeof(struct gfs2_dinode)); zero_bn = *ptr; } for (; i - 1 < height - ip->i_height && n > 0; i++, n--) gfs2_indirect_init(mp, ip->i_gl, i, 0, bn++); if (i - 1 == height - ip->i_height) { i--; gfs2_buffer_copy_tail(mp->mp_bh[i], sizeof(struct gfs2_meta_header), dibh, sizeof(struct gfs2_dinode)); gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + sizeof(__be64)); ptr = (__be64 *)(mp->mp_bh[i]->b_data + sizeof(struct gfs2_meta_header)); *ptr = zero_bn; state = ALLOC_GROW_DEPTH; for(i = branch_start; i < height; i++) { if (mp->mp_bh[i] == NULL) break; brelse(mp->mp_bh[i]); mp->mp_bh[i] = NULL; } i = branch_start; } if (n == 0) break; /* Branching from existing tree */ case ALLOC_GROW_DEPTH: if (i > 1 && i < height) gfs2_trans_add_bh(ip->i_gl, mp->mp_bh[i-1], 1); for (; i < height && n > 0; i++, n--) gfs2_indirect_init(mp, ip->i_gl, i, mp->mp_list[i-1], bn++); if (i == height) state = ALLOC_DATA; if (n == 0) break; /* Tree complete, adding data blocks */ case ALLOC_DATA: BUG_ON(n > dblks); BUG_ON(mp->mp_bh[end_of_metadata] == NULL); gfs2_trans_add_bh(ip->i_gl, mp->mp_bh[end_of_metadata], 1); dblks = n; ptr = metapointer(end_of_metadata, mp); dblock = bn; while (n-- > 0) *ptr++ = cpu_to_be64(bn++); break; } } while (state != ALLOC_DATA); ip->i_height = height; gfs2_add_inode_blocks(&ip->i_inode, alloced); gfs2_dinode_out(ip, mp->mp_bh[0]->b_data); map_bh(bh_map, inode->i_sb, dblock); bh_map->b_size = dblks << inode->i_blkbits; set_buffer_new(bh_map); return 0; }
static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, struct buffer_head *bh_map, struct metapath *mp, const unsigned int sheight, const unsigned int height, const unsigned int maxlen) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); struct super_block *sb = sdp->sd_vfs; struct buffer_head *dibh = mp->mp_bh[0]; u64 bn, dblock = 0; unsigned n, i, blks, alloced = 0, iblks = 0, branch_start = 0; unsigned dblks = 0; unsigned ptrs_per_blk; const unsigned end_of_metadata = height - 1; int ret; int eob = 0; enum alloc_state state; __be64 *ptr; __be64 zero_bn = 0; BUG_ON(sheight < 1); BUG_ON(dibh == NULL); gfs2_trans_add_bh(ip->i_gl, dibh, 1); if (height == sheight) { struct buffer_head *bh; ptr = metapointer(end_of_metadata, mp); bh = mp->mp_bh[end_of_metadata]; dblks = gfs2_extent_length(bh->b_data, bh->b_size, ptr, maxlen, &eob); BUG_ON(dblks < 1); state = ALLOC_DATA; } else { ptrs_per_blk = height > 1 ? sdp->sd_inptrs : sdp->sd_diptrs; dblks = min(maxlen, ptrs_per_blk - mp->mp_list[end_of_metadata]); if (height == ip->i_height) { iblks = height - sheight; state = ALLOC_GROW_DEPTH; } else { state = ALLOC_GROW_HEIGHT; iblks = height - ip->i_height; branch_start = metapath_branch_start(mp); iblks += (height - branch_start); } } blks = dblks + iblks; i = sheight; do { int error; n = blks - alloced; error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL); if (error) return error; alloced += n; if (state != ALLOC_DATA || gfs2_is_jdata(ip)) gfs2_trans_add_unrevoke(sdp, bn, n); switch (state) { case ALLOC_GROW_HEIGHT: if (i == 1) { ptr = (__be64 *)(dibh->b_data + sizeof(struct gfs2_dinode)); zero_bn = *ptr; } for (; i - 1 < height - ip->i_height && n > 0; i++, n--) gfs2_indirect_init(mp, ip->i_gl, i, 0, bn++); if (i - 1 == height - ip->i_height) { i--; gfs2_buffer_copy_tail(mp->mp_bh[i], sizeof(struct gfs2_meta_header), dibh, sizeof(struct gfs2_dinode)); gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + sizeof(__be64)); ptr = (__be64 *)(mp->mp_bh[i]->b_data + sizeof(struct gfs2_meta_header)); *ptr = zero_bn; state = ALLOC_GROW_DEPTH; for(i = branch_start; i < height; i++) { if (mp->mp_bh[i] == NULL) break; brelse(mp->mp_bh[i]); mp->mp_bh[i] = NULL; } i = branch_start; } if (n == 0) break; case ALLOC_GROW_DEPTH: if (i > 1 && i < height) gfs2_trans_add_bh(ip->i_gl, mp->mp_bh[i-1], 1); for (; i < height && n > 0; i++, n--) gfs2_indirect_init(mp, ip->i_gl, i, mp->mp_list[i-1], bn++); if (i == height) state = ALLOC_DATA; if (n == 0) break; case ALLOC_DATA: BUG_ON(n > dblks); BUG_ON(mp->mp_bh[end_of_metadata] == NULL); gfs2_trans_add_bh(ip->i_gl, mp->mp_bh[end_of_metadata], 1); dblks = n; ptr = metapointer(end_of_metadata, mp); dblock = bn; while (n-- > 0) *ptr++ = cpu_to_be64(bn++); if (buffer_zeronew(bh_map)) { ret = sb_issue_zeroout(sb, dblock, dblks, GFP_NOFS); if (ret) { fs_err(sdp, "Failed to zero data buffers\n"); clear_buffer_zeronew(bh_map); } } break; } } while ((state != ALLOC_DATA) || !dblock); ip->i_height = height; gfs2_add_inode_blocks(&ip->i_inode, alloced); gfs2_dinode_out(ip, mp->mp_bh[0]->b_data); map_bh(bh_map, inode->i_sb, dblock); bh_map->b_size = dblks << inode->i_blkbits; set_buffer_new(bh_map); return 0; }
* @create: Non-zero if we may create a new meatdata block * @new: Used to indicate if we did create a new metadata block * @block: the returned disk block number * * Given a metatree, complete to a particular height, checks to see if the next * height of the tree exists. If not the next height of the tree is created. * The block number of the next height of the metadata tree is returned. * */ static int lookup_block(struct gfs2_inode *ip, struct buffer_head *bh, unsigned int height, struct metapath *mp, int create, int *new, u64 *block) { int boundary; __be64 *ptr = metapointer(bh, &boundary, height, mp); if (*ptr) { *block = be64_to_cpu(*ptr); return boundary; } *block = 0; if (!create) return 0; if (height == ip->i_di.di_height - 1 && !gfs2_is_dir(ip)) *block = gfs2_alloc_data(ip); else *block = gfs2_alloc_meta(ip);