@@ -2363,7 +2363,14 @@ struct btrfs_raid_bio *raid56_parity_alloc_scrub_rbio(struct bio *bio,
break;
}
}
- ASSERT(i < rbio->real_stripes);
+ /*
+ * If @scrub_dev is specified, @scrubp must be the stripe number of
+ * P or Q stripe.
+ */
+ if (scrub_dev) {
+ ASSERT(rbio->scrubp >= rbio->nr_data);
+ ASSERT(rbio->scrubp < rbio->real_stripes);
+ }
bitmap_copy(&rbio->dbitmap, dbitmap, rbio->stripe_nsectors);
return rbio;
@@ -2426,7 +2433,8 @@ static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
* Replace is running and our P/Q stripe is being replaced, then we
* need to duplicate the final write to replace target.
*/
- if (bioc->replace_nr_stripes && bioc->replace_stripe_src == rbio->scrubp) {
+ if (rbio->scrubp && bioc->replace_nr_stripes &&
+ bioc->replace_stripe_src == rbio->scrubp) {
is_replace = 1;
bitmap_copy(pbitmap, &rbio->dbitmap, rbio->stripe_nsectors);
}
@@ -2464,6 +2472,7 @@ static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
for_each_set_bit(sectornr, &rbio->dbitmap, rbio->stripe_nsectors) {
struct sector_ptr *sector;
+ bool found_error = false;
void *parity;
/* first collect one page from each data stripe */
@@ -2484,14 +2493,22 @@ static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
}
/* Check scrubbing parity and repair it */
- sector = rbio_stripe_sector(rbio, rbio->scrubp, sectornr);
- parity = kmap_local_page(sector->page) + sector->pgoff;
- if (memcmp(parity, pointers[rbio->scrubp], sectorsize) != 0)
- memcpy(parity, pointers[rbio->scrubp], sectorsize);
- else
+ for (int i = rbio->nr_data; i < rbio->real_stripes; i++) {
+ /* Skip this stripe if it's not our scrub target. */
+ if (rbio->scrubp && i != rbio->scrubp)
+ continue;
+
+ sector = rbio_stripe_sector(rbio, i, sectornr);
+ parity = kmap_local_page(sector->page) + sector->pgoff;
+ if (memcmp(parity, pointers[i], sectorsize) != 0) {
+ memcpy(parity, pointers[i], sectorsize);
+ found_error = true;
+ }
+ kunmap_local(parity);
+ }
+ if (!found_error)
/* Parity is right, needn't writeback */
bitmap_clear(&rbio->dbitmap, sectornr, 1);
- kunmap_local(parity);
for (stripe = nr_data - 1; stripe >= 0; stripe--)
kunmap_local(pointers[stripe]);
@@ -2514,11 +2531,16 @@ static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
for_each_set_bit(sectornr, &rbio->dbitmap, rbio->stripe_nsectors) {
struct sector_ptr *sector;
- sector = rbio_stripe_sector(rbio, rbio->scrubp, sectornr);
- ret = rbio_add_io_sector(rbio, &bio_list, sector, rbio->scrubp,
- sectornr, REQ_OP_WRITE);
- if (ret)
- goto cleanup;
+ for (int i = rbio->nr_data; i < rbio->real_stripes; i++) {
+ /* Skip this stripe if it's not our scrub target. */
+ if (rbio->scrubp && i != rbio->scrubp)
+ continue;
+ sector = rbio_stripe_sector(rbio, i, sectornr);
+ ret = rbio_add_io_sector(rbio, &bio_list, sector, i,
+ sectornr, REQ_OP_WRITE);
+ if (ret)
+ goto cleanup;
+ }
}
if (!is_replace)
@@ -2529,6 +2551,12 @@ static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
* the target device. Check we have a valid source stripe number.
*/
ASSERT(rbio->bioc->replace_stripe_src >= 0);
+ /*
+ * For dev-replace case, scrubp must be provided by the caller at
+ * raid56_parity_alloc_scrub_rbio().
+ */
+ ASSERT(rbio->scrubp >= rbio->nr_data);
+ ASSERT(rbio->scrubp < rbio->real_stripes);
for_each_set_bit(sectornr, pbitmap, rbio->stripe_nsectors) {
struct sector_ptr *sector;
@@ -2625,7 +2653,7 @@ static int recover_scrub_rbio(struct btrfs_raid_bio *rbio)
* scrubbing parity, luckily, use the other one to repair the
* data, or we can not repair the data stripe.
*/
- if (failp != rbio->scrubp) {
+ if (rbio->scrubp && failp != rbio->scrubp) {
ret = -EIO;
goto out;
}
Current the scrub_rbio() workload (used by raid56_parity_alloc_scrub_rbio() then raid56_parity_submit_scrub_rbio()) can only handle one P/Q stripe workload. But inside finish_parity_scrub() we always do the P/Q generation and verification for both P/Q stripes. For the incoming scrub_logical feature, we want to verify P/Q stripes in one go, so here we introduce the ability to scrub both P/Q stripes. To do that the following things are modified: - raid56_parity_alloc_scrub_rbio() to allows empty @scrub_dev Thankfully we don't have any extra sanity checks to reject such empty parameter. - Verify both P and Q stripes if @rbio->scrubp is 0 Now we do the verification in a loop, and skip the stripe if rbio->scrubp is not zero and does not match the stripe number. - Add bad sectors of both P/Q stripes to writeback Unfortunately we're using the same dbitmap, this means if only one of the P/Q stripes is corrupted, we would write both. However we can accept the slightly extra cost for it. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/raid56.c | 56 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 14 deletions(-)