Sean Bartell
2010-Mar-20 04:26 UTC
[PATCH 2/4] btrfs-convert: Add extent iteration functions.
A filesystem can have disk extents in arbitrary places on the disk, as well as extents that must be read into memory because they have compression or encryption btrfs doesn''t support. These extents can be passed to the new extent iteration functions, which will handle all the details of alignment, allocation, etc. --- convert.c | 604 ++++++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 401 insertions(+), 203 deletions(-) diff --git a/convert.c b/convert.c index c48f8ba..bd91990 100644 --- a/convert.c +++ b/convert.c @@ -357,7 +357,7 @@ error: } static int read_disk_extent(struct btrfs_root *root, u64 bytenr, - u32 num_bytes, char *buffer) + u64 num_bytes, char *buffer) { int ret; struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; @@ -371,6 +371,23 @@ fail: ret = -1; return ret; } + +static int write_disk_extent(struct btrfs_root *root, u64 bytenr, + u64 num_bytes, const char *buffer) +{ + int ret; + struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; + + ret = pwrite(fs_devs->latest_bdev, buffer, num_bytes, bytenr); + if (ret != num_bytes) + goto fail; + ret = 0; +fail: + if (ret > 0) + ret = -1; + return ret; +} + /* * Record a file extent. Do all the required works, such as inserting * file extent item, inserting extent item and backref item into extent @@ -378,8 +395,7 @@ fail: */ static int record_file_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 file_pos, u64 disk_bytenr, + u64 *inode_nbytes, u64 file_pos, u64 disk_bytenr, u64 num_bytes, int checksum) { int ret; @@ -391,7 +407,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans, struct btrfs_path path; struct btrfs_extent_item *ei; u32 blocksize = root->sectorsize; - u64 nbytes; if (disk_bytenr == 0) { ret = btrfs_insert_file_extent(trans, root, objectid, @@ -450,8 +465,7 @@ static int record_file_extent(struct btrfs_trans_handle *trans, btrfs_set_file_extent_other_encoding(leaf, fi, 0); btrfs_mark_buffer_dirty(leaf); - nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes; - btrfs_set_stack_inode_nbytes(inode, nbytes); + *inode_nbytes += num_bytes; btrfs_release_path(root, &path); @@ -492,95 +506,355 @@ fail: return ret; } -static int record_file_blocks(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 file_block, u64 disk_block, - u64 num_blocks, int checksum) -{ - u64 file_pos = file_block * root->sectorsize; - u64 disk_bytenr = disk_block * root->sectorsize; - u64 num_bytes = num_blocks * root->sectorsize; - return record_file_extent(trans, root, objectid, inode, file_pos, - disk_bytenr, num_bytes, checksum); -} - -struct blk_iterate_data { +struct extent_iterate_data { struct btrfs_trans_handle *trans; struct btrfs_root *root; - struct btrfs_inode_item *inode; + u64 *inode_nbytes; u64 objectid; - u64 first_block; - u64 disk_block; - u64 num_blocks; - u64 boundary; - int checksum; - int errcode; + int checksum, packing; + u64 last_file_off; + u64 total_size; + enum {EXTENT_ITERATE_TYPE_NONE, EXTENT_ITERATE_TYPE_MEM, + EXTENT_ITERATE_TYPE_DISK} type; + u64 size; + u64 file_off; /* always aligned to sectorsize */ + char *data; /* for mem */ + u64 disk_off; /* for disk */ }; -static int block_iterate_proc(ext2_filsys ext2_fs, - u64 disk_block, u64 file_block, - struct blk_iterate_data *idata) +static u64 extent_boundary(struct btrfs_root *root, u64 extent_start) { - int ret; - int sb_region; - int do_barrier; - struct btrfs_root *root = idata->root; - struct btrfs_trans_handle *trans = idata->trans; - struct btrfs_block_group_cache *cache; - u64 bytenr = disk_block * root->sectorsize; - - sb_region = intersect_with_sb(bytenr, root->sectorsize); - do_barrier = sb_region || disk_block >= idata->boundary; - if ((idata->num_blocks > 0 && do_barrier) || - (file_block > idata->first_block + idata->num_blocks) || - (disk_block != idata->disk_block + idata->num_blocks)) { - if (idata->num_blocks > 0) { - ret = record_file_blocks(trans, root, idata->objectid, - idata->inode, idata->first_block, - idata->disk_block, idata->num_blocks, - idata->checksum); - if (ret) - goto fail; - idata->first_block += idata->num_blocks; - idata->num_blocks = 0; + int i; + u64 offset; + u64 boundary = (u64)-1; + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + offset = btrfs_sb_offset(i); + offset &= ~((u64)STRIPE_LEN - 1); + if (offset > extent_start) { + boundary = offset; + break; + } + if (offset + STRIPE_LEN > extent_start) { + boundary = offset + STRIPE_LEN; + break; } - if (file_block > idata->first_block) { - ret = record_file_blocks(trans, root, idata->objectid, - idata->inode, idata->first_block, - 0, file_block - idata->first_block, - idata->checksum); + } + + struct btrfs_block_group_cache *cache; + cache = btrfs_lookup_block_group(root->fs_info, extent_start); + BUG_ON(!cache); + offset = cache->key.objectid + cache->key.offset; + return min_t(u64, boundary, offset); +} + +static int commit_disk_extent(struct extent_iterate_data *priv, + u64 file_pos, u64 disk_bytenr, u64 num_bytes) +{ + u64 boundary; + int ret; + if (disk_bytenr == 0) + return record_file_extent(priv->trans, priv->root, + priv->objectid, priv->inode_nbytes, + file_pos, disk_bytenr, num_bytes, + priv->checksum); + /* Break up the disk extent on blockgroup and superblock boundaries. */ + while (num_bytes) { + boundary = extent_boundary(priv->root, disk_bytenr); + u64 size = min_t(u64, boundary - disk_bytenr, num_bytes); + ret = record_file_extent(priv->trans, priv->root, + priv->objectid, priv->inode_nbytes, + file_pos, disk_bytenr, size, + priv->checksum); + if (ret) + return ret; + file_pos += size; + disk_bytenr += size; + num_bytes -= size; + } + return 0; +} + +static int commit_file_extents(struct extent_iterate_data *priv) +{ + int ret; + if (priv->type == EXTENT_ITERATE_TYPE_NONE) + return 0; + if (priv->size == 0) + return 0; + if (priv->file_off > priv->last_file_off) { + ret = commit_disk_extent(priv, priv->last_file_off, 0, + priv->file_off - priv->last_file_off); + if (ret) + return ret; + } + priv->last_file_off = priv->file_off + priv->size; + + if (priv->type == EXTENT_ITERATE_TYPE_MEM) { + /* allocate and write to disk */ + struct btrfs_key key; + ret = custom_alloc_extent(priv->root, priv->root->sectorsize, + 0, &key); + if (ret) + return ret; + ret = write_disk_extent(priv->root, key.objectid, priv->size, + priv->data); + if (ret) + return ret; + priv->type = EXTENT_ITERATE_TYPE_DISK; + priv->disk_off = key.objectid; + } + + u64 sectorsize = priv->root->sectorsize; + if (priv->size & (sectorsize - 1)) + priv->size = (priv->size & ~(sectorsize - 1)) + sectorsize; + ret = commit_disk_extent(priv, priv->file_off, priv->disk_off, + priv->size); + if (ret) + return ret; + priv->type = EXTENT_ITERATE_TYPE_NONE; + return 0; +} + +int start_file_extents(struct extent_iterate_data *priv, + struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 *inode_nbytes, + u64 objectid, int checksum, int packing, u64 total_size) +{ + priv->trans = trans; + priv->root = root; + priv->inode_nbytes = inode_nbytes; + priv->objectid = objectid; + priv->checksum = checksum; + priv->packing = packing; + priv->last_file_off = 0; + priv->type = 0; + priv->total_size = total_size; + priv->data = malloc(root->sectorsize); + if (!priv->data) + return -ENOMEM; + return 0; +} + +int start_file_extents_range(struct extent_iterate_data *priv, + struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 *inode_nbytes, + u64 objectid, int checksum, u64 start, u64 end) +{ + priv->trans = trans; + priv->root = root; + priv->inode_nbytes = inode_nbytes; + priv->objectid = objectid; + priv->checksum = checksum; + priv->packing = 0; + priv->last_file_off = start; + priv->type = 0; + priv->total_size = end; + priv->data = malloc(root->sectorsize); + if (!priv->data) + return -ENOMEM; + return 0; +} + +int finish_file_extents(struct extent_iterate_data *priv) +{ + int ret; + + if (priv->packing + && priv->type != EXTENT_ITERATE_TYPE_NONE + && priv->total_size <= BTRFS_MAX_INLINE_DATA_SIZE(priv->root)) { + priv->size = min_t(u64, priv->size, + priv->total_size - priv->file_off); + /* make inline extent */ + if (priv->type == EXTENT_ITERATE_TYPE_DISK) { + ret = read_disk_extent(priv->root, priv->disk_off, + priv->size, priv->data); if (ret) - goto fail; + return ret; } + *priv->inode_nbytes += priv->size; + return btrfs_insert_inline_extent(priv->trans, priv->root, + priv->objectid, + priv->file_off, priv->data, + priv->size); + } - if (sb_region) { - bytenr += STRIPE_LEN - 1; - bytenr &= ~((u64)STRIPE_LEN - 1); - } else { - cache = btrfs_lookup_block_group(root->fs_info, bytenr); - BUG_ON(!cache); - bytenr = cache->key.objectid + cache->key.offset; + ret = commit_file_extents(priv); + if (ret) + return ret; + + if (priv->total_size > priv->last_file_off) { + ret = commit_disk_extent(priv, priv->last_file_off, 0, + priv->total_size - priv->last_file_off); + if (ret) + return ret; + } + free(priv->data); + return 0; +} + +int add_file_mem_extent(struct extent_iterate_data *priv, u64 file_off, + u64 size, char *data); + +int add_file_disk_extent(struct extent_iterate_data *priv, u64 file_off, + u64 disk_off, u64 size) +{ + BUG_ON(file_off < priv->last_file_off); + int ret; + u64 sectorsize = priv->root->sectorsize; + u64 mask = sectorsize - 1; + if (size == 0) + return 0; + if ((file_off & mask) != (disk_off & mask)) { + /* It''s unclear how to CoW this, so don''t. */ + char *data = malloc(size); + if (!data) + return -ENOMEM; + ret = read_disk_extent(priv->root, disk_off, size, data); + if (ret) { + free(data); + return ret; } + ret = add_file_mem_extent(priv, file_off, size, data); + free(data); + return ret; + } + if (priv->type == EXTENT_ITERATE_TYPE_DISK + && priv->file_off + priv->size == file_off + && priv->disk_off + priv->size == disk_off) { + /* It''s a continuation of the same disk extent. */ + priv->size += size; + return 0; + } + if (disk_off == 0 || disk_off & mask) { + /* We need to have an aligned start, so give the first part to + * add_file_mem_extent if necessary. */ + u64 mem_size = min_t(u64, sectorsize - (disk_off & mask), size); + char *data = malloc(mem_size); + if (!data) + return -ENOMEM; + ret = read_disk_extent(priv->root, disk_off, mem_size, data); + if (ret) { + free(data); + return ret; + } + ret = add_file_mem_extent(priv, file_off, mem_size, data); + free(data); + if (ret) + return ret; + file_off += mem_size; + disk_off += mem_size; + size -= mem_size; + if (size == 0) + return 0; + } + ret = commit_file_extents(priv); + if (ret) + return ret; + priv->type = EXTENT_ITERATE_TYPE_DISK; + priv->size = size; + priv->file_off = file_off; + priv->disk_off = disk_off; + return 0; +} + +int add_file_mem_extent(struct extent_iterate_data *priv, u64 file_off, + u64 size, char *data) +{ + BUG_ON(file_off < priv->last_file_off); + int ret; + u64 sectorsize = priv->root->sectorsize; + u64 mask = sectorsize - 1; + u64 aligned_file_off = file_off & ~mask; + u32 alignment = file_off - aligned_file_off; + size += alignment; + + /* If we share a sector with a DISK extent, commit most of it and turn + * the shared part into a MEM extent. */ + if (priv->type == EXTENT_ITERATE_TYPE_DISK + && priv->file_off + priv->size > aligned_file_off) { + u64 mem_size = priv->file_off + priv->size - aligned_file_off; + ret = read_disk_extent(priv->root, aligned_file_off, mem_size, + priv->data); + if (ret) + return ret; + priv->size -= mem_size; + ret = commit_file_extents(priv); + if (ret) + return ret; + priv->type = EXTENT_ITERATE_TYPE_MEM; + priv->size = mem_size; + priv->file_off = aligned_file_off; + } + + /* Put our first sector in priv->data. If we share a sector with the + * previous extent, combine with it. */ + if (priv->type == EXTENT_ITERATE_TYPE_MEM + && priv->file_off + priv->size > aligned_file_off) { + BUG_ON(priv->file_off != aligned_file_off); + memset(priv->data + priv->size, 0, sectorsize - priv->size); + } else { + ret = commit_file_extents(priv); + if (ret) + return ret; + memset(priv->data, 0, sectorsize); + } + if (size < sectorsize) { + memcpy(priv->data + alignment, data, size - alignment); + priv->type = EXTENT_ITERATE_TYPE_MEM; + priv->file_off = aligned_file_off; + priv->size = size; + return 0; + } + memcpy(priv->data + alignment, data, sectorsize - alignment); + data += sectorsize - alignment; + + /* We have full sectors; allocate and write them. */ + u64 aligned_size = size & ~mask; + struct btrfs_key key; + ret = custom_alloc_extent(priv->root, aligned_size, 0, &key); + if (ret) + return ret; + ret = write_disk_extent(priv->root, key.objectid, + sectorsize, priv->data); + if (ret) + return ret; + ret = write_disk_extent(priv->root, key.objectid + sectorsize, + aligned_size - sectorsize, data); + if (ret) + return ret; + ret = add_file_disk_extent(priv, aligned_file_off, key.objectid, + aligned_size); + if (ret) + return ret; - idata->first_block = file_block; - idata->disk_block = disk_block; - idata->boundary = bytenr / root->sectorsize; + /* Leave the rest in priv. */ + size -= aligned_size; + if (size) { + ret = commit_file_extents(priv); + if (ret) + return ret; + aligned_file_off += aligned_size; + data += aligned_size - sectorsize; + priv->type = EXTENT_ITERATE_TYPE_MEM; + priv->file_off = aligned_file_off; + priv->size = size; + memcpy(priv->data, data, size); } - idata->num_blocks++; return 0; -fail: - idata->errcode = ret; - return BLOCK_ABORT; } static int __block_iterate_proc(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data) { - struct blk_iterate_data *idata; - idata = (struct blk_iterate_data *)priv_data; - return block_iterate_proc(fs, *blocknr, blockcnt, idata); + struct extent_iterate_data *idata; + idata = (struct extent_iterate_data *)priv_data; + u64 blocksize = fs->blocksize; + int ret = add_file_disk_extent(idata, blocksize * blockcnt, + blocksize * *blocknr, blocksize); + if (ret) + return BLOCK_ABORT; + return 0; } /* @@ -593,68 +867,23 @@ static int create_file_extents(struct btrfs_trans_handle *trans, int datacsum, int packing) { int ret; - char *buffer = NULL; errcode_t err; - u32 last_block; - u32 sectorsize = root->sectorsize; + u64 inode_nbytes = 0; u64 inode_size = btrfs_stack_inode_size(btrfs_inode); - struct blk_iterate_data data = { - .trans = trans, - .root = root, - .inode = btrfs_inode, - .objectid = objectid, - .first_block = 0, - .disk_block = 0, - .num_blocks = 0, - .boundary = (u64)-1, - .checksum = datacsum, - .errcode = 0, - }; + struct extent_iterate_data data; + ret = start_file_extents(&data, trans, root, &inode_nbytes, objectid, + datacsum, packing, inode_size); + if (ret) + return ret; err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY, NULL, __block_iterate_proc, &data); if (err) goto error; - ret = data.errcode; + ret = finish_file_extents(&data); if (ret) - goto fail; - if (packing && data.first_block == 0 && data.num_blocks > 0 && - inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) { - u64 num_bytes = data.num_blocks * sectorsize; - u64 disk_bytenr = data.disk_block * sectorsize; - u64 nbytes; - - buffer = malloc(num_bytes); - if (!buffer) - return -ENOMEM; - ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer); - if (ret) - goto fail; - if (num_bytes > inode_size) - num_bytes = inode_size; - ret = btrfs_insert_inline_extent(trans, root, objectid, - 0, buffer, num_bytes); - if (ret) - goto fail; - nbytes = btrfs_stack_inode_nbytes(btrfs_inode) + num_bytes; - btrfs_set_stack_inode_nbytes(btrfs_inode, nbytes); - } else if (data.num_blocks > 0) { - ret = record_file_blocks(trans, root, objectid, btrfs_inode, - data.first_block, data.disk_block, - data.num_blocks, data.checksum); - if (ret) - goto fail; - } - data.first_block += data.num_blocks; - last_block = (inode_size + sectorsize - 1) / sectorsize; - if (last_block > data.first_block) { - ret = record_file_blocks(trans, root, objectid, btrfs_inode, - data.first_block, 0, last_block - - data.first_block, data.checksum); - } -fail: - if (buffer) - free(buffer); - return ret; + return ret; + btrfs_set_stack_inode_nbytes(btrfs_inode, inode_nbytes); + return 0; error: fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err)); return -1; @@ -1206,52 +1435,33 @@ static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs, */ static int create_image_file_range(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, + u64 *inode_nbytes, u64 start_byte, u64 end_byte, struct extent_io_tree *orig_free_tree) { - u32 blocksize = root->sectorsize; - u32 block = start_byte / blocksize; - u32 last_block = (end_byte + blocksize - 1) / blocksize; int ret = 0; - struct blk_iterate_data data = { - .trans = trans, - .root = root, - .inode = inode, - .objectid = objectid, - .first_block = block, - .disk_block = 0, - .num_blocks = 0, - .boundary = (u64)-1, - .checksum = 0, - .errcode = 0, - }; - for (; start_byte < end_byte; block++, start_byte += blocksize) { - if (test_range_bit(orig_free_tree, start_byte, - start_byte + blocksize, EXTENT_DIRTY, 1)) - continue; - ret = block_iterate_proc(NULL, block, block, &data); - if (ret & BLOCK_ABORT) { - ret = data.errcode; - goto fail; - } - } - if (data.num_blocks > 0) { - ret = record_file_blocks(trans, root, objectid, inode, - data.first_block, data.disk_block, - data.num_blocks, 0); - if (ret) - goto fail; - data.first_block += data.num_blocks; - } - if (last_block > data.first_block) { - ret = record_file_blocks(trans, root, objectid, inode, - data.first_block, 0, last_block - - data.first_block, 0); + struct extent_iterate_data data; + ret = start_file_extents_range(&data, trans, root, inode_nbytes, + objectid, 0, start_byte, end_byte); + if (ret) + return ret; + while (start_byte < end_byte) { + u64 start, end; + ret = find_first_extent_bit(orig_free_tree, start_byte, + &start, &end, EXTENT_DIRTY); if (ret) - goto fail; + start = end_byte; + if (start > start_byte) { + u64 size = min_t(u64, start - start_byte, + end_byte - start_byte); + ret = add_file_disk_extent(&data, start_byte, + start_byte, size); + if (ret) + return ret; + } + start_byte = end + 1; } -fail: + ret = finish_file_extents(&data); return ret; } /* @@ -1279,6 +1489,7 @@ static int create_ext2_image(struct btrfs_root *root, const char *name, u64 last_byte; u64 first_free; u64 total_bytes; + u64 inode_nbytes; u32 sectorsize = root->sectorsize; total_bytes = btrfs_super_total_bytes(&fs_info->super_copy); @@ -1289,7 +1500,7 @@ static int create_ext2_image(struct btrfs_root *root, const char *name, btrfs_set_stack_inode_generation(&btrfs_inode, 1); btrfs_set_stack_inode_size(&btrfs_inode, total_bytes); btrfs_set_stack_inode_nlink(&btrfs_inode, 1); - btrfs_set_stack_inode_nbytes(&btrfs_inode, 0); + inode_nbytes = 0; btrfs_set_stack_inode_mode(&btrfs_inode, S_IFREG | 0400); btrfs_set_stack_inode_flags(&btrfs_inode, BTRFS_INODE_NODATASUM | BTRFS_INODE_READONLY); @@ -1315,7 +1526,7 @@ static int create_ext2_image(struct btrfs_root *root, const char *name, if (ret) goto fail; ret = record_file_extent(trans, root, objectid, - &btrfs_inode, last_byte, + &inode_nbytes, last_byte, key.objectid, sectorsize, 0); if (ret) goto fail; @@ -1370,12 +1581,12 @@ next: if (bytenr > last_byte) { ret = create_image_file_range(trans, root, objectid, - &btrfs_inode, last_byte, + &inode_nbytes, last_byte, bytenr, orig_free_tree); if (ret) goto fail; } - ret = record_file_extent(trans, root, objectid, &btrfs_inode, + ret = record_file_extent(trans, root, objectid, &inode_nbytes, bytenr, bytenr, num_bytes, 0); if (ret) goto fail; @@ -1392,12 +1603,14 @@ next: btrfs_release_path(root, &path); if (total_bytes > last_byte) { ret = create_image_file_range(trans, root, objectid, - &btrfs_inode, last_byte, + &inode_nbytes, last_byte, total_bytes, orig_free_tree); if (ret) goto fail; } + btrfs_set_stack_inode_nbytes(&btrfs_inode, inode_nbytes); + ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); if (ret) goto fail; @@ -1934,7 +2147,7 @@ static int relocate_one_reference(struct btrfs_trans_handle *trans, struct btrfs_key key; struct btrfs_path path; struct btrfs_inode_item inode; - struct blk_iterate_data data; + struct extent_iterate_data data; u64 bytenr; u64 num_bytes; u64 cur_offset; @@ -1990,22 +2203,14 @@ static int relocate_one_reference(struct btrfs_trans_handle *trans, btrfs_release_path(root, &path); BUG_ON(num_bytes & (sectorsize - 1)); - nbytes = btrfs_stack_inode_nbytes(&inode) - num_bytes; - btrfs_set_stack_inode_nbytes(&inode, nbytes); datacsum = !(btrfs_stack_inode_flags(&inode) & BTRFS_INODE_NODATASUM); - data = (struct blk_iterate_data) { - .trans = trans, - .root = root, - .inode = &inode, - .objectid = extent_key->objectid, - .first_block = extent_key->offset / sectorsize, - .disk_block = 0, - .num_blocks = 0, - .boundary = (u64)-1, - .checksum = datacsum, - .errcode = 0, - }; + ret = start_file_extents_range(&data, trans, root, &nbytes, + extent_key->objectid, datacsum, + extent_key->offset, + extent_key->offset + num_bytes); + if (ret) + goto fail; cur_offset = extent_key->offset; while (num_bytes > 0) { @@ -2035,26 +2240,19 @@ static int relocate_one_reference(struct btrfs_trans_handle *trans, BUG_ON(ret); } - ret = block_iterate_proc(NULL, new_pos / sectorsize, - cur_offset / sectorsize, &data); - if (ret & BLOCK_ABORT) { - ret = data.errcode; + ret = add_file_disk_extent(&data, cur_offset, new_pos, + sectorsize); + if (ret) goto fail; - } cur_offset += sectorsize; bytenr += sectorsize; num_bytes -= sectorsize; } - if (data.num_blocks > 0) { - ret = record_file_blocks(trans, root, - extent_key->objectid, &inode, - data.first_block, data.disk_block, - data.num_blocks, datacsum); - if (ret) - goto fail; - } + ret = finish_file_extents(&data); + if (ret) + goto fail; key.objectid = extent_key->objectid; key.offset = 0; -- 1.6.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Sean Bartell
2010-Mar-22 04:55 UTC
Re: [PATCH 2/4] btrfs-convert: Add extent iteration functions.
Whoops, there''s a major memory leak. Please apply this patch to the patch :). diff --git a/convert.c b/convert.c index dfd2976..7bb4ed0 100644 --- a/convert.c +++ b/convert.c @@ -471,21 +471,24 @@ int finish_file_extents(struct extent_iterate_data *priv) return ret; } *priv->inode_nbytes += priv->size; - return btrfs_insert_inline_extent(priv->trans, priv->root, - priv->objectid, - priv->file_off, priv->data, - priv->size); - } - - ret = commit_file_extents(priv); - if (ret) - return ret; - - if (priv->total_size > priv->last_file_off) { - ret = commit_disk_extent(priv, priv->last_file_off, 0, - priv->total_size - priv->last_file_off); + ret = btrfs_insert_inline_extent(priv->trans, priv->root, + priv->objectid, + priv->file_off, priv->data, + priv->size); if (ret) return ret; + } else { + ret = commit_file_extents(priv); + if (ret) + return ret; + + if (priv->total_size > priv->last_file_off) { + ret = commit_disk_extent(priv, priv->last_file_off, 0, + priv->total_size - + priv->last_file_off); + if (ret) + return ret; + } } free(priv->data); return 0; -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Yan, Zheng
2010-May-18 13:06 UTC
Re: [PATCH 2/4] btrfs-convert: Add extent iteration functions.
On Sat, Mar 20, 2010 at 12:26 PM, Sean Bartell <wingedtachikoma@gmail.com> wrote:> A filesystem can have disk extents in arbitrary places on the disk, as > well as extents that must be read into memory because they have > compression or encryption btrfs doesn''t support. These extents can be > passed to the new extent iteration functions, which will handle all the > details of alignment, allocation, etc. > --- > convert.c | 604 ++++++++++++++++++++++++++++++++++++++++--------------------- > 1 files changed, 401 insertions(+), 203 deletions(-) > > diff --git a/convert.c b/convert.c > index c48f8ba..bd91990 100644 > --- a/convert.c > +++ b/convert.c > @@ -357,7 +357,7 @@ error: > } > > static int read_disk_extent(struct btrfs_root *root, u64 bytenr, > - u32 num_bytes, char *buffer) > + u64 num_bytes, char *buffer) > { > int ret; > struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; > @@ -371,6 +371,23 @@ fail: > ret = -1; > return ret; > } > + > +static int write_disk_extent(struct btrfs_root *root, u64 bytenr, > + u64 num_bytes, const char *buffer) > +{ > + int ret; > + struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; > + > + ret = pwrite(fs_devs->latest_bdev, buffer, num_bytes, bytenr); > + if (ret != num_bytes) > + goto fail; > + ret = 0; > +fail: > + if (ret > 0) > + ret = -1; > + return ret; > +} > + > /* > * Record a file extent. Do all the required works, such as inserting > * file extent item, inserting extent item and backref item into extent > @@ -378,8 +395,7 @@ fail: > */ > static int record_file_extent(struct btrfs_trans_handle *trans, > struct btrfs_root *root, u64 objectid, > - struct btrfs_inode_item *inode, > - u64 file_pos, u64 disk_bytenr, > + u64 *inode_nbytes, u64 file_pos, u64 disk_bytenr, > u64 num_bytes, int checksum) > { > int ret; > @@ -391,7 +407,6 @@ static int record_file_extent(struct btrfs_trans_handle *trans, > struct btrfs_path path; > struct btrfs_extent_item *ei; > u32 blocksize = root->sectorsize; > - u64 nbytes; > > if (disk_bytenr == 0) { > ret = btrfs_insert_file_extent(trans, root, objectid, > @@ -450,8 +465,7 @@ static int record_file_extent(struct btrfs_trans_handle *trans, > btrfs_set_file_extent_other_encoding(leaf, fi, 0); > btrfs_mark_buffer_dirty(leaf); > > - nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes; > - btrfs_set_stack_inode_nbytes(inode, nbytes); > + *inode_nbytes += num_bytes; > > btrfs_release_path(root, &path); > > @@ -492,95 +506,355 @@ fail: > return ret; > } > > -static int record_file_blocks(struct btrfs_trans_handle *trans, > - struct btrfs_root *root, u64 objectid, > - struct btrfs_inode_item *inode, > - u64 file_block, u64 disk_block, > - u64 num_blocks, int checksum) > -{ > - u64 file_pos = file_block * root->sectorsize; > - u64 disk_bytenr = disk_block * root->sectorsize; > - u64 num_bytes = num_blocks * root->sectorsize; > - return record_file_extent(trans, root, objectid, inode, file_pos, > - disk_bytenr, num_bytes, checksum); > -} > - > -struct blk_iterate_data { > +struct extent_iterate_data { > struct btrfs_trans_handle *trans; > struct btrfs_root *root; > - struct btrfs_inode_item *inode; > + u64 *inode_nbytes; > u64 objectid; > - u64 first_block; > - u64 disk_block; > - u64 num_blocks; > - u64 boundary; > - int checksum; > - int errcode; > + int checksum, packing; > + u64 last_file_off; > + u64 total_size; > + enum {EXTENT_ITERATE_TYPE_NONE, EXTENT_ITERATE_TYPE_MEM, > + EXTENT_ITERATE_TYPE_DISK} type; > + u64 size; > + u64 file_off; /* always aligned to sectorsize */ > + char *data; /* for mem */ > + u64 disk_off; /* for disk */ > }; > > -static int block_iterate_proc(ext2_filsys ext2_fs, > - u64 disk_block, u64 file_block, > - struct blk_iterate_data *idata) > +static u64 extent_boundary(struct btrfs_root *root, u64 extent_start) > { > - int ret; > - int sb_region; > - int do_barrier; > - struct btrfs_root *root = idata->root; > - struct btrfs_trans_handle *trans = idata->trans; > - struct btrfs_block_group_cache *cache; > - u64 bytenr = disk_block * root->sectorsize; > - > - sb_region = intersect_with_sb(bytenr, root->sectorsize); > - do_barrier = sb_region || disk_block >= idata->boundary; > - if ((idata->num_blocks > 0 && do_barrier) || > - (file_block > idata->first_block + idata->num_blocks) || > - (disk_block != idata->disk_block + idata->num_blocks)) { > - if (idata->num_blocks > 0) { > - ret = record_file_blocks(trans, root, idata->objectid, > - idata->inode, idata->first_block, > - idata->disk_block, idata->num_blocks, > - idata->checksum); > - if (ret) > - goto fail; > - idata->first_block += idata->num_blocks; > - idata->num_blocks = 0; > + int i; > + u64 offset; > + u64 boundary = (u64)-1; > + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { > + offset = btrfs_sb_offset(i); > + offset &= ~((u64)STRIPE_LEN - 1); > + if (offset > extent_start) { > + boundary = offset; > + break; > + } > + if (offset + STRIPE_LEN > extent_start) { > + boundary = offset + STRIPE_LEN; > + break; > } > - if (file_block > idata->first_block) { > - ret = record_file_blocks(trans, root, idata->objectid, > - idata->inode, idata->first_block, > - 0, file_block - idata->first_block, > - idata->checksum); > + } > + > + struct btrfs_block_group_cache *cache; > + cache = btrfs_lookup_block_group(root->fs_info, extent_start); > + BUG_ON(!cache); > + offset = cache->key.objectid + cache->key.offset; > + return min_t(u64, boundary, offset); > +} > + > +static int commit_disk_extent(struct extent_iterate_data *priv, > + u64 file_pos, u64 disk_bytenr, u64 num_bytes) > +{ > + u64 boundary; > + int ret; > + if (disk_bytenr == 0) > + return record_file_extent(priv->trans, priv->root, > + priv->objectid, priv->inode_nbytes, > + file_pos, disk_bytenr, num_bytes, > + priv->checksum); > + /* Break up the disk extent on blockgroup and superblock boundaries. */ > + while (num_bytes) { > + boundary = extent_boundary(priv->root, disk_bytenr); > + u64 size = min_t(u64, boundary - disk_bytenr, num_bytes); > + ret = record_file_extent(priv->trans, priv->root, > + priv->objectid, priv->inode_nbytes, > + file_pos, disk_bytenr, size, > + priv->checksum); > + if (ret) > + return ret; > + file_pos += size; > + disk_bytenr += size; > + num_bytes -= size; > + } > + return 0; > +} > + > +static int commit_file_extents(struct extent_iterate_data *priv) > +{ > + int ret; > + if (priv->type == EXTENT_ITERATE_TYPE_NONE) > + return 0; > + if (priv->size == 0) > + return 0; > + if (priv->file_off > priv->last_file_off) { > + ret = commit_disk_extent(priv, priv->last_file_off, 0, > + priv->file_off - priv->last_file_off); > + if (ret) > + return ret; > + } > + priv->last_file_off = priv->file_off + priv->size; > + > + if (priv->type == EXTENT_ITERATE_TYPE_MEM) { > + /* allocate and write to disk */ > + struct btrfs_key key; > + ret = custom_alloc_extent(priv->root, priv->root->sectorsize, > + 0, &key); > + if (ret) > + return ret; > + ret = write_disk_extent(priv->root, key.objectid, priv->size, > + priv->data); > + if (ret) > + return ret; > + priv->type = EXTENT_ITERATE_TYPE_DISK; > + priv->disk_off = key.objectid; > + } > + > + u64 sectorsize = priv->root->sectorsize; > + if (priv->size & (sectorsize - 1)) > + priv->size = (priv->size & ~(sectorsize - 1)) + sectorsize; > + ret = commit_disk_extent(priv, priv->file_off, priv->disk_off, > + priv->size); > + if (ret) > + return ret; > + priv->type = EXTENT_ITERATE_TYPE_NONE; > + return 0; > +} > + > +int start_file_extents(struct extent_iterate_data *priv, > + struct btrfs_trans_handle *trans, > + struct btrfs_root *root, u64 *inode_nbytes, > + u64 objectid, int checksum, int packing, u64 total_size) > +{ > + priv->trans = trans; > + priv->root = root; > + priv->inode_nbytes = inode_nbytes; > + priv->objectid = objectid; > + priv->checksum = checksum; > + priv->packing = packing; > + priv->last_file_off = 0; > + priv->type = 0; > + priv->total_size = total_size; > + priv->data = malloc(root->sectorsize); > + if (!priv->data) > + return -ENOMEM; > + return 0; > +} > + > +int start_file_extents_range(struct extent_iterate_data *priv, > + struct btrfs_trans_handle *trans, > + struct btrfs_root *root, u64 *inode_nbytes, > + u64 objectid, int checksum, u64 start, u64 end) > +{ > + priv->trans = trans; > + priv->root = root; > + priv->inode_nbytes = inode_nbytes; > + priv->objectid = objectid; > + priv->checksum = checksum; > + priv->packing = 0; > + priv->last_file_off = start; > + priv->type = 0; > + priv->total_size = end; > + priv->data = malloc(root->sectorsize); > + if (!priv->data) > + return -ENOMEM; > + return 0; > +} > + > +int finish_file_extents(struct extent_iterate_data *priv) > +{ > + int ret; > + > + if (priv->packing > + && priv->type != EXTENT_ITERATE_TYPE_NONE > + && priv->total_size <= BTRFS_MAX_INLINE_DATA_SIZE(priv->root)) { > + priv->size = min_t(u64, priv->size, > + priv->total_size - priv->file_off); > + /* make inline extent */ > + if (priv->type == EXTENT_ITERATE_TYPE_DISK) { > + ret = read_disk_extent(priv->root, priv->disk_off, > + priv->size, priv->data); > if (ret) > - goto fail; > + return ret; > } > + *priv->inode_nbytes += priv->size; > + return btrfs_insert_inline_extent(priv->trans, priv->root, > + priv->objectid, > + priv->file_off, priv->data, > + priv->size); > + } > > - if (sb_region) { > - bytenr += STRIPE_LEN - 1; > - bytenr &= ~((u64)STRIPE_LEN - 1); > - } else { > - cache = btrfs_lookup_block_group(root->fs_info, bytenr); > - BUG_ON(!cache); > - bytenr = cache->key.objectid + cache->key.offset; > + ret = commit_file_extents(priv); > + if (ret) > + return ret; > + > + if (priv->total_size > priv->last_file_off) { > + ret = commit_disk_extent(priv, priv->last_file_off, 0, > + priv->total_size - priv->last_file_off); > + if (ret) > + return ret; > + } > + free(priv->data); > + return 0; > +} > + > +int add_file_mem_extent(struct extent_iterate_data *priv, u64 file_off, > + u64 size, char *data); > + > +int add_file_disk_extent(struct extent_iterate_data *priv, u64 file_off, > + u64 disk_off, u64 size) > +{ > + BUG_ON(file_off < priv->last_file_off); > + int ret; > + u64 sectorsize = priv->root->sectorsize; > + u64 mask = sectorsize - 1; > + if (size == 0) > + return 0; > + if ((file_off & mask) != (disk_off & mask)) { > + /* It''s unclear how to CoW this, so don''t. */ > + char *data = malloc(size); > + if (!data) > + return -ENOMEM; > + ret = read_disk_extent(priv->root, disk_off, size, data); > + if (ret) { > + free(data); > + return ret; > } > + ret = add_file_mem_extent(priv, file_off, size, data); > + free(data); > + return ret; > + } > + if (priv->type == EXTENT_ITERATE_TYPE_DISK > + && priv->file_off + priv->size == file_off > + && priv->disk_off + priv->size == disk_off) { > + /* It''s a continuation of the same disk extent. */ > + priv->size += size; > + return 0; > + } > + if (disk_off == 0 || disk_off & mask) {why "disk_off == 0" is needed here?> + /* We need to have an aligned start, so give the first part to > + * add_file_mem_extent if necessary. */ > + u64 mem_size = min_t(u64, sectorsize - (disk_off & mask), size); > + char *data = malloc(mem_size); > + if (!data) > + return -ENOMEM; > + ret = read_disk_extent(priv->root, disk_off, mem_size, data); > + if (ret) { > + free(data); > + return ret; > + } > + ret = add_file_mem_extent(priv, file_off, mem_size, data); > + free(data); > + if (ret) > + return ret; > + file_off += mem_size; > + disk_off += mem_size; > + size -= mem_size; > + if (size == 0) > + return 0; > + } > + ret = commit_file_extents(priv); > + if (ret) > + return ret; > + priv->type = EXTENT_ITERATE_TYPE_DISK; > + priv->size = size; > + priv->file_off = file_off; > + priv->disk_off = disk_off; > + return 0; > +} > + > +int add_file_mem_extent(struct extent_iterate_data *priv, u64 file_off, > + u64 size, char *data) > +{ > + BUG_ON(file_off < priv->last_file_off); > + int ret; > + u64 sectorsize = priv->root->sectorsize; > + u64 mask = sectorsize - 1; > + u64 aligned_file_off = file_off & ~mask; > + u32 alignment = file_off - aligned_file_off; > + size += alignment; > + > + /* If we share a sector with a DISK extent, commit most of it and turn > + * the shared part into a MEM extent. */ > + if (priv->type == EXTENT_ITERATE_TYPE_DISK > + && priv->file_off + priv->size > aligned_file_off) { > + u64 mem_size = priv->file_off + priv->size - aligned_file_off; > + ret = read_disk_extent(priv->root, aligned_file_off, mem_size, > + priv->data); > + if (ret) > + return ret; > + priv->size -= mem_size; > + ret = commit_file_extents(priv); > + if (ret) > + return ret; > + priv->type = EXTENT_ITERATE_TYPE_MEM; > + priv->size = mem_size; > + priv->file_off = aligned_file_off; > + } > + > + /* Put our first sector in priv->data. If we share a sector with the > + * previous extent, combine with it. */ > + if (priv->type == EXTENT_ITERATE_TYPE_MEM > + && priv->file_off + priv->size > aligned_file_off) { > + BUG_ON(priv->file_off != aligned_file_off); > + memset(priv->data + priv->size, 0, sectorsize - priv->size); > + } else { > + ret = commit_file_extents(priv); > + if (ret) > + return ret; > + memset(priv->data, 0, sectorsize); > + } > + if (size < sectorsize) { > + memcpy(priv->data + alignment, data, size - alignment); > + priv->type = EXTENT_ITERATE_TYPE_MEM; > + priv->file_off = aligned_file_off; > + priv->size = size; > + return 0; > + } > + memcpy(priv->data + alignment, data, sectorsize - alignment); > + data += sectorsize - alignment; > + > + /* We have full sectors; allocate and write them. */ > + u64 aligned_size = size & ~mask; > + struct btrfs_key key; > + ret = custom_alloc_extent(priv->root, aligned_size, 0, &key); > + if (ret) > + return ret; > + ret = write_disk_extent(priv->root, key.objectid, > + sectorsize, priv->data); > + if (ret) > + return ret; > + ret = write_disk_extent(priv->root, key.objectid + sectorsize, > + aligned_size - sectorsize, data); > + if (ret) > + return ret; > + ret = add_file_disk_extent(priv, aligned_file_off, key.objectid, > + aligned_size); > + if (ret) > + return ret; > > - idata->first_block = file_block; > - idata->disk_block = disk_block; > - idata->boundary = bytenr / root->sectorsize; > + /* Leave the rest in priv. */ > + size -= aligned_size; > + if (size) { > + ret = commit_file_extents(priv); > + if (ret) > + return ret; > + aligned_file_off += aligned_size; > + data += aligned_size - sectorsize; > + priv->type = EXTENT_ITERATE_TYPE_MEM; > + priv->file_off = aligned_file_off; > + priv->size = size; > + memcpy(priv->data, data, size); > } > - idata->num_blocks++; > return 0; > -fail: > - idata->errcode = ret; > - return BLOCK_ABORT; > } > > static int __block_iterate_proc(ext2_filsys fs, blk_t *blocknr, > e2_blkcnt_t blockcnt, blk_t ref_block, > int ref_offset, void *priv_data) > { > - struct blk_iterate_data *idata; > - idata = (struct blk_iterate_data *)priv_data; > - return block_iterate_proc(fs, *blocknr, blockcnt, idata); > + struct extent_iterate_data *idata; > + idata = (struct extent_iterate_data *)priv_data; > + u64 blocksize = fs->blocksize; > + int ret = add_file_disk_extent(idata, blocksize * blockcnt, > + blocksize * *blocknr, blocksize); > + if (ret) > + return BLOCK_ABORT; > + return 0; > } > > /* > @@ -593,68 +867,23 @@ static int create_file_extents(struct btrfs_trans_handle *trans, > int datacsum, int packing) > { > int ret; > - char *buffer = NULL; > errcode_t err; > - u32 last_block; > - u32 sectorsize = root->sectorsize; > + u64 inode_nbytes = 0; > u64 inode_size = btrfs_stack_inode_size(btrfs_inode); > - struct blk_iterate_data data = { > - .trans = trans, > - .root = root, > - .inode = btrfs_inode, > - .objectid = objectid, > - .first_block = 0, > - .disk_block = 0, > - .num_blocks = 0, > - .boundary = (u64)-1, > - .checksum = datacsum, > - .errcode = 0, > - }; > + struct extent_iterate_data data; > + ret = start_file_extents(&data, trans, root, &inode_nbytes, objectid, > + datacsum, packing, inode_size); > + if (ret) > + return ret; > err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY, > NULL, __block_iterate_proc, &data); > if (err) > goto error; > - ret = data.errcode; > + ret = finish_file_extents(&data); > if (ret) > - goto fail; > - if (packing && data.first_block == 0 && data.num_blocks > 0 && > - inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) { > - u64 num_bytes = data.num_blocks * sectorsize; > - u64 disk_bytenr = data.disk_block * sectorsize; > - u64 nbytes; > - > - buffer = malloc(num_bytes); > - if (!buffer) > - return -ENOMEM; > - ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer); > - if (ret) > - goto fail; > - if (num_bytes > inode_size) > - num_bytes = inode_size; > - ret = btrfs_insert_inline_extent(trans, root, objectid, > - 0, buffer, num_bytes); > - if (ret) > - goto fail; > - nbytes = btrfs_stack_inode_nbytes(btrfs_inode) + num_bytes; > - btrfs_set_stack_inode_nbytes(btrfs_inode, nbytes); > - } else if (data.num_blocks > 0) { > - ret = record_file_blocks(trans, root, objectid, btrfs_inode, > - data.first_block, data.disk_block, > - data.num_blocks, data.checksum); > - if (ret) > - goto fail; > - } > - data.first_block += data.num_blocks; > - last_block = (inode_size + sectorsize - 1) / sectorsize; > - if (last_block > data.first_block) { > - ret = record_file_blocks(trans, root, objectid, btrfs_inode, > - data.first_block, 0, last_block - > - data.first_block, data.checksum); > - } > -fail: > - if (buffer) > - free(buffer); > - return ret; > + return ret; > + btrfs_set_stack_inode_nbytes(btrfs_inode, inode_nbytes); > + return 0; > error: > fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err)); > return -1; > @@ -1206,52 +1435,33 @@ static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs, > */ > static int create_image_file_range(struct btrfs_trans_handle *trans, > struct btrfs_root *root, u64 objectid, > - struct btrfs_inode_item *inode, > + u64 *inode_nbytes, > u64 start_byte, u64 end_byte, > struct extent_io_tree *orig_free_tree) > { > - u32 blocksize = root->sectorsize; > - u32 block = start_byte / blocksize; > - u32 last_block = (end_byte + blocksize - 1) / blocksize; > int ret = 0; > - struct blk_iterate_data data = { > - .trans = trans, > - .root = root, > - .inode = inode, > - .objectid = objectid, > - .first_block = block, > - .disk_block = 0, > - .num_blocks = 0, > - .boundary = (u64)-1, > - .checksum = 0, > - .errcode = 0, > - }; > - for (; start_byte < end_byte; block++, start_byte += blocksize) { > - if (test_range_bit(orig_free_tree, start_byte, > - start_byte + blocksize, EXTENT_DIRTY, 1)) > - continue; > - ret = block_iterate_proc(NULL, block, block, &data); > - if (ret & BLOCK_ABORT) { > - ret = data.errcode; > - goto fail; > - } > - } > - if (data.num_blocks > 0) { > - ret = record_file_blocks(trans, root, objectid, inode, > - data.first_block, data.disk_block, > - data.num_blocks, 0); > - if (ret) > - goto fail; > - data.first_block += data.num_blocks; > - } > - if (last_block > data.first_block) { > - ret = record_file_blocks(trans, root, objectid, inode, > - data.first_block, 0, last_block - > - data.first_block, 0); > + struct extent_iterate_data data; > + ret = start_file_extents_range(&data, trans, root, inode_nbytes, > + objectid, 0, start_byte, end_byte); > + if (ret) > + return ret; > + while (start_byte < end_byte) { > + u64 start, end; > + ret = find_first_extent_bit(orig_free_tree, start_byte, > + &start, &end, EXTENT_DIRTY); > if (ret) > - goto fail; > + start = end_byte; > + if (start > start_byte) { > + u64 size = min_t(u64, start - start_byte, > + end_byte - start_byte); > + ret = add_file_disk_extent(&data, start_byte, > + start_byte, size); > + if (ret) > + return ret; > + } > + start_byte = end + 1; > } > -fail: > + ret = finish_file_extents(&data); > return ret; > } > /* > @@ -1279,6 +1489,7 @@ static int create_ext2_image(struct btrfs_root *root, const char *name, > u64 last_byte; > u64 first_free; > u64 total_bytes; > + u64 inode_nbytes; > u32 sectorsize = root->sectorsize; > > total_bytes = btrfs_super_total_bytes(&fs_info->super_copy); > @@ -1289,7 +1500,7 @@ static int create_ext2_image(struct btrfs_root *root, const char *name, > btrfs_set_stack_inode_generation(&btrfs_inode, 1); > btrfs_set_stack_inode_size(&btrfs_inode, total_bytes); > btrfs_set_stack_inode_nlink(&btrfs_inode, 1); > - btrfs_set_stack_inode_nbytes(&btrfs_inode, 0); > + inode_nbytes = 0; > btrfs_set_stack_inode_mode(&btrfs_inode, S_IFREG | 0400); > btrfs_set_stack_inode_flags(&btrfs_inode, BTRFS_INODE_NODATASUM | > BTRFS_INODE_READONLY); > @@ -1315,7 +1526,7 @@ static int create_ext2_image(struct btrfs_root *root, const char *name, > if (ret) > goto fail; > ret = record_file_extent(trans, root, objectid, > - &btrfs_inode, last_byte, > + &inode_nbytes, last_byte, > key.objectid, sectorsize, 0); > if (ret) > goto fail; > @@ -1370,12 +1581,12 @@ next: > > if (bytenr > last_byte) { > ret = create_image_file_range(trans, root, objectid, > - &btrfs_inode, last_byte, > + &inode_nbytes, last_byte, > bytenr, orig_free_tree); > if (ret) > goto fail; > } > - ret = record_file_extent(trans, root, objectid, &btrfs_inode, > + ret = record_file_extent(trans, root, objectid, &inode_nbytes, > bytenr, bytenr, num_bytes, 0); > if (ret) > goto fail; > @@ -1392,12 +1603,14 @@ next: > btrfs_release_path(root, &path); > if (total_bytes > last_byte) { > ret = create_image_file_range(trans, root, objectid, > - &btrfs_inode, last_byte, > + &inode_nbytes, last_byte, > total_bytes, orig_free_tree); > if (ret) > goto fail; > } > > + btrfs_set_stack_inode_nbytes(&btrfs_inode, inode_nbytes); > + > ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); > if (ret) > goto fail; > @@ -1934,7 +2147,7 @@ static int relocate_one_reference(struct btrfs_trans_handle *trans, > struct btrfs_key key; > struct btrfs_path path; > struct btrfs_inode_item inode; > - struct blk_iterate_data data; > + struct extent_iterate_data data; > u64 bytenr; > u64 num_bytes; > u64 cur_offset; > @@ -1990,22 +2203,14 @@ static int relocate_one_reference(struct btrfs_trans_handle *trans, > btrfs_release_path(root, &path); > > BUG_ON(num_bytes & (sectorsize - 1)); > - nbytes = btrfs_stack_inode_nbytes(&inode) - num_bytes; > - btrfs_set_stack_inode_nbytes(&inode, nbytes); > datacsum = !(btrfs_stack_inode_flags(&inode) & BTRFS_INODE_NODATASUM); > > - data = (struct blk_iterate_data) { > - .trans = trans, > - .root = root, > - .inode = &inode, > - .objectid = extent_key->objectid, > - .first_block = extent_key->offset / sectorsize, > - .disk_block = 0, > - .num_blocks = 0, > - .boundary = (u64)-1, > - .checksum = datacsum, > - .errcode = 0, > - }; > + ret = start_file_extents_range(&data, trans, root, &nbytes, > + extent_key->objectid, datacsum, > + extent_key->offset, > + extent_key->offset + num_bytes); > + if (ret) > + goto fail; > > cur_offset = extent_key->offset; > while (num_bytes > 0) { > @@ -2035,26 +2240,19 @@ static int relocate_one_reference(struct btrfs_trans_handle *trans, > BUG_ON(ret); > } > > - ret = block_iterate_proc(NULL, new_pos / sectorsize, > - cur_offset / sectorsize, &data); > - if (ret & BLOCK_ABORT) { > - ret = data.errcode; > + ret = add_file_disk_extent(&data, cur_offset, new_pos, > + sectorsize); > + if (ret) > goto fail; > - } > > cur_offset += sectorsize; > bytenr += sectorsize; > num_bytes -= sectorsize; > } > > - if (data.num_blocks > 0) { > - ret = record_file_blocks(trans, root, > - extent_key->objectid, &inode, > - data.first_block, data.disk_block, > - data.num_blocks, datacsum); > - if (ret) > - goto fail; > - } > + ret = finish_file_extents(&data); > + if (ret) > + goto fail; > > key.objectid = extent_key->objectid; > key.offset = 0; > -- > 1.6.4.4 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >-- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Sean Bartell
2010-May-18 15:37 UTC
Re: [PATCH 2/4] btrfs-convert: Add extent iteration functions.
On Tue, May 18, 2010 at 09:06:54PM +0800, Yan, Zheng wrote:> On Sat, Mar 20, 2010 at 12:26 PM, Sean Bartell > <wingedtachikoma@gmail.com> wrote: > > +int add_file_disk_extent(struct extent_iterate_data *priv, u64 file_off, > > + u64 disk_off, u64 size) > > +{ > > + BUG_ON(file_off < priv->last_file_off); > > + int ret; > > + u64 sectorsize = priv->root->sectorsize; > > + u64 mask = sectorsize - 1; > > + if (size == 0) > > + return 0; > > + if ((file_off & mask) != (disk_off & mask)) { > > + /* It''s unclear how to CoW this, so don''t. */ > > + char *data = malloc(size); > > + if (!data) > > + return -ENOMEM; > > + ret = read_disk_extent(priv->root, disk_off, size, data); > > + if (ret) { > > + free(data); > > + return ret; > > } > > + ret = add_file_mem_extent(priv, file_off, size, data); > > + free(data); > > + return ret; > > + } > > + if (priv->type == EXTENT_ITERATE_TYPE_DISK > > + && priv->file_off + priv->size == file_off > > + && priv->disk_off + priv->size == disk_off) { > > + /* It''s a continuation of the same disk extent. */ > > + priv->size += size; > > + return 0; > > + } > > + if (disk_off == 0 || disk_off & mask) { > > why "disk_off == 0" is needed here?To btrfs, a disk offset of 0 represents a sparse extent. If the original filesystem passes in an extent at 0, this chops off the first block to prevent it from being mistakenly interpreted as a sparse extent.> > + /* We need to have an aligned start, so give the first part to > > + * add_file_mem_extent if necessary. */ > > + u64 mem_size = min_t(u64, sectorsize - (disk_off & mask), size); > > + char *data = malloc(mem_size); > > + if (!data) > > + return -ENOMEM; > > + ret = read_disk_extent(priv->root, disk_off, mem_size, data); > > + if (ret) { > > + free(data); > > + return ret; > > + } > > + ret = add_file_mem_extent(priv, file_off, mem_size, data); > > + free(data); > > + if (ret) > > + return ret; > > + file_off += mem_size; > > + disk_off += mem_size; > > + size -= mem_size; > > + if (size == 0) > > + return 0; > > + } > > + ret = commit_file_extents(priv); > > + if (ret) > > + return ret; > > + priv->type = EXTENT_ITERATE_TYPE_DISK; > > + priv->size = size; > > + priv->file_off = file_off; > > + priv->disk_off = disk_off; > > + return 0; > > +}-- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html