diff options
author | Miao Xie <miaox@cn.fujitsu.com> | 2013-07-03 21:25:19 +0800 |
---|---|---|
committer | Chris Mason <chris.mason@fusionio.com> | 2013-07-03 14:06:55 -0400 |
commit | 3b9e6dd4379ed8f2fb50bee8dce4245038498211 (patch) | |
tree | 62d67b301d3e8981a74703f4c0fd7591b179aaf4 /cmds-chunk.c | |
parent | 68acb1075e0da2d9f170cb52f561c5225787dbdf (diff) |
Btrfs-progs: Add chunk rebuild function for RAID1/SINGLE/DUP
Add chunk rebuild for RAID1/SINGLE/DUP to chunk-recover command.
Before this patch chunk-recover can only scan and reuse the old chunk
data to recover. With this patch, chunk-recover can use the reference
between chunk/block group/dev extent to rebuild the whole chunk tree
even when old chunks are not available.
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'cmds-chunk.c')
-rw-r--r-- | cmds-chunk.c | 175 |
1 files changed, 172 insertions, 3 deletions
diff --git a/cmds-chunk.c b/cmds-chunk.c index 35577ed4..7b740a34 100644 --- a/cmds-chunk.c +++ b/cmds-chunk.c @@ -42,6 +42,7 @@ #include "commands.h" #define BTRFS_CHUNK_TREE_REBUILD_ABORTED -7500 +#define BTRFS_STRIPE_LEN (64 * 1024) struct recover_control { int verbose; @@ -1251,6 +1252,174 @@ again: goto again; } +static int btrfs_get_device_extents(u64 chunk_object, + struct list_head *orphan_devexts, + struct list_head *ret_list) +{ + struct device_extent_record *devext; + struct device_extent_record *next; + int count = 0; + + list_for_each_entry_safe(devext, next, orphan_devexts, chunk_list) { + if (devext->chunk_offset == chunk_object) { + list_move_tail(&devext->chunk_list, ret_list); + count++; + } + } + return count; +} + +static int calc_num_stripes(u64 type) +{ + if (type & (BTRFS_BLOCK_GROUP_RAID0 | + BTRFS_BLOCK_GROUP_RAID10 | + BTRFS_BLOCK_GROUP_RAID5 | + BTRFS_BLOCK_GROUP_RAID6)) + return 0; + else if (type & (BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_DUP)) + return 2; + else + return 1; +} + +static inline int calc_sub_nstripes(u64 type) +{ + if (type & BTRFS_BLOCK_GROUP_RAID10) + return 2; + else + return 1; +} + +static int btrfs_verify_device_extents(struct block_group_record *bg, + struct list_head *devexts, int ndevexts) +{ + struct device_extent_record *devext; + u64 strpie_length; + int expected_num_stripes; + + expected_num_stripes = calc_num_stripes(bg->flags); + if (!expected_num_stripes && expected_num_stripes != ndevexts) + return 1; + + strpie_length = calc_stripe_length(bg->flags, bg->offset, ndevexts); + list_for_each_entry(devext, devexts, chunk_list) { + if (devext->length != strpie_length) + return 1; + } + return 0; +} + +static int btrfs_rebuild_unordered_chunk_stripes(struct recover_control *rc, + struct chunk_record *chunk) +{ + struct device_extent_record *devext; + struct btrfs_device *device; + int i; + + devext = list_first_entry(&chunk->dextents, struct device_extent_record, + chunk_list); + for (i = 0; i < chunk->num_stripes; i++) { + chunk->stripes[i].devid = devext->objectid; + chunk->stripes[i].offset = devext->offset; + device = btrfs_find_device_by_devid(rc->fs_devices, + devext->objectid, + 0); + if (!device) + return -ENOENT; + BUG_ON(btrfs_find_device_by_devid(rc->fs_devices, + devext->objectid, + 1)); + memcpy(chunk->stripes[i].dev_uuid, device->uuid, + BTRFS_UUID_SIZE); + devext = list_next_entry(devext, chunk_list); + } + return 0; +} + +static int btrfs_rebuild_chunk_stripes(struct recover_control *rc, + struct chunk_record *chunk) +{ + int ret; + + if (chunk->type_flags & (BTRFS_BLOCK_GROUP_RAID10 | + BTRFS_BLOCK_GROUP_RAID0 | + BTRFS_BLOCK_GROUP_RAID5 | + BTRFS_BLOCK_GROUP_RAID6)) + BUG_ON(1); /* Fixme: implement in the next patch */ + else + ret = btrfs_rebuild_unordered_chunk_stripes(rc, chunk); + + return ret; +} + +static int btrfs_recover_chunks(struct recover_control *rc) +{ + struct chunk_record *chunk; + struct block_group_record *bg; + struct block_group_record *next; + LIST_HEAD(new_chunks); + LIST_HEAD(devexts); + int nstripes; + int ret; + + /* create the chunk by block group */ + list_for_each_entry_safe(bg, next, &rc->bg.block_groups, list) { + nstripes = btrfs_get_device_extents(bg->objectid, + &rc->devext.no_chunk_orphans, + &devexts); + chunk = malloc(btrfs_chunk_record_size(nstripes)); + if (!chunk) + return -ENOMEM; + memset(chunk, 0, btrfs_chunk_record_size(nstripes)); + INIT_LIST_HEAD(&chunk->dextents); + chunk->bg_rec = bg; + chunk->cache.start = bg->objectid; + chunk->cache.size = bg->offset; + chunk->objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + chunk->type = BTRFS_CHUNK_ITEM_KEY; + chunk->offset = bg->objectid; + chunk->generation = bg->generation; + chunk->length = bg->offset; + chunk->owner = BTRFS_CHUNK_TREE_OBJECTID; + chunk->stripe_len = BTRFS_STRIPE_LEN; + chunk->type_flags = bg->flags; + chunk->io_width = BTRFS_STRIPE_LEN; + chunk->io_align = BTRFS_STRIPE_LEN; + chunk->sector_size = rc->sectorsize; + chunk->sub_stripes = calc_sub_nstripes(bg->flags); + + ret = insert_cache_extent(&rc->chunk, &chunk->cache); + BUG_ON(ret); + + if (!nstripes) { + list_add_tail(&chunk->list, &rc->bad_chunks); + continue; + } + + list_splice_init(&devexts, &chunk->dextents); + + ret = btrfs_verify_device_extents(bg, &devexts, nstripes); + if (ret) { + list_add_tail(&chunk->list, &rc->bad_chunks); + continue; + } + + chunk->num_stripes = nstripes; + ret = btrfs_rebuild_chunk_stripes(rc, chunk); + if (ret) + list_add_tail(&chunk->list, &rc->bad_chunks); + else + list_add_tail(&chunk->list, &rc->good_chunks); + } + /* + * Don't worry about the lost orphan device extents, they don't + * have its chunk and block group, they must be the old ones that + * we have dropped. + */ + return 0; +} + static int btrfs_recover_chunk_tree(char *path, int verbose, int yes) { int ret = 0; @@ -1287,9 +1456,9 @@ static int btrfs_recover_chunk_tree(char *path, int verbose, int yes) if (ret) { if (!list_empty(&rc.bg.block_groups) || !list_empty(&rc.devext.no_chunk_orphans)) { - fprintf(stderr, - "There are some orphan block groups and device extents, we can't repair them now.\n"); - goto fail_rc; + ret = btrfs_recover_chunks(&rc); + if (ret) + goto fail_rc; } /* * If the chunk is healthy, its block group item and device |