Skip to content

Commit

Permalink
md/raid1: fix bio handling problems in process_checks()
Browse files Browse the repository at this point in the history
Recent change to use bio_copy_data() in raid1 when repairing
an array is faulty.

The underlying may have changed the bio in various ways using
bio_advance and these need to be undone not just for the 'sbio' which
is being copied to, but also the 'pbio' (primary) which is being
copied from.

So perform the reset on all bios that were read from and do it early.

This also ensure that the sbio->bi_io_vec[j].bv_len passed to
memcmp is correct.

This fixes a crash during a 'check' of a RAID1 array.  The crash was
introduced in 3.10 so this is suitable for 3.10-stable.

Cc: stable@vger.kernel.org (3.10)
Reported-by: Joe Lawrence <joe.lawrence@stratus.com>
Signed-off-by: NeilBrown <neilb@suse.de>
  • Loading branch information
NeilBrown committed Jul 18, 2013
1 parent 5024c29 commit 30bc9b5
Showing 1 changed file with 30 additions and 23 deletions.
53 changes: 30 additions & 23 deletions drivers/md/raid1.c
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,36 @@ static int process_checks(struct r1bio *r1_bio)
int i;
int vcnt;

/* Fix variable parts of all bios */
vcnt = (r1_bio->sectors + PAGE_SIZE / 512 - 1) >> (PAGE_SHIFT - 9);
for (i = 0; i < conf->raid_disks * 2; i++) {
int j;
int size;
struct bio *b = r1_bio->bios[i];
if (b->bi_end_io != end_sync_read)
continue;
/* fixup the bio for reuse */
bio_reset(b);
b->bi_vcnt = vcnt;
b->bi_size = r1_bio->sectors << 9;
b->bi_sector = r1_bio->sector +
conf->mirrors[i].rdev->data_offset;
b->bi_bdev = conf->mirrors[i].rdev->bdev;
b->bi_end_io = end_sync_read;
b->bi_private = r1_bio;

size = b->bi_size;
for (j = 0; j < vcnt ; j++) {
struct bio_vec *bi;
bi = &b->bi_io_vec[j];
bi->bv_offset = 0;
if (size > PAGE_SIZE)
bi->bv_len = PAGE_SIZE;
else
bi->bv_len = size;
size -= PAGE_SIZE;
}
}
for (primary = 0; primary < conf->raid_disks * 2; primary++)
if (r1_bio->bios[primary]->bi_end_io == end_sync_read &&
test_bit(BIO_UPTODATE, &r1_bio->bios[primary]->bi_flags)) {
Expand All @@ -1857,12 +1887,10 @@ static int process_checks(struct r1bio *r1_bio)
break;
}
r1_bio->read_disk = primary;
vcnt = (r1_bio->sectors + PAGE_SIZE / 512 - 1) >> (PAGE_SHIFT - 9);
for (i = 0; i < conf->raid_disks * 2; i++) {
int j;
struct bio *pbio = r1_bio->bios[primary];
struct bio *sbio = r1_bio->bios[i];
int size;

if (sbio->bi_end_io != end_sync_read)
continue;
Expand All @@ -1888,27 +1916,6 @@ static int process_checks(struct r1bio *r1_bio)
rdev_dec_pending(conf->mirrors[i].rdev, mddev);
continue;
}
/* fixup the bio for reuse */
bio_reset(sbio);
sbio->bi_vcnt = vcnt;
sbio->bi_size = r1_bio->sectors << 9;
sbio->bi_sector = r1_bio->sector +
conf->mirrors[i].rdev->data_offset;
sbio->bi_bdev = conf->mirrors[i].rdev->bdev;
sbio->bi_end_io = end_sync_read;
sbio->bi_private = r1_bio;

size = sbio->bi_size;
for (j = 0; j < vcnt ; j++) {
struct bio_vec *bi;
bi = &sbio->bi_io_vec[j];
bi->bv_offset = 0;
if (size > PAGE_SIZE)
bi->bv_len = PAGE_SIZE;
else
bi->bv_len = size;
size -= PAGE_SIZE;
}

bio_copy_data(sbio, pbio);
}
Expand Down

0 comments on commit 30bc9b5

Please sign in to comment.