mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
md/raid5: fix a race condition in stripe batch
commit 3664847d95 upstream.
We have a race condition in below scenario, say have 3 continuous stripes, sh1,
sh2 and sh3, sh1 is the stripe_head of sh2 and sh3:
CPU1 CPU2 CPU3
handle_stripe(sh3)
stripe_add_to_batch_list(sh3)
-> lock(sh2, sh3)
-> lock batch_lock(sh1)
-> add sh3 to batch_list of sh1
-> unlock batch_lock(sh1)
clear_batch_ready(sh1)
-> lock(sh1) and batch_lock(sh1)
-> clear STRIPE_BATCH_READY for all stripes in batch_list
-> unlock(sh1) and batch_lock(sh1)
->clear_batch_ready(sh3)
-->test_and_clear_bit(STRIPE_BATCH_READY, sh3)
--->return 0 as sh->batch == NULL
-> sh3->batch_head = sh1
-> unlock (sh2, sh3)
In CPU1, handle_stripe will continue handle sh3 even it's in batch stripe list
of sh1. By moving sh3->batch_head assignment in to batch_lock, we make it
impossible to clear STRIPE_BATCH_READY before batch_head is set.
Thanks Stephane for helping debug this tricky issue.
Reported-and-tested-by: Stephane Thiell <sthiell@stanford.edu>
Signed-off-by: Shaohua Li <shli@fb.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
5fb4be27da
commit
648798cc2f
@@ -829,6 +829,14 @@ static void stripe_add_to_batch_list(struct r5conf *conf, struct stripe_head *sh
|
||||
spin_unlock(&head->batch_head->batch_lock);
|
||||
goto unlock_out;
|
||||
}
|
||||
/*
|
||||
* We must assign batch_head of this stripe within the
|
||||
* batch_lock, otherwise clear_batch_ready of batch head
|
||||
* stripe could clear BATCH_READY bit of this stripe and
|
||||
* this stripe->batch_head doesn't get assigned, which
|
||||
* could confuse clear_batch_ready for this stripe
|
||||
*/
|
||||
sh->batch_head = head->batch_head;
|
||||
|
||||
/*
|
||||
* at this point, head's BATCH_READY could be cleared, but we
|
||||
@@ -836,8 +844,6 @@ static void stripe_add_to_batch_list(struct r5conf *conf, struct stripe_head *sh
|
||||
*/
|
||||
list_add(&sh->batch_list, &head->batch_list);
|
||||
spin_unlock(&head->batch_head->batch_lock);
|
||||
|
||||
sh->batch_head = head->batch_head;
|
||||
} else {
|
||||
head->batch_head = head;
|
||||
sh->batch_head = head->batch_head;
|
||||
|
||||
Reference in New Issue
Block a user