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 | goto abort; | 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 | pr_debug("md/raid0:%s: done.\n", mdname(mddev)); | 285 | pr_debug("md/raid0:%s: done.\n", mdname(mddev)); |
| 274 | *private_conf = conf; | 286 | *private_conf = conf; |
| 275 | 287 | ||
| @@ -431,6 +443,20 @@ exit_acct_set: · 155 unmodified lines | |||
| 431 | return ret; | 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 | static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) | 460 | static void raid0_handle_discard(struct mddev *mddev, struct bio *bio) |
| 435 | { | 461 | { |
| 436 | struct r0conf *conf = mddev->private; | 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 | sector_t end_disk_offset; | 470 | sector_t end_disk_offset; |
| 445 | unsigned int end_disk_index; | 471 | unsigned int end_disk_index; |
| 446 | unsigned int disk; | 472 | unsigned int disk; |
| 473 | sector_t orig_start, orig_end; | ||
| 447 | 474 | ||
| 475 | orig_start = start; | ||
| 448 | zone = find_zone(conf, &start); | 476 | zone = find_zone(conf, &start); |
| 449 | 477 | ||
| 450 | if (bio_end_sector(bio) > zone->zone_end) { | 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 | } else | 486 | } else |
| 459 | end = bio_end_sector(bio); | 487 | end = bio_end_sector(bio); |
| 460 | 488 | ||
| 489 | orig_end = end; | ||
| 461 | if (zone != conf->strip_zone) | 490 | if (zone != conf->strip_zone) |
| 462 | end = end - zone[-1].zone_end; | 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 | last_stripe_index = end; | 498 | last_stripe_index = end; |
| 470 | sector_div(last_stripe_index, stripe_size); | 499 | sector_div(last_stripe_index, stripe_size); |
| 471 | 500 | ||
| 472 | start_disk_index = (int)(start - first_stripe_index * stripe_size) / | 501 | /* In the first zone the original and alternate layouts are the same */ |
| 473 | mddev->chunk_sectors; | 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 | start_disk_offset = ((int)(start - first_stripe_index * stripe_size) % | 518 | start_disk_offset = ((int)(start - first_stripe_index * stripe_size) % |
| 475 | mddev->chunk_sectors) + | 519 | mddev->chunk_sectors) + |
| 476 | first_stripe_index * mddev->chunk_sectors; | 520 | first_stripe_index * mddev->chunk_sectors; |
| 477 | end_disk_index = (int)(end - last_stripe_index * stripe_size) / | ||
| 478 | mddev->chunk_sectors; | ||
| 479 | end_disk_offset = ((int)(end - last_stripe_index * stripe_size) % | 521 | end_disk_offset = ((int)(end - last_stripe_index * stripe_size) % |
| 480 | mddev->chunk_sectors) + | 522 | mddev->chunk_sectors) + |
| 481 | last_stripe_index * mddev->chunk_sectors; | 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 | for (disk = 0; disk < zone->nb_dev; disk++) { | 525 | for (disk = 0; disk < zone->nb_dev; disk++) { |
| 484 | sector_t dev_start, dev_end; | 526 | sector_t dev_start, dev_end; |
| 485 | struct md_rdev *rdev; | 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 | dev_start = (first_stripe_index + 1) * | 534 | dev_start = (first_stripe_index + 1) * |
| 489 | mddev->chunk_sectors; | 535 | mddev->chunk_sectors; |
| 490 | else if (disk > start_disk_index) | 536 | else if (compare_disk > start_disk_index) |
| 491 | dev_start = first_stripe_index * mddev->chunk_sectors; | 537 | dev_start = first_stripe_index * mddev->chunk_sectors; |
| 492 | else | 538 | else |
| 493 | dev_start = start_disk_offset; | 539 | dev_start = start_disk_offset; |
| 494 | 540 | ||
| 495 | if (disk < end_disk_index) | 541 | if (compare_disk < end_disk_index) |
| 496 | dev_end = (last_stripe_index + 1) * mddev->chunk_sectors; | 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 | dev_end = last_stripe_index * mddev->chunk_sectors; | 544 | dev_end = last_stripe_index * mddev->chunk_sectors; |
| 499 | else | 545 | else |
| 500 | dev_end = end_disk_offset; | 546 | dev_end = end_disk_offset; |
| @@ -6,6 +6,7 @@ struct strip_zone { · 5 unmodified lines | |||
| 6 | sector_t zone_end; /* Start of the next zone (in sectors) */ | 6 | sector_t zone_end; /* Start of the next zone (in sectors) */ |
| 7 | sector_t dev_start; /* Zone offset in real dev (in sectors) */ | 7 | sector_t dev_start; /* Zone offset in real dev (in sectors) */ |
| 8 | int nb_dev; /* # of devices attached to the zone */ | 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 | /* Linux 3.14 (20d0189b101) made an unintended change to | 12 | /* Linux 3.14 (20d0189b101) made an unintended change to |