diff options
author | Roman Gushchin <klamm@yandex-team.ru> | 2015-10-30 19:53:50 -0400 |
---|---|---|
committer | NeilBrown <neilb@suse.com> | 2015-10-30 19:53:50 -0400 |
commit | b8a9d66d043ffac116100775a469f05f5158c16f (patch) | |
tree | 919820a8816f109a663b940b7074bb1b6fb58b6a | |
parent | 8bce6d35b308d73cdb2ee273c95d711a55be688c (diff) |
md/raid5: fix locking in handle_stripe_clean_event()
After commit 566c09c53455 ("raid5: relieve lock contention in get_active_stripe()")
__find_stripe() is called under conf->hash_locks + hash.
But handle_stripe_clean_event() calls remove_hash() under
conf->device_lock.
Under some cirscumstances the hash chain can be circuited,
and we get an infinite loop with disabled interrupts and locked hash
lock in __find_stripe(). This leads to hard lockup on multiple CPUs
and following system crash.
I was able to reproduce this behavior on raid6 over 6 ssd disks.
The devices_handle_discard_safely option should be set to enable trim
support. The following script was used:
for i in `seq 1 32`; do
dd if=/dev/zero of=large$i bs=10M count=100 &
done
neilb: original was against a 3.x kernel. I forward-ported
to 4.3-rc. This verison is suitable for any kernel since
Commit: 59fc630b8b5f ("RAID5: batch adjacent full stripe write")
(v4.1+). I'll post a version for earlier kernels to stable.
Signed-off-by: Roman Gushchin <klamm@yandex-team.ru>
Fixes: 566c09c53455 ("raid5: relieve lock contention in get_active_stripe()")
Signed-off-by: NeilBrown <neilb@suse.com>
Cc: Shaohua Li <shli@kernel.org>
Cc: <stable@vger.kernel.org> # 3.13 - 4.2
-rw-r--r-- | drivers/md/raid5.c | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 49bb8d3ff9be..45933c160697 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c | |||
@@ -3499,6 +3499,7 @@ returnbi: | |||
3499 | } | 3499 | } |
3500 | if (!discard_pending && | 3500 | if (!discard_pending && |
3501 | test_bit(R5_Discard, &sh->dev[sh->pd_idx].flags)) { | 3501 | test_bit(R5_Discard, &sh->dev[sh->pd_idx].flags)) { |
3502 | int hash; | ||
3502 | clear_bit(R5_Discard, &sh->dev[sh->pd_idx].flags); | 3503 | clear_bit(R5_Discard, &sh->dev[sh->pd_idx].flags); |
3503 | clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags); | 3504 | clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags); |
3504 | if (sh->qd_idx >= 0) { | 3505 | if (sh->qd_idx >= 0) { |
@@ -3512,16 +3513,17 @@ returnbi: | |||
3512 | * no updated data, so remove it from hash list and the stripe | 3513 | * no updated data, so remove it from hash list and the stripe |
3513 | * will be reinitialized | 3514 | * will be reinitialized |
3514 | */ | 3515 | */ |
3515 | spin_lock_irq(&conf->device_lock); | ||
3516 | unhash: | 3516 | unhash: |
3517 | hash = sh->hash_lock_index; | ||
3518 | spin_lock_irq(conf->hash_locks + hash); | ||
3517 | remove_hash(sh); | 3519 | remove_hash(sh); |
3520 | spin_unlock_irq(conf->hash_locks + hash); | ||
3518 | if (head_sh->batch_head) { | 3521 | if (head_sh->batch_head) { |
3519 | sh = list_first_entry(&sh->batch_list, | 3522 | sh = list_first_entry(&sh->batch_list, |
3520 | struct stripe_head, batch_list); | 3523 | struct stripe_head, batch_list); |
3521 | if (sh != head_sh) | 3524 | if (sh != head_sh) |
3522 | goto unhash; | 3525 | goto unhash; |
3523 | } | 3526 | } |
3524 | spin_unlock_irq(&conf->device_lock); | ||
3525 | sh = head_sh; | 3527 | sh = head_sh; |
3526 | 3528 | ||
3527 | if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state)) | 3529 | if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state)) |