diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/md/raid5.c | 69 |
1 files changed, 62 insertions, 7 deletions
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 516baf49a1fa..b443cd2459df 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c | |||
@@ -532,13 +532,21 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s) | |||
532 | bi->bi_end_io = raid5_end_read_request; | 532 | bi->bi_end_io = raid5_end_read_request; |
533 | 533 | ||
534 | rcu_read_lock(); | 534 | rcu_read_lock(); |
535 | rdev = rcu_dereference(conf->disks[i].rdev); | ||
536 | rrdev = rcu_dereference(conf->disks[i].replacement); | 535 | rrdev = rcu_dereference(conf->disks[i].replacement); |
536 | smp_mb(); /* Ensure that if rrdev is NULL, rdev won't be */ | ||
537 | rdev = rcu_dereference(conf->disks[i].rdev); | ||
538 | if (!rdev) { | ||
539 | rdev = rrdev; | ||
540 | rrdev = NULL; | ||
541 | } | ||
537 | if (rw & WRITE) { | 542 | if (rw & WRITE) { |
538 | if (replace_only) | 543 | if (replace_only) |
539 | rdev = NULL; | 544 | rdev = NULL; |
545 | if (rdev == rrdev) | ||
546 | /* We raced and saw duplicates */ | ||
547 | rrdev = NULL; | ||
540 | } else { | 548 | } else { |
541 | if (test_bit(R5_ReadRepl, &sh->dev[i].flags)) | 549 | if (test_bit(R5_ReadRepl, &sh->dev[i].flags) && rrdev) |
542 | rdev = rrdev; | 550 | rdev = rrdev; |
543 | rrdev = NULL; | 551 | rrdev = NULL; |
544 | } | 552 | } |
@@ -1640,7 +1648,7 @@ static void raid5_end_read_request(struct bio * bi, int error) | |||
1640 | int disks = sh->disks, i; | 1648 | int disks = sh->disks, i; |
1641 | int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags); | 1649 | int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags); |
1642 | char b[BDEVNAME_SIZE]; | 1650 | char b[BDEVNAME_SIZE]; |
1643 | struct md_rdev *rdev; | 1651 | struct md_rdev *rdev = NULL; |
1644 | 1652 | ||
1645 | 1653 | ||
1646 | for (i=0 ; i<disks; i++) | 1654 | for (i=0 ; i<disks; i++) |
@@ -1655,8 +1663,13 @@ static void raid5_end_read_request(struct bio * bi, int error) | |||
1655 | return; | 1663 | return; |
1656 | } | 1664 | } |
1657 | if (test_bit(R5_ReadRepl, &sh->dev[i].flags)) | 1665 | if (test_bit(R5_ReadRepl, &sh->dev[i].flags)) |
1666 | /* If replacement finished while this request was outstanding, | ||
1667 | * 'replacement' might be NULL already. | ||
1668 | * In that case it moved down to 'rdev'. | ||
1669 | * rdev is not removed until all requests are finished. | ||
1670 | */ | ||
1658 | rdev = conf->disks[i].replacement; | 1671 | rdev = conf->disks[i].replacement; |
1659 | else | 1672 | if (!rdev) |
1660 | rdev = conf->disks[i].rdev; | 1673 | rdev = conf->disks[i].rdev; |
1661 | 1674 | ||
1662 | if (uptodate) { | 1675 | if (uptodate) { |
@@ -1753,7 +1766,14 @@ static void raid5_end_write_request(struct bio *bi, int error) | |||
1753 | } | 1766 | } |
1754 | if (bi == &sh->dev[i].rreq) { | 1767 | if (bi == &sh->dev[i].rreq) { |
1755 | rdev = conf->disks[i].replacement; | 1768 | rdev = conf->disks[i].replacement; |
1756 | replacement = 1; | 1769 | if (rdev) |
1770 | replacement = 1; | ||
1771 | else | ||
1772 | /* rdev was removed and 'replacement' | ||
1773 | * replaced it. rdev is not removed | ||
1774 | * until all requests are finished. | ||
1775 | */ | ||
1776 | rdev = conf->disks[i].rdev; | ||
1757 | break; | 1777 | break; |
1758 | } | 1778 | } |
1759 | } | 1779 | } |
@@ -3539,6 +3559,9 @@ finish: | |||
3539 | } | 3559 | } |
3540 | if (test_and_clear_bit(R5_MadeGoodRepl, &dev->flags)) { | 3560 | if (test_and_clear_bit(R5_MadeGoodRepl, &dev->flags)) { |
3541 | rdev = conf->disks[i].replacement; | 3561 | rdev = conf->disks[i].replacement; |
3562 | if (!rdev) | ||
3563 | /* rdev have been moved down */ | ||
3564 | rdev = conf->disks[i].rdev; | ||
3542 | rdev_clear_badblocks(rdev, sh->sector, | 3565 | rdev_clear_badblocks(rdev, sh->sector, |
3543 | STRIPE_SECTORS); | 3566 | STRIPE_SECTORS); |
3544 | rdev_dec_pending(rdev, conf->mddev); | 3567 | rdev_dec_pending(rdev, conf->mddev); |
@@ -5204,7 +5227,25 @@ static int raid5_spare_active(struct mddev *mddev) | |||
5204 | 5227 | ||
5205 | for (i = 0; i < conf->raid_disks; i++) { | 5228 | for (i = 0; i < conf->raid_disks; i++) { |
5206 | tmp = conf->disks + i; | 5229 | tmp = conf->disks + i; |
5207 | if (tmp->rdev | 5230 | if (tmp->replacement |
5231 | && tmp->replacement->recovery_offset == MaxSector | ||
5232 | && !test_bit(Faulty, &tmp->replacement->flags) | ||
5233 | && !test_and_set_bit(In_sync, &tmp->replacement->flags)) { | ||
5234 | /* Replacement has just become active. */ | ||
5235 | if (!tmp->rdev | ||
5236 | || !test_and_clear_bit(In_sync, &tmp->rdev->flags)) | ||
5237 | count++; | ||
5238 | if (tmp->rdev) { | ||
5239 | /* Replaced device not technically faulty, | ||
5240 | * but we need to be sure it gets removed | ||
5241 | * and never re-added. | ||
5242 | */ | ||
5243 | set_bit(Faulty, &tmp->rdev->flags); | ||
5244 | sysfs_notify_dirent_safe( | ||
5245 | tmp->rdev->sysfs_state); | ||
5246 | } | ||
5247 | sysfs_notify_dirent_safe(tmp->replacement->sysfs_state); | ||
5248 | } else if (tmp->rdev | ||
5208 | && tmp->rdev->recovery_offset == MaxSector | 5249 | && tmp->rdev->recovery_offset == MaxSector |
5209 | && !test_bit(Faulty, &tmp->rdev->flags) | 5250 | && !test_bit(Faulty, &tmp->rdev->flags) |
5210 | && !test_and_set_bit(In_sync, &tmp->rdev->flags)) { | 5251 | && !test_and_set_bit(In_sync, &tmp->rdev->flags)) { |
@@ -5250,6 +5291,7 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev) | |||
5250 | if (!test_bit(Faulty, &rdev->flags) && | 5291 | if (!test_bit(Faulty, &rdev->flags) && |
5251 | mddev->recovery_disabled != conf->recovery_disabled && | 5292 | mddev->recovery_disabled != conf->recovery_disabled && |
5252 | !has_failed(conf) && | 5293 | !has_failed(conf) && |
5294 | (!p->replacement || p->replacement == rdev) && | ||
5253 | number < conf->raid_disks) { | 5295 | number < conf->raid_disks) { |
5254 | err = -EBUSY; | 5296 | err = -EBUSY; |
5255 | goto abort; | 5297 | goto abort; |
@@ -5260,7 +5302,20 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev) | |||
5260 | /* lost the race, try later */ | 5302 | /* lost the race, try later */ |
5261 | err = -EBUSY; | 5303 | err = -EBUSY; |
5262 | *rdevp = rdev; | 5304 | *rdevp = rdev; |
5263 | } | 5305 | } else if (p->replacement) { |
5306 | /* We must have just cleared 'rdev' */ | ||
5307 | p->rdev = p->replacement; | ||
5308 | clear_bit(Replacement, &p->replacement->flags); | ||
5309 | smp_mb(); /* Make sure other CPUs may see both as identical | ||
5310 | * but will never see neither - if they are careful | ||
5311 | */ | ||
5312 | p->replacement = NULL; | ||
5313 | clear_bit(WantReplacement, &rdev->flags); | ||
5314 | } else | ||
5315 | /* We might have just removed the Replacement as faulty- | ||
5316 | * clear the bit just in case | ||
5317 | */ | ||
5318 | clear_bit(WantReplacement, &rdev->flags); | ||
5264 | abort: | 5319 | abort: |
5265 | 5320 | ||
5266 | print_raid5_conf(conf); | 5321 | print_raid5_conf(conf); |