Skip to content

Commit ee37d73

Browse files
Alex Wushligit
authored andcommitted
md/raid10: Fix raid10 replace hang when new added disk faulty
[Symptom] Resync thread hang when new added disk faulty during replacing. [Root Cause] In raid10_sync_request(), we expect to issue a bio with callback end_sync_read(), and a bio with callback end_sync_write(). In normal situation, we will add resyncing sectors into mddev->recovery_active when raid10_sync_request() returned, and sub resynced sectors from mddev->recovery_active when end_sync_write() calls end_sync_request(). If new added disk, which are replacing the old disk, is set faulty, there is a race condition: 1. In the first rcu protected section, resync thread did not detect that mreplace is set faulty and pass the condition. 2. In the second rcu protected section, mreplace is set faulty. 3. But, resync thread will prepare the read object first, and then check the write condition. 4. It will find that mreplace is set faulty and do not have to prepare write object. This cause we add resync sectors but never sub it. [How to Reproduce] This issue can be easily reproduced by the following steps: mdadm -C /dev/md0 --assume-clean -l 10 -n 4 /dev/sd[abcd] mdadm /dev/md0 -a /dev/sde mdadm /dev/md0 --replace /dev/sdd sleep 1 mdadm /dev/md0 -f /dev/sde [How to Fix] This issue can be fixed by using local variables to record the result of test conditions. Once the conditions are satisfied, we can make sure that we need to issue a bio for read and a bio for write. Previous 'commit 24afd80 ("md/raid10: handle recovery of replacement devices.")' will also check whether bio is NULL, but leave the comment saying that it is a pointless test. So we remove this dummy check. Reported-by: Alex Chen <[email protected]> Reviewed-by: Allen Peng <[email protected]> Reviewed-by: BingJing Chang <[email protected]> Signed-off-by: Alex Wu <[email protected]> Signed-off-by: Shaohua Li <[email protected]>
1 parent fb73b35 commit ee37d73

File tree

1 file changed

+14
-13
lines changed

1 file changed

+14
-13
lines changed

drivers/md/raid10.c

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3079,18 +3079,24 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
30793079
sector_t sect;
30803080
int must_sync;
30813081
int any_working;
3082+
int need_recover = 0;
3083+
int need_replace = 0;
30823084
struct raid10_info *mirror = &conf->mirrors[i];
30833085
struct md_rdev *mrdev, *mreplace;
30843086

30853087
rcu_read_lock();
30863088
mrdev = rcu_dereference(mirror->rdev);
30873089
mreplace = rcu_dereference(mirror->replacement);
30883090

3089-
if ((mrdev == NULL ||
3090-
test_bit(Faulty, &mrdev->flags) ||
3091-
test_bit(In_sync, &mrdev->flags)) &&
3092-
(mreplace == NULL ||
3093-
test_bit(Faulty, &mreplace->flags))) {
3091+
if (mrdev != NULL &&
3092+
!test_bit(Faulty, &mrdev->flags) &&
3093+
!test_bit(In_sync, &mrdev->flags))
3094+
need_recover = 1;
3095+
if (mreplace != NULL &&
3096+
!test_bit(Faulty, &mreplace->flags))
3097+
need_replace = 1;
3098+
3099+
if (!need_recover && !need_replace) {
30943100
rcu_read_unlock();
30953101
continue;
30963102
}
@@ -3213,7 +3219,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
32133219
r10_bio->devs[1].devnum = i;
32143220
r10_bio->devs[1].addr = to_addr;
32153221

3216-
if (!test_bit(In_sync, &mrdev->flags)) {
3222+
if (need_recover) {
32173223
bio = r10_bio->devs[1].bio;
32183224
bio->bi_next = biolist;
32193225
biolist = bio;
@@ -3230,16 +3236,11 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
32303236
bio = r10_bio->devs[1].repl_bio;
32313237
if (bio)
32323238
bio->bi_end_io = NULL;
3233-
/* Note: if mreplace != NULL, then bio
3239+
/* Note: if need_replace, then bio
32343240
* cannot be NULL as r10buf_pool_alloc will
32353241
* have allocated it.
3236-
* So the second test here is pointless.
3237-
* But it keeps semantic-checkers happy, and
3238-
* this comment keeps human reviewers
3239-
* happy.
32403242
*/
3241-
if (mreplace == NULL || bio == NULL ||
3242-
test_bit(Faulty, &mreplace->flags))
3243+
if (!need_replace)
32433244
break;
32443245
bio->bi_next = biolist;
32453246
biolist = bio;

0 commit comments

Comments
 (0)