btrfs-progs: Allow __btrfs_map_block_v2 to remove unrelated stripes

For READ, caller normally hopes to get what they request, other than
full stripe map.

In this case, we should remove unrelated stripe map, just like the
following case:
               32K               96K
               |<-request range->|
         0              64k           128K
RAID0:   |    Data 1    |   Data 2    |
              disk1         disk2
Before this patch, we return the full stripe:
Stripe 0: Logical 0, Physical X, Len 64K, Dev disk1
Stripe 1: Logical 64k, Physical Y, Len 64K, Dev disk2

After this patch, we limit the stripe result to the request range:
Stripe 0: Logical 32K, Physical X+32K, Len 32K, Dev disk1
Stripe 1: Logical 64k, Physical Y, Len 32K, Dev disk2

And if it's a RAID5/6 stripe, we just handle it like RAID0, ignoring
parities.

This should make caller easier to use.

Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
This commit is contained in:
Qu Wenruo 2016-12-26 14:29:27 +08:00 committed by David Sterba
parent 066745d697
commit 4ab47d1e90

103
volumes.c
View file

@ -1738,6 +1738,107 @@ static int fill_full_map_block(struct map_lookup *map, u64 start, u64 length,
return 0;
}
static void del_one_stripe(struct btrfs_map_block *map_block, int i)
{
int cur_nr = map_block->num_stripes;
int size_left = (cur_nr - 1 - i) * sizeof(struct btrfs_map_stripe);
memmove(&map_block->stripes[i], &map_block->stripes[i + 1], size_left);
map_block->num_stripes--;
}
static void remove_unrelated_stripes(struct map_lookup *map,
int rw, u64 start, u64 length,
struct btrfs_map_block *map_block)
{
int i = 0;
/*
* RAID5/6 write must use full stripe.
* No need to do anything.
*/
if (map->type & (BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6) &&
rw == WRITE)
return;
/*
* For RAID0/1/10/DUP, whatever read/write, we can remove unrelated
* stripes without causing anything wrong.
* RAID5/6 READ is just like RAID0, we don't care parity unless we need
* to recovery.
* For recovery, rw should be set to WRITE.
*/
while (i < map_block->num_stripes) {
struct btrfs_map_stripe *stripe;
u64 orig_logical; /* Original stripe logical start */
u64 orig_end; /* Original stripe logical end */
stripe = &map_block->stripes[i];
/*
* For READ, we don't really care parity
*/
if (stripe->logical == BTRFS_RAID5_P_STRIPE ||
stripe->logical == BTRFS_RAID6_Q_STRIPE) {
del_one_stripe(map_block, i);
continue;
}
/* Completely unrelated stripe */
if (stripe->logical >= start + length ||
stripe->logical + stripe->length <= start) {
del_one_stripe(map_block, i);
continue;
}
/* Covered stripe, modify its logical and physical */
orig_logical = stripe->logical;
orig_end = stripe->logical + stripe->length;
if (start + length <= orig_end) {
/*
* |<--range-->|
* | stripe |
* Or
* |<range>|
* | stripe |
*/
stripe->logical = max(orig_logical, start);
stripe->length = start + length;
stripe->physical += stripe->logical - orig_logical;
} else if (start >= orig_logical) {
/*
* |<-range--->|
* | stripe |
* Or
* |<range>|
* | stripe |
*/
stripe->logical = start;
stripe->length = min(orig_end, start + length);
stripe->physical += stripe->logical - orig_logical;
}
/*
* Remaining case:
* |<----range----->|
* | stripe |
* No need to do any modification
*/
i++;
}
/* Recaculate map_block size */
map_block->start = 0;
map_block->length = 0;
for (i = 0; i < map_block->num_stripes; i++) {
struct btrfs_map_stripe *stripe;
stripe = &map_block->stripes[i];
if (stripe->logical > map_block->start)
map_block->start = stripe->logical;
if (stripe->logical + stripe->length >
map_block->start + map_block->length)
map_block->length = stripe->logical + stripe->length -
map_block->start;
}
}
int __btrfs_map_block_v2(struct btrfs_fs_info *fs_info, int rw, u64 logical,
u64 length, struct btrfs_map_block **map_ret)
{
@ -1773,7 +1874,7 @@ int __btrfs_map_block_v2(struct btrfs_fs_info *fs_info, int rw, u64 logical,
free(map_block);
return ret;
}
/* TODO: Remove unrelated map_stripes for READ operation */
remove_unrelated_stripes(map, rw, logical, length, map_block);
*map_ret = map_block;
return 0;