4.13-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Shaohua Li <s...@fb.com>

commit 3664847d95e60a9a943858b7800f8484669740fc 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 <sthi...@stanford.edu>
Signed-off-by: Shaohua Li <s...@fb.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 drivers/md/raid5.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -812,6 +812,14 @@ static void stripe_add_to_batch_list(str
                        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
@@ -819,8 +827,6 @@ static void stripe_add_to_batch_list(str
                 */
                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;


Reply via email to