Pull MD fix from Song: "This patch fixes data corruption caused by discard on raid0 array with original layout." * tag 'md-fixes-20230630' of https://git.kernel.org/pub/scm/linux/kernel/git/song/md: md/raid0: add discard support for the 'original' layout
Unified · Split
| @@ -270,6 +270,18 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) · 269 unmodified lines | ||
| 270 | 270 | goto abort; |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | + if (conf->layout == RAID0_ORIG_LAYOUT) { | |
| 274 | + for (i = 1; i < conf->nr_strip_zones; i++) { | |
| 275 | + sector_t first_sector = conf->strip_zone[i-1].zone_end; | |
| 276 | + | |
| 277 | + sector_div(first_sector, mddev->chunk_sectors); | |
| 278 | + zone = conf->strip_zone + i; | |
| 279 | + /* disk_shift is first disk index used in the zone */ | |
| 280 | + zone->disk_shift = sector_div(first_sector, | |
| 281 | + zone->nb_dev); | |
| 282 | + } | |
| 283 | + } | |
| 284 | + | |
| 273 | 285 | pr_debug("md/raid0:%s: done.\n", mdname(mddev)); |
| 274 | 286 | *private_conf = conf; |
| 275 | 287 | |
| @@ -431,6 +443,20 @@ exit_acct_set: · 155 unmodified lines | ||
| 431 | 443 | return ret; |
| 432 | 444 | } |
| 433 | 445 | |
| 446 | +/* | |
| 447 | + * Convert disk_index to the disk order in which it is read/written. | |
| 448 | + * For example, if we have 4 disks, they are numbered 0,1,2,3. If we | |
| 449 | + * write the disks starting at disk 3, then the read/write order would | |
| 450 | + * be disk 3, then 0, then 1, and then disk 2 and we want map_disk_shift() | |
| 451 | + * to map the disks as follows 0,1,2,3 => 1,2,3,0. So disk 0 would map | |
| 452 | + * to 1, 1 to 2, 2 to 3, and 3 to 0. That way we can compare disks in | |
| 453 | + * that 'output' space to understand the read/write disk ordering. | |
| 454 | + */ | |
| 455 | +static int map_disk_shift(int disk_index, int num_disks, int disk_shift) | |
| 456 | +{ | |
| 457 | + return ((disk_index + num_disks - disk_shift) % num_disks); | |
| 458 | +} | |
| 459 | + | |
| 434 | 460 | static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) |
| 435 | 461 | { |
| 436 | 462 | struct r0conf *conf = mddev->private; |
| @@ -444,7 +470,9 @@ static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) · 7 unmodified lines | ||
| 444 | 470 | sector_t end_disk_offset; |
| 445 | 471 | unsigned int end_disk_index; |
| 446 | 472 | unsigned int disk; |
| 473 | + sector_t orig_start, orig_end; | |
| 447 | 474 | |
| 475 | + orig_start = start; | |
| 448 | 476 | zone = find_zone(conf, &start); |
| 449 | 477 | |
| 450 | 478 | if (bio_end_sector(bio) > zone->zone_end) { |
| @@ -458,6 +486,7 @@ static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) · 7 unmodified lines | ||
| 458 | 486 | } else |
| 459 | 487 | end = bio_end_sector(bio); |
| 460 | 488 | |
| 489 | + orig_end = end; | |
| 461 | 490 | if (zone != conf->strip_zone) |
| 462 | 491 | end = end - zone[-1].zone_end; |
| 463 | 492 | |
| @@ -469,13 +498,26 @@ static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) · 5 unmodified lines | ||
| 469 | 498 | last_stripe_index = end; |
| 470 | 499 | sector_div(last_stripe_index, stripe_size); |
| 471 | 500 | |
| 472 | − start_disk_index = (int)(start - first_stripe_index * stripe_size) / | |
| 473 | − mddev->chunk_sectors; | |
| 501 | + /* In the first zone the original and alternate layouts are the same */ | |
| 502 | + if ((conf->layout == RAID0_ORIG_LAYOUT) && (zone != conf->strip_zone)) { | |
| 503 | + sector_div(orig_start, mddev->chunk_sectors); | |
| 504 | + start_disk_index = sector_div(orig_start, zone->nb_dev); | |
| 505 | + start_disk_index = map_disk_shift(start_disk_index, | |
| 506 | + zone->nb_dev, | |
| 507 | + zone->disk_shift); | |
| 508 | + sector_div(orig_end, mddev->chunk_sectors); | |
| 509 | + end_disk_index = sector_div(orig_end, zone->nb_dev); | |
| 510 | + end_disk_index = map_disk_shift(end_disk_index, | |
| 511 | + zone->nb_dev, zone->disk_shift); | |
| 512 | + } else { | |
| 513 | + start_disk_index = (int)(start - first_stripe_index * stripe_size) / | |
| 514 | + mddev->chunk_sectors; | |
| 515 | + end_disk_index = (int)(end - last_stripe_index * stripe_size) / | |
| 516 | + mddev->chunk_sectors; | |
| 517 | + } | |
| 474 | 518 | start_disk_offset = ((int)(start - first_stripe_index * stripe_size) % |
| 475 | 519 | mddev->chunk_sectors) + |
| 476 | 520 | first_stripe_index * mddev->chunk_sectors; |
| 477 | − end_disk_index = (int)(end - last_stripe_index * stripe_size) / | |
| 478 | − mddev->chunk_sectors; | |
| 479 | 521 | end_disk_offset = ((int)(end - last_stripe_index * stripe_size) % |
| 480 | 522 | mddev->chunk_sectors) + |
| 481 | 523 | last_stripe_index * mddev->chunk_sectors; |
| @@ -483,18 +525,22 @@ static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) · 1 unmodified line | ||
| 483 | 525 | for (disk = 0; disk < zone->nb_dev; disk++) { |
| 484 | 526 | sector_t dev_start, dev_end; |
| 485 | 527 | struct md_rdev *rdev; |
| 528 | + int compare_disk; | |
| 529 | + | |
| 530 | + compare_disk = map_disk_shift(disk, zone->nb_dev, | |
| 531 | + zone->disk_shift); | |
| 486 | 532 | |
| 487 | − if (disk < start_disk_index) | |
| 533 | + if (compare_disk < start_disk_index) | |
| 488 | 534 | dev_start = (first_stripe_index + 1) * |
| 489 | 535 | mddev->chunk_sectors; |
| 490 | − else if (disk > start_disk_index) | |
| 536 | + else if (compare_disk > start_disk_index) | |
| 491 | 537 | dev_start = first_stripe_index * mddev->chunk_sectors; |
| 492 | 538 | else |
| 493 | 539 | dev_start = start_disk_offset; |
| 494 | 540 | |
| 495 | − if (disk < end_disk_index) | |
| 541 | + if (compare_disk < end_disk_index) | |
| 496 | 542 | dev_end = (last_stripe_index + 1) * mddev->chunk_sectors; |
| 497 | − else if (disk > end_disk_index) | |
| 543 | + else if (compare_disk > end_disk_index) | |
| 498 | 544 | dev_end = last_stripe_index * mddev->chunk_sectors; |
| 499 | 545 | else |
| 500 | 546 | dev_end = end_disk_offset; |
| @@ -6,6 +6,7 @@ struct strip_zone { · 5 unmodified lines | ||
| 6 | 6 | sector_t zone_end; /* Start of the next zone (in sectors) */ |
| 7 | 7 | sector_t dev_start; /* Zone offset in real dev (in sectors) */ |
| 8 | 8 | int nb_dev; /* # of devices attached to the zone */ |
| 9 | + int disk_shift; /* start disk for the original layout */ | |
| 9 | 10 | }; |
| 10 | 11 | |
| 11 | 12 | /* Linux 3.14 (20d0189b101) made an unintended change to |