Skip to content

Commit

Permalink
Btrfs, raid56: fix use-after-free problem in the final device replace…
Browse files Browse the repository at this point in the history
… procedure on raid56

The commit c404e0d (Btrfs: fix use-after-free in the finishing
procedure of the device replace) fixed a use-after-free problem
which happened when removing the source device at the end of device
replace, but at that time, btrfs didn't support device replace
on raid56, so we didn't fix the problem on the raid56 profile.
Currently, we implemented device replace for raid56, so we need
kick that problem out before we enable that function for raid56.

The fix method is very simple, we just increase the bio per-cpu
counter before we submit a raid56 io, and decrease the counter
when the raid56 io ends.

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
  • Loading branch information
Miao Xie committed Dec 3, 2014
1 parent 7603597 commit 4245215
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 20 deletions.
7 changes: 6 additions & 1 deletion fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4097,7 +4097,12 @@ int btrfs_scrub_progress(struct btrfs_root *root, u64 devid,
/* dev-replace.c */
void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info);
void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info);
void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info);
void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount);

static inline void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info)
{
btrfs_bio_counter_sub(fs_info, 1);
}

/* reada.c */
struct reada_control {
Expand Down
4 changes: 2 additions & 2 deletions fs/btrfs/dev-replace.c
Original file line number Diff line number Diff line change
Expand Up @@ -920,9 +920,9 @@ void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info)
percpu_counter_inc(&fs_info->bio_counter);
}

void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info)
void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount)
{
percpu_counter_dec(&fs_info->bio_counter);
percpu_counter_sub(&fs_info->bio_counter, amount);

if (waitqueue_active(&fs_info->replace_wait))
wake_up(&fs_info->replace_wait);
Expand Down
41 changes: 32 additions & 9 deletions fs/btrfs/raid56.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ struct btrfs_raid_bio {
*/
int bio_list_bytes;

int generic_bio_cnt;

atomic_t refs;

atomic_t stripes_pending;
Expand Down Expand Up @@ -354,6 +356,7 @@ static void merge_rbio(struct btrfs_raid_bio *dest,
{
bio_list_merge(&dest->bio_list, &victim->bio_list);
dest->bio_list_bytes += victim->bio_list_bytes;
dest->generic_bio_cnt += victim->generic_bio_cnt;
bio_list_init(&victim->bio_list);
}

Expand Down Expand Up @@ -891,6 +894,10 @@ static void rbio_orig_end_io(struct btrfs_raid_bio *rbio, int err, int uptodate)
{
struct bio *cur = bio_list_get(&rbio->bio_list);
struct bio *next;

if (rbio->generic_bio_cnt)
btrfs_bio_counter_sub(rbio->fs_info, rbio->generic_bio_cnt);

free_raid_bio(rbio);

while (cur) {
Expand Down Expand Up @@ -1775,6 +1782,7 @@ int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
struct btrfs_raid_bio *rbio;
struct btrfs_plug_cb *plug = NULL;
struct blk_plug_cb *cb;
int ret;

rbio = alloc_rbio(root, bbio, raid_map, stripe_len);
if (IS_ERR(rbio)) {
Expand All @@ -1785,12 +1793,19 @@ int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
rbio->bio_list_bytes = bio->bi_iter.bi_size;
rbio->operation = BTRFS_RBIO_WRITE;

btrfs_bio_counter_inc_noblocked(root->fs_info);
rbio->generic_bio_cnt = 1;

/*
* don't plug on full rbios, just get them out the door
* as quickly as we can
*/
if (rbio_is_full(rbio))
return full_stripe_write(rbio);
if (rbio_is_full(rbio)) {
ret = full_stripe_write(rbio);
if (ret)
btrfs_bio_counter_dec(root->fs_info);
return ret;
}

cb = blk_check_plugged(btrfs_raid_unplug, root->fs_info,
sizeof(*plug));
Expand All @@ -1801,10 +1816,13 @@ int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
INIT_LIST_HEAD(&plug->rbio_list);
}
list_add_tail(&rbio->plug_list, &plug->rbio_list);
ret = 0;
} else {
return __raid56_parity_write(rbio);
ret = __raid56_parity_write(rbio);
if (ret)
btrfs_bio_counter_dec(root->fs_info);
}
return 0;
return ret;
}

/*
Expand Down Expand Up @@ -2139,31 +2157,36 @@ static int __raid56_parity_recover(struct btrfs_raid_bio *rbio)
*/
int raid56_parity_recover(struct btrfs_root *root, struct bio *bio,
struct btrfs_bio *bbio, u64 *raid_map,
u64 stripe_len, int mirror_num, int hold_bbio)
u64 stripe_len, int mirror_num, int generic_io)
{
struct btrfs_raid_bio *rbio;
int ret;

rbio = alloc_rbio(root, bbio, raid_map, stripe_len);
if (IS_ERR(rbio)) {
__free_bbio_and_raid_map(bbio, raid_map, !hold_bbio);
__free_bbio_and_raid_map(bbio, raid_map, generic_io);
return PTR_ERR(rbio);
}

if (hold_bbio)
set_bit(RBIO_HOLD_BBIO_MAP_BIT, &rbio->flags);
rbio->operation = BTRFS_RBIO_READ_REBUILD;
bio_list_add(&rbio->bio_list, bio);
rbio->bio_list_bytes = bio->bi_iter.bi_size;

rbio->faila = find_logical_bio_stripe(rbio, bio);
if (rbio->faila == -1) {
BUG();
__free_bbio_and_raid_map(bbio, raid_map, !hold_bbio);
__free_bbio_and_raid_map(bbio, raid_map, generic_io);
kfree(rbio);
return -EIO;
}

if (generic_io) {
btrfs_bio_counter_inc_noblocked(root->fs_info);
rbio->generic_bio_cnt = 1;
} else {
set_bit(RBIO_HOLD_BBIO_MAP_BIT, &rbio->flags);
}

/*
* reconstruct from the q stripe if they are
* asking for mirror 3
Expand Down
4 changes: 2 additions & 2 deletions fs/btrfs/raid56.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ struct btrfs_raid_bio;
struct btrfs_device;

int raid56_parity_recover(struct btrfs_root *root, struct bio *bio,
struct btrfs_bio *bbio, u64 *raid_map,
u64 stripe_len, int mirror_num, int hold_bbio);
struct btrfs_bio *bbio, u64 *raid_map,
u64 stripe_len, int mirror_num, int generic_io);
int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
struct btrfs_bio *bbio, u64 *raid_map,
u64 stripe_len);
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/scrub.c
Original file line number Diff line number Diff line change
Expand Up @@ -1477,7 +1477,7 @@ static int scrub_submit_raid56_bio_wait(struct btrfs_fs_info *fs_info,
ret = raid56_parity_recover(fs_info->fs_root, bio, page->recover->bbio,
page->recover->raid_map,
page->recover->map_length,
page->mirror_num, 1);
page->mirror_num, 0);
if (ret)
return ret;

Expand Down
7 changes: 2 additions & 5 deletions fs/btrfs/volumes.c
Original file line number Diff line number Diff line change
Expand Up @@ -5843,12 +5843,9 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
} else {
ret = raid56_parity_recover(root, bio, bbio,
raid_map, map_length,
mirror_num, 0);
mirror_num, 1);
}
/*
* FIXME, replace dosen't support raid56 yet, please fix
* it in the future.
*/

btrfs_bio_counter_dec(root->fs_info);
return ret;
}
Expand Down

0 comments on commit 4245215

Please sign in to comment.