提交 b4ee1782 编写于 作者: O Omar Sandoval 提交者: Chris Mason

Btrfs: add RAID 5/6 BTRFS_RBIO_REBUILD_MISSING operation

The current RAID 5/6 recovery code isn't quite prepared to handle
missing devices. In particular, it expects a bio that we previously
attempted to use in the read path, meaning that it has valid pages
allocated. However, missing devices have a NULL blkdev, and we can't
call bio_add_page() on a bio with a NULL blkdev. We could do manual
manipulation of bio->bi_io_vec, but that's pretty gross. So instead, add
a separate path that allows us to manually add pages to the rbio.
Signed-off-by: NOmar Sandoval <osandov@fb.com>
Signed-off-by: NChris Mason <clm@fb.com>
上级 7cb2c420
...@@ -61,9 +61,10 @@ ...@@ -61,9 +61,10 @@
#define RBIO_CACHE_SIZE 1024 #define RBIO_CACHE_SIZE 1024
enum btrfs_rbio_ops { enum btrfs_rbio_ops {
BTRFS_RBIO_WRITE = 0, BTRFS_RBIO_WRITE,
BTRFS_RBIO_READ_REBUILD = 1, BTRFS_RBIO_READ_REBUILD,
BTRFS_RBIO_PARITY_SCRUB = 2, BTRFS_RBIO_PARITY_SCRUB,
BTRFS_RBIO_REBUILD_MISSING,
}; };
struct btrfs_raid_bio { struct btrfs_raid_bio {
...@@ -602,6 +603,10 @@ static int rbio_can_merge(struct btrfs_raid_bio *last, ...@@ -602,6 +603,10 @@ static int rbio_can_merge(struct btrfs_raid_bio *last,
cur->operation == BTRFS_RBIO_PARITY_SCRUB) cur->operation == BTRFS_RBIO_PARITY_SCRUB)
return 0; return 0;
if (last->operation == BTRFS_RBIO_REBUILD_MISSING ||
cur->operation == BTRFS_RBIO_REBUILD_MISSING)
return 0;
return 1; return 1;
} }
...@@ -793,7 +798,10 @@ static noinline void unlock_stripe(struct btrfs_raid_bio *rbio) ...@@ -793,7 +798,10 @@ static noinline void unlock_stripe(struct btrfs_raid_bio *rbio)
if (next->operation == BTRFS_RBIO_READ_REBUILD) if (next->operation == BTRFS_RBIO_READ_REBUILD)
async_read_rebuild(next); async_read_rebuild(next);
else if (next->operation == BTRFS_RBIO_WRITE) { else if (next->operation == BTRFS_RBIO_REBUILD_MISSING) {
steal_rbio(rbio, next);
async_read_rebuild(next);
} else if (next->operation == BTRFS_RBIO_WRITE) {
steal_rbio(rbio, next); steal_rbio(rbio, next);
async_rmw_stripe(next); async_rmw_stripe(next);
} else if (next->operation == BTRFS_RBIO_PARITY_SCRUB) { } else if (next->operation == BTRFS_RBIO_PARITY_SCRUB) {
...@@ -1809,7 +1817,8 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio) ...@@ -1809,7 +1817,8 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio)
faila = rbio->faila; faila = rbio->faila;
failb = rbio->failb; failb = rbio->failb;
if (rbio->operation == BTRFS_RBIO_READ_REBUILD) { if (rbio->operation == BTRFS_RBIO_READ_REBUILD ||
rbio->operation == BTRFS_RBIO_REBUILD_MISSING) {
spin_lock_irq(&rbio->bio_list_lock); spin_lock_irq(&rbio->bio_list_lock);
set_bit(RBIO_RMW_LOCKED_BIT, &rbio->flags); set_bit(RBIO_RMW_LOCKED_BIT, &rbio->flags);
spin_unlock_irq(&rbio->bio_list_lock); spin_unlock_irq(&rbio->bio_list_lock);
...@@ -1834,7 +1843,8 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio) ...@@ -1834,7 +1843,8 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio)
* if we're rebuilding a read, we have to use * if we're rebuilding a read, we have to use
* pages from the bio list * pages from the bio list
*/ */
if (rbio->operation == BTRFS_RBIO_READ_REBUILD && if ((rbio->operation == BTRFS_RBIO_READ_REBUILD ||
rbio->operation == BTRFS_RBIO_REBUILD_MISSING) &&
(stripe == faila || stripe == failb)) { (stripe == faila || stripe == failb)) {
page = page_in_rbio(rbio, stripe, pagenr, 0); page = page_in_rbio(rbio, stripe, pagenr, 0);
} else { } else {
...@@ -1943,7 +1953,8 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio) ...@@ -1943,7 +1953,8 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio)
* if we're rebuilding a read, we have to use * if we're rebuilding a read, we have to use
* pages from the bio list * pages from the bio list
*/ */
if (rbio->operation == BTRFS_RBIO_READ_REBUILD && if ((rbio->operation == BTRFS_RBIO_READ_REBUILD ||
rbio->operation == BTRFS_RBIO_REBUILD_MISSING) &&
(stripe == faila || stripe == failb)) { (stripe == faila || stripe == failb)) {
page = page_in_rbio(rbio, stripe, pagenr, 0); page = page_in_rbio(rbio, stripe, pagenr, 0);
} else { } else {
...@@ -1964,6 +1975,8 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio) ...@@ -1964,6 +1975,8 @@ static void __raid_recover_end_io(struct btrfs_raid_bio *rbio)
else else
clear_bit(RBIO_CACHE_READY_BIT, &rbio->flags); clear_bit(RBIO_CACHE_READY_BIT, &rbio->flags);
rbio_orig_end_io(rbio, err, err == 0);
} else if (rbio->operation == BTRFS_RBIO_REBUILD_MISSING) {
rbio_orig_end_io(rbio, err, err == 0); rbio_orig_end_io(rbio, err, err == 0);
} else if (err == 0) { } else if (err == 0) {
rbio->faila = -1; rbio->faila = -1;
...@@ -2101,7 +2114,8 @@ static int __raid56_parity_recover(struct btrfs_raid_bio *rbio) ...@@ -2101,7 +2114,8 @@ static int __raid56_parity_recover(struct btrfs_raid_bio *rbio)
return 0; return 0;
cleanup: cleanup:
if (rbio->operation == BTRFS_RBIO_READ_REBUILD) if (rbio->operation == BTRFS_RBIO_READ_REBUILD ||
rbio->operation == BTRFS_RBIO_REBUILD_MISSING)
rbio_orig_end_io(rbio, -EIO, 0); rbio_orig_end_io(rbio, -EIO, 0);
return -EIO; return -EIO;
} }
...@@ -2232,8 +2246,9 @@ raid56_parity_alloc_scrub_rbio(struct btrfs_root *root, struct bio *bio, ...@@ -2232,8 +2246,9 @@ raid56_parity_alloc_scrub_rbio(struct btrfs_root *root, struct bio *bio,
return rbio; return rbio;
} }
void raid56_parity_add_scrub_pages(struct btrfs_raid_bio *rbio, /* Used for both parity scrub and missing. */
struct page *page, u64 logical) void raid56_add_scrub_pages(struct btrfs_raid_bio *rbio, struct page *page,
u64 logical)
{ {
int stripe_offset; int stripe_offset;
int index; int index;
...@@ -2668,3 +2683,55 @@ void raid56_parity_submit_scrub_rbio(struct btrfs_raid_bio *rbio) ...@@ -2668,3 +2683,55 @@ void raid56_parity_submit_scrub_rbio(struct btrfs_raid_bio *rbio)
if (!lock_stripe_add(rbio)) if (!lock_stripe_add(rbio))
async_scrub_parity(rbio); async_scrub_parity(rbio);
} }
/* The following code is used for dev replace of a missing RAID 5/6 device. */
struct btrfs_raid_bio *
raid56_alloc_missing_rbio(struct btrfs_root *root, struct bio *bio,
struct btrfs_bio *bbio, u64 length)
{
struct btrfs_raid_bio *rbio;
rbio = alloc_rbio(root, bbio, length);
if (IS_ERR(rbio))
return NULL;
rbio->operation = BTRFS_RBIO_REBUILD_MISSING;
bio_list_add(&rbio->bio_list, bio);
/*
* This is a special bio which is used to hold the completion handler
* and make the scrub rbio is similar to the other types
*/
ASSERT(!bio->bi_iter.bi_size);
rbio->faila = find_logical_bio_stripe(rbio, bio);
if (rbio->faila == -1) {
BUG();
kfree(rbio);
return NULL;
}
return rbio;
}
static void missing_raid56_work(struct btrfs_work *work)
{
struct btrfs_raid_bio *rbio;
rbio = container_of(work, struct btrfs_raid_bio, work);
__raid56_parity_recover(rbio);
}
static void async_missing_raid56(struct btrfs_raid_bio *rbio)
{
btrfs_init_work(&rbio->work, btrfs_rmw_helper,
missing_raid56_work, NULL, NULL);
btrfs_queue_work(rbio->fs_info->rmw_workers, &rbio->work);
}
void raid56_submit_missing_rbio(struct btrfs_raid_bio *rbio)
{
if (!lock_stripe_add(rbio))
async_missing_raid56(rbio);
}
...@@ -48,15 +48,21 @@ int raid56_parity_recover(struct btrfs_root *root, struct bio *bio, ...@@ -48,15 +48,21 @@ int raid56_parity_recover(struct btrfs_root *root, struct bio *bio,
int raid56_parity_write(struct btrfs_root *root, struct bio *bio, int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
struct btrfs_bio *bbio, u64 stripe_len); struct btrfs_bio *bbio, u64 stripe_len);
void raid56_add_scrub_pages(struct btrfs_raid_bio *rbio, struct page *page,
u64 logical);
struct btrfs_raid_bio * struct btrfs_raid_bio *
raid56_parity_alloc_scrub_rbio(struct btrfs_root *root, struct bio *bio, raid56_parity_alloc_scrub_rbio(struct btrfs_root *root, struct bio *bio,
struct btrfs_bio *bbio, u64 stripe_len, struct btrfs_bio *bbio, u64 stripe_len,
struct btrfs_device *scrub_dev, struct btrfs_device *scrub_dev,
unsigned long *dbitmap, int stripe_nsectors); unsigned long *dbitmap, int stripe_nsectors);
void raid56_parity_add_scrub_pages(struct btrfs_raid_bio *rbio,
struct page *page, u64 logical);
void raid56_parity_submit_scrub_rbio(struct btrfs_raid_bio *rbio); void raid56_parity_submit_scrub_rbio(struct btrfs_raid_bio *rbio);
struct btrfs_raid_bio *
raid56_alloc_missing_rbio(struct btrfs_root *root, struct bio *bio,
struct btrfs_bio *bbio, u64 length);
void raid56_submit_missing_rbio(struct btrfs_raid_bio *rbio);
int btrfs_alloc_stripe_hash_table(struct btrfs_fs_info *info); int btrfs_alloc_stripe_hash_table(struct btrfs_fs_info *info);
void btrfs_free_stripe_hash_table(struct btrfs_fs_info *info); void btrfs_free_stripe_hash_table(struct btrfs_fs_info *info);
#endif #endif
...@@ -2720,8 +2720,7 @@ static void scrub_parity_check_and_repair(struct scrub_parity *sparity) ...@@ -2720,8 +2720,7 @@ static void scrub_parity_check_and_repair(struct scrub_parity *sparity)
goto rbio_out; goto rbio_out;
list_for_each_entry(spage, &sparity->spages, list) list_for_each_entry(spage, &sparity->spages, list)
raid56_parity_add_scrub_pages(rbio, spage->page, raid56_add_scrub_pages(rbio, spage->page, spage->logical);
spage->logical);
scrub_pending_bio_inc(sctx); scrub_pending_bio_inc(sctx);
raid56_parity_submit_scrub_rbio(rbio); raid56_parity_submit_scrub_rbio(rbio);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册