Commit 5ab56090 authored by Liu Bo's avatar Liu Bo Committed by David Sterba

Btrfs: introduce a function to get extra mirror from replace

As the part of getting extra mirror in __btrfs_map_block is
independent, this puts it into a separate function.
Reviewed-by: default avatarQu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: default avatarLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 0b3d4cd3
......@@ -139,6 +139,11 @@ static int btrfs_relocate_sys_chunks(struct btrfs_fs_info *fs_info);
static void __btrfs_reset_dev_stats(struct btrfs_device *dev);
static void btrfs_dev_stat_print_on_error(struct btrfs_device *dev);
static void btrfs_dev_stat_print_on_load(struct btrfs_device *device);
static int __btrfs_map_block(struct btrfs_fs_info *fs_info,
enum btrfs_map_op op,
u64 logical, u64 *length,
struct btrfs_bio **bbio_ret,
int mirror_num, int need_raid_map);
DEFINE_MUTEX(uuid_mutex);
static LIST_HEAD(fs_uuids);
......@@ -5450,6 +5455,83 @@ static int __btrfs_map_block_for_discard(struct btrfs_fs_info *fs_info,
return ret;
}
/*
* In dev-replace case, for repair case (that's the only case where the mirror
* is selected explicitly when calling btrfs_map_block), blocks left of the
* left cursor can also be read from the target drive.
*
* For REQ_GET_READ_MIRRORS, the target drive is added as the last one to the
* array of stripes.
* For READ, it also needs to be supported using the same mirror number.
*
* If the requested block is not left of the left cursor, EIO is returned. This
* can happen because btrfs_num_copies() returns one more in the dev-replace
* case.
*/
static int get_extra_mirror_from_replace(struct btrfs_fs_info *fs_info,
u64 logical, u64 length,
u64 srcdev_devid, int *mirror_num,
u64 *physical)
{
struct btrfs_bio *bbio = NULL;
int num_stripes;
int index_srcdev = 0;
int found = 0;
u64 physical_of_found = 0;
int i;
int ret = 0;
ret = __btrfs_map_block(fs_info, BTRFS_MAP_GET_READ_MIRRORS,
logical, &length, &bbio, 0, 0);
if (ret) {
ASSERT(bbio == NULL);
return ret;
}
num_stripes = bbio->num_stripes;
if (*mirror_num > num_stripes) {
/*
* BTRFS_MAP_GET_READ_MIRRORS does not contain this mirror,
* that means that the requested area is not left of the left
* cursor
*/
btrfs_put_bbio(bbio);
return -EIO;
}
/*
* process the rest of the function using the mirror_num of the source
* drive. Therefore look it up first. At the end, patch the device
* pointer to the one of the target drive.
*/
for (i = 0; i < num_stripes; i++) {
if (bbio->stripes[i].dev->devid != srcdev_devid)
continue;
/*
* In case of DUP, in order to keep it simple, only add the
* mirror with the lowest physical address
*/
if (found &&
physical_of_found <= bbio->stripes[i].physical)
continue;
index_srcdev = i;
found = 1;
physical_of_found = bbio->stripes[i].physical;
}
btrfs_put_bbio(bbio);
ASSERT(found);
if (!found)
return -EIO;
*mirror_num = index_srcdev + 1;
*physical = physical_of_found;
return ret;
}
static int __btrfs_map_block(struct btrfs_fs_info *fs_info,
enum btrfs_map_op op,
u64 logical, u64 *length,
......@@ -5554,79 +5636,14 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info,
if (dev_replace_is_ongoing && mirror_num == map->num_stripes + 1 &&
op != BTRFS_MAP_WRITE && op != BTRFS_MAP_GET_READ_MIRRORS &&
dev_replace->tgtdev != NULL) {
/*
* in dev-replace case, for repair case (that's the only
* case where the mirror is selected explicitly when
* calling btrfs_map_block), blocks left of the left cursor
* can also be read from the target drive.
* For REQ_GET_READ_MIRRORS, the target drive is added as
* the last one to the array of stripes. For READ, it also
* needs to be supported using the same mirror number.
* If the requested block is not left of the left cursor,
* EIO is returned. This can happen because btrfs_num_copies()
* returns one more in the dev-replace case.
*/
u64 tmp_length = *length;
struct btrfs_bio *tmp_bbio = NULL;
int tmp_num_stripes;
u64 srcdev_devid = dev_replace->srcdev->devid;
int index_srcdev = 0;
int found = 0;
u64 physical_of_found = 0;
ret = __btrfs_map_block(fs_info, BTRFS_MAP_GET_READ_MIRRORS,
logical, &tmp_length, &tmp_bbio, 0, 0);
if (ret) {
WARN_ON(tmp_bbio != NULL);
goto out;
}
tmp_num_stripes = tmp_bbio->num_stripes;
if (mirror_num > tmp_num_stripes) {
/*
* BTRFS_MAP_GET_READ_MIRRORS does not contain this
* mirror, that means that the requested area
* is not left of the left cursor
*/
ret = -EIO;
btrfs_put_bbio(tmp_bbio);
goto out;
}
/*
* process the rest of the function using the mirror_num
* of the source drive. Therefore look it up first.
* At the end, patch the device pointer to the one of the
* target drive.
*/
for (i = 0; i < tmp_num_stripes; i++) {
if (tmp_bbio->stripes[i].dev->devid != srcdev_devid)
continue;
/*
* In case of DUP, in order to keep it simple, only add
* the mirror with the lowest physical address
*/
if (found &&
physical_of_found <= tmp_bbio->stripes[i].physical)
continue;
index_srcdev = i;
found = 1;
physical_of_found = tmp_bbio->stripes[i].physical;
}
btrfs_put_bbio(tmp_bbio);
if (!found) {
WARN_ON(1);
ret = -EIO;
ret = get_extra_mirror_from_replace(fs_info, logical, *length,
dev_replace->srcdev->devid,
&mirror_num,
&physical_to_patch_in_first_stripe);
if (ret)
goto out;
}
mirror_num = index_srcdev + 1;
else
patch_the_first_stripe_for_dev_replace = 1;
physical_to_patch_in_first_stripe = physical_of_found;
} else if (mirror_num > map->num_stripes) {
mirror_num = 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment