md/raid5: handle possible race as reshape completes.
authorNeilBrown <neilb@suse.com>
Fri, 24 Jul 2015 03:30:32 +0000 (13:30 +1000)
committerNeilBrown <neilb@suse.com>
Mon, 31 Aug 2015 17:38:59 +0000 (19:38 +0200)
It is possible (though unlikely) for a reshape to be
interrupted between the time that end_reshape is called
and the time when raid5_finish_reshape is called.

This can leave conf->reshape_progress set to MaxSector,
but mddev->reshape_position not.

This combination confused reshape_request() when ->reshape_backwards.
As conf->reshape_progress is so high, it seems the reshape hasn't
really begun.  But assuming MaxSector is a valid address only
leads to sorrow.

So ensure reshape_position and reshape_progress both agree,
and add an extra check in reshape_request() just in case they don't.

Signed-off-by: NeilBrown <neilb@suse.com>
drivers/md/raid5.c

index 1c27aa10f89cc07c296f544e892f7841d89ee43e..7b0c706f33e743f721ac99a2855a70272965b3cb 100644 (file)
@@ -5355,6 +5355,10 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *sk
                    conf->reshape_progress < raid5_size(mddev, 0, 0)) {
                        sector_nr = raid5_size(mddev, 0, 0)
                                - conf->reshape_progress;
+               } else if (mddev->reshape_backwards &&
+                          conf->reshape_progress == MaxSector) {
+                       /* shouldn't happen, but just in case, finish up.*/
+                       sector_nr = MaxSector;
                } else if (!mddev->reshape_backwards &&
                           conf->reshape_progress > 0)
                        sector_nr = conf->reshape_progress;
@@ -7446,6 +7450,7 @@ static void end_reshape(struct r5conf *conf)
                        rdev->data_offset = rdev->new_data_offset;
                smp_wmb();
                conf->reshape_progress = MaxSector;
+               conf->mddev->reshape_position = MaxSector;
                spin_unlock_irq(&conf->device_lock);
                wake_up(&conf->wait_for_overlap);