Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs
Pull btrfs updates from Chris Mason: "This pull is mostly cleanups and fixes: - The raid5/6 cleanups from Zhao Lei fixup some long standing warts in the code and add improvements on top of the scrubbing support from 3.19. - Josef has round one of our ENOSPC fixes coming from large btrfs clusters here at FB. - Dave Sterba continues a long series of cleanups (thanks Dave), and Filipe continues hammering on corner cases in fsync and others This all was held up a little trying to track down a use-after-free in btrfs raid5/6. It's not clear yet if this is just made easier to trigger with this pull or if its a new bug from the raid5/6 cleanups. Dave Sterba is the only one to trigger it so far, but he has a consistent way to reproduce, so we'll get it nailed shortly" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs: (68 commits) Btrfs: don't remove extents and xattrs when logging new names Btrfs: fix fsync data loss after adding hard link to inode Btrfs: fix BUG_ON in btrfs_orphan_add() when delete unused block group Btrfs: account for large extents with enospc Btrfs: don't set and clear delalloc for O_DIRECT writes Btrfs: only adjust outstanding_extents when we do a short write btrfs: Fix out-of-space bug Btrfs: scrub, fix sleep in atomic context Btrfs: fix scheduler warning when syncing log Btrfs: Remove unnecessary placeholder in btrfs_err_code btrfs: cleanup init for list in free-space-cache btrfs: delete chunk allocation attemp when setting block group ro btrfs: clear bio reference after submit_one_bio() Btrfs: fix scrub race leading to use-after-free Btrfs: add missing cleanup on sysfs init failure Btrfs: fix race between transaction commit and empty block group removal btrfs: add more checks to btrfs_read_sys_array btrfs: cleanup, rename a few variables in btrfs_read_sys_array btrfs: add checks for sys_chunk_array sizes btrfs: more superblock checks, lower bounds on devices and sectorsize/nodesize ...
This commit is contained in:
313
fs/btrfs/scrub.c
313
fs/btrfs/scrub.c
@@ -66,7 +66,6 @@ struct scrub_ctx;
|
||||
struct scrub_recover {
|
||||
atomic_t refs;
|
||||
struct btrfs_bio *bbio;
|
||||
u64 *raid_map;
|
||||
u64 map_length;
|
||||
};
|
||||
|
||||
@@ -80,7 +79,7 @@ struct scrub_page {
|
||||
u64 logical;
|
||||
u64 physical;
|
||||
u64 physical_for_dev_replace;
|
||||
atomic_t ref_count;
|
||||
atomic_t refs;
|
||||
struct {
|
||||
unsigned int mirror_num:8;
|
||||
unsigned int have_csum:1;
|
||||
@@ -113,7 +112,7 @@ struct scrub_block {
|
||||
struct scrub_page *pagev[SCRUB_MAX_PAGES_PER_BLOCK];
|
||||
int page_count;
|
||||
atomic_t outstanding_pages;
|
||||
atomic_t ref_count; /* free mem on transition to zero */
|
||||
atomic_t refs; /* free mem on transition to zero */
|
||||
struct scrub_ctx *sctx;
|
||||
struct scrub_parity *sparity;
|
||||
struct {
|
||||
@@ -142,7 +141,7 @@ struct scrub_parity {
|
||||
|
||||
int stripe_len;
|
||||
|
||||
atomic_t ref_count;
|
||||
atomic_t refs;
|
||||
|
||||
struct list_head spages;
|
||||
|
||||
@@ -194,6 +193,15 @@ struct scrub_ctx {
|
||||
*/
|
||||
struct btrfs_scrub_progress stat;
|
||||
spinlock_t stat_lock;
|
||||
|
||||
/*
|
||||
* Use a ref counter to avoid use-after-free issues. Scrub workers
|
||||
* decrement bios_in_flight and workers_pending and then do a wakeup
|
||||
* on the list_wait wait queue. We must ensure the main scrub task
|
||||
* doesn't free the scrub context before or while the workers are
|
||||
* doing the wakeup() call.
|
||||
*/
|
||||
atomic_t refs;
|
||||
};
|
||||
|
||||
struct scrub_fixup_nodatasum {
|
||||
@@ -236,10 +244,7 @@ static void scrub_pending_bio_dec(struct scrub_ctx *sctx);
|
||||
static void scrub_pending_trans_workers_inc(struct scrub_ctx *sctx);
|
||||
static void scrub_pending_trans_workers_dec(struct scrub_ctx *sctx);
|
||||
static int scrub_handle_errored_block(struct scrub_block *sblock_to_check);
|
||||
static int scrub_setup_recheck_block(struct scrub_ctx *sctx,
|
||||
struct btrfs_fs_info *fs_info,
|
||||
struct scrub_block *original_sblock,
|
||||
u64 length, u64 logical,
|
||||
static int scrub_setup_recheck_block(struct scrub_block *original_sblock,
|
||||
struct scrub_block *sblocks_for_recheck);
|
||||
static void scrub_recheck_block(struct btrfs_fs_info *fs_info,
|
||||
struct scrub_block *sblock, int is_metadata,
|
||||
@@ -251,8 +256,7 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
|
||||
const u8 *csum, u64 generation,
|
||||
u16 csum_size);
|
||||
static int scrub_repair_block_from_good_copy(struct scrub_block *sblock_bad,
|
||||
struct scrub_block *sblock_good,
|
||||
int force_write);
|
||||
struct scrub_block *sblock_good);
|
||||
static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad,
|
||||
struct scrub_block *sblock_good,
|
||||
int page_num, int force_write);
|
||||
@@ -302,10 +306,12 @@ static int copy_nocow_pages(struct scrub_ctx *sctx, u64 logical, u64 len,
|
||||
static void copy_nocow_pages_worker(struct btrfs_work *work);
|
||||
static void __scrub_blocked_if_needed(struct btrfs_fs_info *fs_info);
|
||||
static void scrub_blocked_if_needed(struct btrfs_fs_info *fs_info);
|
||||
static void scrub_put_ctx(struct scrub_ctx *sctx);
|
||||
|
||||
|
||||
static void scrub_pending_bio_inc(struct scrub_ctx *sctx)
|
||||
{
|
||||
atomic_inc(&sctx->refs);
|
||||
atomic_inc(&sctx->bios_in_flight);
|
||||
}
|
||||
|
||||
@@ -313,6 +319,7 @@ static void scrub_pending_bio_dec(struct scrub_ctx *sctx)
|
||||
{
|
||||
atomic_dec(&sctx->bios_in_flight);
|
||||
wake_up(&sctx->list_wait);
|
||||
scrub_put_ctx(sctx);
|
||||
}
|
||||
|
||||
static void __scrub_blocked_if_needed(struct btrfs_fs_info *fs_info)
|
||||
@@ -346,6 +353,7 @@ static void scrub_pending_trans_workers_inc(struct scrub_ctx *sctx)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = sctx->dev_root->fs_info;
|
||||
|
||||
atomic_inc(&sctx->refs);
|
||||
/*
|
||||
* increment scrubs_running to prevent cancel requests from
|
||||
* completing as long as a worker is running. we must also
|
||||
@@ -388,6 +396,7 @@ static void scrub_pending_trans_workers_dec(struct scrub_ctx *sctx)
|
||||
atomic_dec(&sctx->workers_pending);
|
||||
wake_up(&fs_info->scrub_pause_wait);
|
||||
wake_up(&sctx->list_wait);
|
||||
scrub_put_ctx(sctx);
|
||||
}
|
||||
|
||||
static void scrub_free_csums(struct scrub_ctx *sctx)
|
||||
@@ -433,6 +442,12 @@ static noinline_for_stack void scrub_free_ctx(struct scrub_ctx *sctx)
|
||||
kfree(sctx);
|
||||
}
|
||||
|
||||
static void scrub_put_ctx(struct scrub_ctx *sctx)
|
||||
{
|
||||
if (atomic_dec_and_test(&sctx->refs))
|
||||
scrub_free_ctx(sctx);
|
||||
}
|
||||
|
||||
static noinline_for_stack
|
||||
struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev, int is_dev_replace)
|
||||
{
|
||||
@@ -457,6 +472,7 @@ struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev, int is_dev_replace)
|
||||
sctx = kzalloc(sizeof(*sctx), GFP_NOFS);
|
||||
if (!sctx)
|
||||
goto nomem;
|
||||
atomic_set(&sctx->refs, 1);
|
||||
sctx->is_dev_replace = is_dev_replace;
|
||||
sctx->pages_per_rd_bio = pages_per_rd_bio;
|
||||
sctx->curr = -1;
|
||||
@@ -520,6 +536,7 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root,
|
||||
struct inode_fs_paths *ipath = NULL;
|
||||
struct btrfs_root *local_root;
|
||||
struct btrfs_key root_key;
|
||||
struct btrfs_key key;
|
||||
|
||||
root_key.objectid = root;
|
||||
root_key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
@@ -530,7 +547,14 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root,
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = inode_item_info(inum, 0, local_root, swarn->path);
|
||||
/*
|
||||
* this makes the path point to (inum INODE_ITEM ioff)
|
||||
*/
|
||||
key.objectid = inum;
|
||||
key.type = BTRFS_INODE_ITEM_KEY;
|
||||
key.offset = 0;
|
||||
|
||||
ret = btrfs_search_slot(NULL, local_root, &key, swarn->path, 0, 0);
|
||||
if (ret) {
|
||||
btrfs_release_path(swarn->path);
|
||||
goto err;
|
||||
@@ -848,8 +872,7 @@ static inline void scrub_get_recover(struct scrub_recover *recover)
|
||||
static inline void scrub_put_recover(struct scrub_recover *recover)
|
||||
{
|
||||
if (atomic_dec_and_test(&recover->refs)) {
|
||||
kfree(recover->bbio);
|
||||
kfree(recover->raid_map);
|
||||
btrfs_put_bbio(recover->bbio);
|
||||
kfree(recover);
|
||||
}
|
||||
}
|
||||
@@ -955,8 +978,7 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
|
||||
}
|
||||
|
||||
/* setup the context, map the logical blocks and alloc the pages */
|
||||
ret = scrub_setup_recheck_block(sctx, fs_info, sblock_to_check, length,
|
||||
logical, sblocks_for_recheck);
|
||||
ret = scrub_setup_recheck_block(sblock_to_check, sblocks_for_recheck);
|
||||
if (ret) {
|
||||
spin_lock(&sctx->stat_lock);
|
||||
sctx->stat.read_errors++;
|
||||
@@ -1030,9 +1052,10 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
|
||||
if (!is_metadata && !have_csum) {
|
||||
struct scrub_fixup_nodatasum *fixup_nodatasum;
|
||||
|
||||
nodatasum_case:
|
||||
WARN_ON(sctx->is_dev_replace);
|
||||
|
||||
nodatasum_case:
|
||||
|
||||
/*
|
||||
* !is_metadata and !have_csum, this means that the data
|
||||
* might not be COW'ed, that it might be modified
|
||||
@@ -1091,76 +1114,20 @@ nodatasum_case:
|
||||
sblock_other->no_io_error_seen) {
|
||||
if (sctx->is_dev_replace) {
|
||||
scrub_write_block_to_dev_replace(sblock_other);
|
||||
} else {
|
||||
int force_write = is_metadata || have_csum;
|
||||
|
||||
ret = scrub_repair_block_from_good_copy(
|
||||
sblock_bad, sblock_other,
|
||||
force_write);
|
||||
}
|
||||
if (0 == ret)
|
||||
goto corrected_error;
|
||||
} else {
|
||||
ret = scrub_repair_block_from_good_copy(
|
||||
sblock_bad, sblock_other);
|
||||
if (!ret)
|
||||
goto corrected_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* for dev_replace, pick good pages and write to the target device.
|
||||
*/
|
||||
if (sctx->is_dev_replace) {
|
||||
success = 1;
|
||||
for (page_num = 0; page_num < sblock_bad->page_count;
|
||||
page_num++) {
|
||||
int sub_success;
|
||||
|
||||
sub_success = 0;
|
||||
for (mirror_index = 0;
|
||||
mirror_index < BTRFS_MAX_MIRRORS &&
|
||||
sblocks_for_recheck[mirror_index].page_count > 0;
|
||||
mirror_index++) {
|
||||
struct scrub_block *sblock_other =
|
||||
sblocks_for_recheck + mirror_index;
|
||||
struct scrub_page *page_other =
|
||||
sblock_other->pagev[page_num];
|
||||
|
||||
if (!page_other->io_error) {
|
||||
ret = scrub_write_page_to_dev_replace(
|
||||
sblock_other, page_num);
|
||||
if (ret == 0) {
|
||||
/* succeeded for this page */
|
||||
sub_success = 1;
|
||||
break;
|
||||
} else {
|
||||
btrfs_dev_replace_stats_inc(
|
||||
&sctx->dev_root->
|
||||
fs_info->dev_replace.
|
||||
num_write_errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sub_success) {
|
||||
/*
|
||||
* did not find a mirror to fetch the page
|
||||
* from. scrub_write_page_to_dev_replace()
|
||||
* handles this case (page->io_error), by
|
||||
* filling the block with zeros before
|
||||
* submitting the write request
|
||||
*/
|
||||
success = 0;
|
||||
ret = scrub_write_page_to_dev_replace(
|
||||
sblock_bad, page_num);
|
||||
if (ret)
|
||||
btrfs_dev_replace_stats_inc(
|
||||
&sctx->dev_root->fs_info->
|
||||
dev_replace.num_write_errors);
|
||||
}
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
if (sblock_bad->no_io_error_seen && !sctx->is_dev_replace)
|
||||
goto did_not_correct_error;
|
||||
|
||||
/*
|
||||
* for regular scrub, repair those pages that are errored.
|
||||
* In case of I/O errors in the area that is supposed to be
|
||||
* repaired, continue by picking good copies of those pages.
|
||||
* Select the good pages from mirrors to rewrite bad pages from
|
||||
@@ -1184,44 +1151,64 @@ nodatasum_case:
|
||||
* mirror, even if other 512 byte sectors in the same PAGE_SIZE
|
||||
* area are unreadable.
|
||||
*/
|
||||
|
||||
/* can only fix I/O errors from here on */
|
||||
if (sblock_bad->no_io_error_seen)
|
||||
goto did_not_correct_error;
|
||||
|
||||
success = 1;
|
||||
for (page_num = 0; page_num < sblock_bad->page_count; page_num++) {
|
||||
for (page_num = 0; page_num < sblock_bad->page_count;
|
||||
page_num++) {
|
||||
struct scrub_page *page_bad = sblock_bad->pagev[page_num];
|
||||
struct scrub_block *sblock_other = NULL;
|
||||
|
||||
if (!page_bad->io_error)
|
||||
/* skip no-io-error page in scrub */
|
||||
if (!page_bad->io_error && !sctx->is_dev_replace)
|
||||
continue;
|
||||
|
||||
for (mirror_index = 0;
|
||||
mirror_index < BTRFS_MAX_MIRRORS &&
|
||||
sblocks_for_recheck[mirror_index].page_count > 0;
|
||||
mirror_index++) {
|
||||
struct scrub_block *sblock_other = sblocks_for_recheck +
|
||||
mirror_index;
|
||||
struct scrub_page *page_other = sblock_other->pagev[
|
||||
page_num];
|
||||
|
||||
if (!page_other->io_error) {
|
||||
ret = scrub_repair_page_from_good_copy(
|
||||
sblock_bad, sblock_other, page_num, 0);
|
||||
if (0 == ret) {
|
||||
page_bad->io_error = 0;
|
||||
break; /* succeeded for this page */
|
||||
/* try to find no-io-error page in mirrors */
|
||||
if (page_bad->io_error) {
|
||||
for (mirror_index = 0;
|
||||
mirror_index < BTRFS_MAX_MIRRORS &&
|
||||
sblocks_for_recheck[mirror_index].page_count > 0;
|
||||
mirror_index++) {
|
||||
if (!sblocks_for_recheck[mirror_index].
|
||||
pagev[page_num]->io_error) {
|
||||
sblock_other = sblocks_for_recheck +
|
||||
mirror_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!sblock_other)
|
||||
success = 0;
|
||||
}
|
||||
|
||||
if (page_bad->io_error) {
|
||||
/* did not find a mirror to copy the page from */
|
||||
success = 0;
|
||||
if (sctx->is_dev_replace) {
|
||||
/*
|
||||
* did not find a mirror to fetch the page
|
||||
* from. scrub_write_page_to_dev_replace()
|
||||
* handles this case (page->io_error), by
|
||||
* filling the block with zeros before
|
||||
* submitting the write request
|
||||
*/
|
||||
if (!sblock_other)
|
||||
sblock_other = sblock_bad;
|
||||
|
||||
if (scrub_write_page_to_dev_replace(sblock_other,
|
||||
page_num) != 0) {
|
||||
btrfs_dev_replace_stats_inc(
|
||||
&sctx->dev_root->
|
||||
fs_info->dev_replace.
|
||||
num_write_errors);
|
||||
success = 0;
|
||||
}
|
||||
} else if (sblock_other) {
|
||||
ret = scrub_repair_page_from_good_copy(sblock_bad,
|
||||
sblock_other,
|
||||
page_num, 0);
|
||||
if (0 == ret)
|
||||
page_bad->io_error = 0;
|
||||
else
|
||||
success = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
if (success && !sctx->is_dev_replace) {
|
||||
if (is_metadata || have_csum) {
|
||||
/*
|
||||
* need to verify the checksum now that all
|
||||
@@ -1288,19 +1275,18 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int scrub_nr_raid_mirrors(struct btrfs_bio *bbio, u64 *raid_map)
|
||||
static inline int scrub_nr_raid_mirrors(struct btrfs_bio *bbio)
|
||||
{
|
||||
if (raid_map) {
|
||||
if (raid_map[bbio->num_stripes - 1] == RAID6_Q_STRIPE)
|
||||
return 3;
|
||||
else
|
||||
return 2;
|
||||
} else {
|
||||
if (bbio->map_type & BTRFS_BLOCK_GROUP_RAID5)
|
||||
return 2;
|
||||
else if (bbio->map_type & BTRFS_BLOCK_GROUP_RAID6)
|
||||
return 3;
|
||||
else
|
||||
return (int)bbio->num_stripes;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void scrub_stripe_index_and_offset(u64 logical, u64 *raid_map,
|
||||
static inline void scrub_stripe_index_and_offset(u64 logical, u64 map_type,
|
||||
u64 *raid_map,
|
||||
u64 mapped_length,
|
||||
int nstripes, int mirror,
|
||||
int *stripe_index,
|
||||
@@ -1308,7 +1294,7 @@ static inline void scrub_stripe_index_and_offset(u64 logical, u64 *raid_map,
|
||||
{
|
||||
int i;
|
||||
|
||||
if (raid_map) {
|
||||
if (map_type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
|
||||
/* RAID5/6 */
|
||||
for (i = 0; i < nstripes; i++) {
|
||||
if (raid_map[i] == RAID6_Q_STRIPE ||
|
||||
@@ -1329,72 +1315,65 @@ static inline void scrub_stripe_index_and_offset(u64 logical, u64 *raid_map,
|
||||
}
|
||||
}
|
||||
|
||||
static int scrub_setup_recheck_block(struct scrub_ctx *sctx,
|
||||
struct btrfs_fs_info *fs_info,
|
||||
struct scrub_block *original_sblock,
|
||||
u64 length, u64 logical,
|
||||
static int scrub_setup_recheck_block(struct scrub_block *original_sblock,
|
||||
struct scrub_block *sblocks_for_recheck)
|
||||
{
|
||||
struct scrub_ctx *sctx = original_sblock->sctx;
|
||||
struct btrfs_fs_info *fs_info = sctx->dev_root->fs_info;
|
||||
u64 length = original_sblock->page_count * PAGE_SIZE;
|
||||
u64 logical = original_sblock->pagev[0]->logical;
|
||||
struct scrub_recover *recover;
|
||||
struct btrfs_bio *bbio;
|
||||
u64 *raid_map;
|
||||
u64 sublen;
|
||||
u64 mapped_length;
|
||||
u64 stripe_offset;
|
||||
int stripe_index;
|
||||
int page_index;
|
||||
int page_index = 0;
|
||||
int mirror_index;
|
||||
int nmirrors;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* note: the two members ref_count and outstanding_pages
|
||||
* note: the two members refs and outstanding_pages
|
||||
* are not used (and not set) in the blocks that are used for
|
||||
* the recheck procedure
|
||||
*/
|
||||
|
||||
page_index = 0;
|
||||
while (length > 0) {
|
||||
sublen = min_t(u64, length, PAGE_SIZE);
|
||||
mapped_length = sublen;
|
||||
bbio = NULL;
|
||||
raid_map = NULL;
|
||||
|
||||
/*
|
||||
* with a length of PAGE_SIZE, each returned stripe
|
||||
* represents one mirror
|
||||
*/
|
||||
ret = btrfs_map_sblock(fs_info, REQ_GET_READ_MIRRORS, logical,
|
||||
&mapped_length, &bbio, 0, &raid_map);
|
||||
&mapped_length, &bbio, 0, 1);
|
||||
if (ret || !bbio || mapped_length < sublen) {
|
||||
kfree(bbio);
|
||||
kfree(raid_map);
|
||||
btrfs_put_bbio(bbio);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
recover = kzalloc(sizeof(struct scrub_recover), GFP_NOFS);
|
||||
if (!recover) {
|
||||
kfree(bbio);
|
||||
kfree(raid_map);
|
||||
btrfs_put_bbio(bbio);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
atomic_set(&recover->refs, 1);
|
||||
recover->bbio = bbio;
|
||||
recover->raid_map = raid_map;
|
||||
recover->map_length = mapped_length;
|
||||
|
||||
BUG_ON(page_index >= SCRUB_PAGES_PER_RD_BIO);
|
||||
|
||||
nmirrors = scrub_nr_raid_mirrors(bbio, raid_map);
|
||||
nmirrors = min(scrub_nr_raid_mirrors(bbio), BTRFS_MAX_MIRRORS);
|
||||
|
||||
for (mirror_index = 0; mirror_index < nmirrors;
|
||||
mirror_index++) {
|
||||
struct scrub_block *sblock;
|
||||
struct scrub_page *page;
|
||||
|
||||
if (mirror_index >= BTRFS_MAX_MIRRORS)
|
||||
continue;
|
||||
|
||||
sblock = sblocks_for_recheck + mirror_index;
|
||||
sblock->sctx = sctx;
|
||||
page = kzalloc(sizeof(*page), GFP_NOFS);
|
||||
@@ -1410,9 +1389,12 @@ leave_nomem:
|
||||
sblock->pagev[page_index] = page;
|
||||
page->logical = logical;
|
||||
|
||||
scrub_stripe_index_and_offset(logical, raid_map,
|
||||
scrub_stripe_index_and_offset(logical,
|
||||
bbio->map_type,
|
||||
bbio->raid_map,
|
||||
mapped_length,
|
||||
bbio->num_stripes,
|
||||
bbio->num_stripes -
|
||||
bbio->num_tgtdevs,
|
||||
mirror_index,
|
||||
&stripe_index,
|
||||
&stripe_offset);
|
||||
@@ -1458,7 +1440,8 @@ static void scrub_bio_wait_endio(struct bio *bio, int error)
|
||||
|
||||
static inline int scrub_is_page_on_raid56(struct scrub_page *page)
|
||||
{
|
||||
return page->recover && page->recover->raid_map;
|
||||
return page->recover &&
|
||||
(page->recover->bbio->map_type & BTRFS_BLOCK_GROUP_RAID56_MASK);
|
||||
}
|
||||
|
||||
static int scrub_submit_raid56_bio_wait(struct btrfs_fs_info *fs_info,
|
||||
@@ -1475,7 +1458,6 @@ static int scrub_submit_raid56_bio_wait(struct btrfs_fs_info *fs_info,
|
||||
bio->bi_end_io = scrub_bio_wait_endio;
|
||||
|
||||
ret = raid56_parity_recover(fs_info->fs_root, bio, page->recover->bbio,
|
||||
page->recover->raid_map,
|
||||
page->recover->map_length,
|
||||
page->mirror_num, 0);
|
||||
if (ret)
|
||||
@@ -1615,8 +1597,7 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
|
||||
}
|
||||
|
||||
static int scrub_repair_block_from_good_copy(struct scrub_block *sblock_bad,
|
||||
struct scrub_block *sblock_good,
|
||||
int force_write)
|
||||
struct scrub_block *sblock_good)
|
||||
{
|
||||
int page_num;
|
||||
int ret = 0;
|
||||
@@ -1626,8 +1607,7 @@ static int scrub_repair_block_from_good_copy(struct scrub_block *sblock_bad,
|
||||
|
||||
ret_sub = scrub_repair_page_from_good_copy(sblock_bad,
|
||||
sblock_good,
|
||||
page_num,
|
||||
force_write);
|
||||
page_num, 1);
|
||||
if (ret_sub)
|
||||
ret = ret_sub;
|
||||
}
|
||||
@@ -2067,12 +2047,12 @@ static int scrub_checksum_super(struct scrub_block *sblock)
|
||||
|
||||
static void scrub_block_get(struct scrub_block *sblock)
|
||||
{
|
||||
atomic_inc(&sblock->ref_count);
|
||||
atomic_inc(&sblock->refs);
|
||||
}
|
||||
|
||||
static void scrub_block_put(struct scrub_block *sblock)
|
||||
{
|
||||
if (atomic_dec_and_test(&sblock->ref_count)) {
|
||||
if (atomic_dec_and_test(&sblock->refs)) {
|
||||
int i;
|
||||
|
||||
if (sblock->sparity)
|
||||
@@ -2086,12 +2066,12 @@ static void scrub_block_put(struct scrub_block *sblock)
|
||||
|
||||
static void scrub_page_get(struct scrub_page *spage)
|
||||
{
|
||||
atomic_inc(&spage->ref_count);
|
||||
atomic_inc(&spage->refs);
|
||||
}
|
||||
|
||||
static void scrub_page_put(struct scrub_page *spage)
|
||||
{
|
||||
if (atomic_dec_and_test(&spage->ref_count)) {
|
||||
if (atomic_dec_and_test(&spage->refs)) {
|
||||
if (spage->page)
|
||||
__free_page(spage->page);
|
||||
kfree(spage);
|
||||
@@ -2217,7 +2197,7 @@ static int scrub_pages(struct scrub_ctx *sctx, u64 logical, u64 len,
|
||||
|
||||
/* one ref inside this function, plus one for each page added to
|
||||
* a bio later on */
|
||||
atomic_set(&sblock->ref_count, 1);
|
||||
atomic_set(&sblock->refs, 1);
|
||||
sblock->sctx = sctx;
|
||||
sblock->no_io_error_seen = 1;
|
||||
|
||||
@@ -2510,7 +2490,7 @@ static int scrub_pages_for_parity(struct scrub_parity *sparity,
|
||||
|
||||
/* one ref inside this function, plus one for each page added to
|
||||
* a bio later on */
|
||||
atomic_set(&sblock->ref_count, 1);
|
||||
atomic_set(&sblock->refs, 1);
|
||||
sblock->sctx = sctx;
|
||||
sblock->no_io_error_seen = 1;
|
||||
sblock->sparity = sparity;
|
||||
@@ -2705,7 +2685,6 @@ static void scrub_parity_check_and_repair(struct scrub_parity *sparity)
|
||||
struct btrfs_raid_bio *rbio;
|
||||
struct scrub_page *spage;
|
||||
struct btrfs_bio *bbio = NULL;
|
||||
u64 *raid_map = NULL;
|
||||
u64 length;
|
||||
int ret;
|
||||
|
||||
@@ -2716,8 +2695,8 @@ static void scrub_parity_check_and_repair(struct scrub_parity *sparity)
|
||||
length = sparity->logic_end - sparity->logic_start + 1;
|
||||
ret = btrfs_map_sblock(sctx->dev_root->fs_info, WRITE,
|
||||
sparity->logic_start,
|
||||
&length, &bbio, 0, &raid_map);
|
||||
if (ret || !bbio || !raid_map)
|
||||
&length, &bbio, 0, 1);
|
||||
if (ret || !bbio || !bbio->raid_map)
|
||||
goto bbio_out;
|
||||
|
||||
bio = btrfs_io_bio_alloc(GFP_NOFS, 0);
|
||||
@@ -2729,8 +2708,7 @@ static void scrub_parity_check_and_repair(struct scrub_parity *sparity)
|
||||
bio->bi_end_io = scrub_parity_bio_endio;
|
||||
|
||||
rbio = raid56_parity_alloc_scrub_rbio(sctx->dev_root, bio, bbio,
|
||||
raid_map, length,
|
||||
sparity->scrub_dev,
|
||||
length, sparity->scrub_dev,
|
||||
sparity->dbitmap,
|
||||
sparity->nsectors);
|
||||
if (!rbio)
|
||||
@@ -2747,8 +2725,7 @@ static void scrub_parity_check_and_repair(struct scrub_parity *sparity)
|
||||
rbio_out:
|
||||
bio_put(bio);
|
||||
bbio_out:
|
||||
kfree(bbio);
|
||||
kfree(raid_map);
|
||||
btrfs_put_bbio(bbio);
|
||||
bitmap_or(sparity->ebitmap, sparity->ebitmap, sparity->dbitmap,
|
||||
sparity->nsectors);
|
||||
spin_lock(&sctx->stat_lock);
|
||||
@@ -2765,12 +2742,12 @@ static inline int scrub_calc_parity_bitmap_len(int nsectors)
|
||||
|
||||
static void scrub_parity_get(struct scrub_parity *sparity)
|
||||
{
|
||||
atomic_inc(&sparity->ref_count);
|
||||
atomic_inc(&sparity->refs);
|
||||
}
|
||||
|
||||
static void scrub_parity_put(struct scrub_parity *sparity)
|
||||
{
|
||||
if (!atomic_dec_and_test(&sparity->ref_count))
|
||||
if (!atomic_dec_and_test(&sparity->refs))
|
||||
return;
|
||||
|
||||
scrub_parity_check_and_repair(sparity);
|
||||
@@ -2820,7 +2797,7 @@ static noinline_for_stack int scrub_raid56_parity(struct scrub_ctx *sctx,
|
||||
sparity->scrub_dev = sdev;
|
||||
sparity->logic_start = logic_start;
|
||||
sparity->logic_end = logic_end;
|
||||
atomic_set(&sparity->ref_count, 1);
|
||||
atomic_set(&sparity->refs, 1);
|
||||
INIT_LIST_HEAD(&sparity->spages);
|
||||
sparity->dbitmap = sparity->bitmap;
|
||||
sparity->ebitmap = (void *)sparity->bitmap + bitmap_len;
|
||||
@@ -3037,8 +3014,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
|
||||
} else if (map->type & BTRFS_BLOCK_GROUP_DUP) {
|
||||
increment = map->stripe_len;
|
||||
mirror_num = num % map->num_stripes + 1;
|
||||
} else if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
|
||||
BTRFS_BLOCK_GROUP_RAID6)) {
|
||||
} else if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
|
||||
get_raid56_logic_offset(physical, num, map, &offset, NULL);
|
||||
increment = map->stripe_len * nr_data_stripes(map);
|
||||
mirror_num = 1;
|
||||
@@ -3074,8 +3050,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
|
||||
*/
|
||||
logical = base + offset;
|
||||
physical_end = physical + nstripes * map->stripe_len;
|
||||
if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
|
||||
BTRFS_BLOCK_GROUP_RAID6)) {
|
||||
if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
|
||||
get_raid56_logic_offset(physical_end, num,
|
||||
map, &logic_end, NULL);
|
||||
logic_end += base;
|
||||
@@ -3121,8 +3096,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
|
||||
ret = 0;
|
||||
while (physical < physical_end) {
|
||||
/* for raid56, we skip parity stripe */
|
||||
if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
|
||||
BTRFS_BLOCK_GROUP_RAID6)) {
|
||||
if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
|
||||
ret = get_raid56_logic_offset(physical, num,
|
||||
map, &logical, &stripe_logical);
|
||||
logical += base;
|
||||
@@ -3280,8 +3254,7 @@ again:
|
||||
scrub_free_csums(sctx);
|
||||
if (extent_logical + extent_len <
|
||||
key.objectid + bytes) {
|
||||
if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
|
||||
BTRFS_BLOCK_GROUP_RAID6)) {
|
||||
if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
|
||||
/*
|
||||
* loop until we find next data stripe
|
||||
* or we have finished all stripes.
|
||||
@@ -3775,7 +3748,7 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
|
||||
scrub_workers_put(fs_info);
|
||||
mutex_unlock(&fs_info->scrub_lock);
|
||||
|
||||
scrub_free_ctx(sctx);
|
||||
scrub_put_ctx(sctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -3881,14 +3854,14 @@ static void scrub_remap_extent(struct btrfs_fs_info *fs_info,
|
||||
&mapped_length, &bbio, 0);
|
||||
if (ret || !bbio || mapped_length < extent_len ||
|
||||
!bbio->stripes[0].dev->bdev) {
|
||||
kfree(bbio);
|
||||
btrfs_put_bbio(bbio);
|
||||
return;
|
||||
}
|
||||
|
||||
*extent_physical = bbio->stripes[0].physical;
|
||||
*extent_mirror_num = bbio->mirror_num;
|
||||
*extent_dev = bbio->stripes[0].dev;
|
||||
kfree(bbio);
|
||||
btrfs_put_bbio(bbio);
|
||||
}
|
||||
|
||||
static int scrub_setup_wr_ctx(struct scrub_ctx *sctx,
|
||||
|
Reference in New Issue
Block a user