Skip to content

Commit 25b3a82

Browse files
bmarzinsliu-song-6
authored andcommitted
md/raid5: recheck if reshape has finished with device_lock held
When handling an IO request, MD checks if a reshape is currently happening, and if so, where the IO sector is in relation to the reshape progress. MD uses conf->reshape_progress for both of these tasks. When the reshape finishes, conf->reshape_progress is set to MaxSector. If this occurs after MD checks if the reshape is currently happening but before it calls ahead_of_reshape(), then ahead_of_reshape() will end up comparing the IO sector against MaxSector. During a backwards reshape, this will make MD think the IO sector is in the area not yet reshaped, causing it to use the previous configuration, and map the IO to the sector where that data was before the reshape. This bug can be triggered by running the lvm2 lvconvert-raid-reshape-linear_to_raid6-single-type.sh test in a loop, although it's very hard to reproduce. Fix this by factoring the code that checks where the IO sector is in relation to the reshape out to a helper called get_reshape_loc(), which reads reshape_progress and reshape_safe while holding the device_lock, and then rechecks if the reshape has finished before calling ahead_of_reshape with the saved values. Also use the helper during the REQ_NOWAIT check to see if the location is inside of the reshape region. Fixes: fef9c61 ("md/raid5: change reshape-progress measurement to cope with reshaping backwards.") Signed-off-by: Benjamin Marzinski <[email protected]> Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent a1fd37f commit 25b3a82

File tree

1 file changed

+41
-23
lines changed

1 file changed

+41
-23
lines changed

drivers/md/raid5.c

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5899,6 +5899,39 @@ static int add_all_stripe_bios(struct r5conf *conf,
58995899
return ret;
59005900
}
59015901

5902+
enum reshape_loc {
5903+
LOC_NO_RESHAPE,
5904+
LOC_AHEAD_OF_RESHAPE,
5905+
LOC_INSIDE_RESHAPE,
5906+
LOC_BEHIND_RESHAPE,
5907+
};
5908+
5909+
static enum reshape_loc get_reshape_loc(struct mddev *mddev,
5910+
struct r5conf *conf, sector_t logical_sector)
5911+
{
5912+
sector_t reshape_progress, reshape_safe;
5913+
/*
5914+
* Spinlock is needed as reshape_progress may be
5915+
* 64bit on a 32bit platform, and so it might be
5916+
* possible to see a half-updated value
5917+
* Of course reshape_progress could change after
5918+
* the lock is dropped, so once we get a reference
5919+
* to the stripe that we think it is, we will have
5920+
* to check again.
5921+
*/
5922+
spin_lock_irq(&conf->device_lock);
5923+
reshape_progress = conf->reshape_progress;
5924+
reshape_safe = conf->reshape_safe;
5925+
spin_unlock_irq(&conf->device_lock);
5926+
if (reshape_progress == MaxSector)
5927+
return LOC_NO_RESHAPE;
5928+
if (ahead_of_reshape(mddev, logical_sector, reshape_progress))
5929+
return LOC_AHEAD_OF_RESHAPE;
5930+
if (ahead_of_reshape(mddev, logical_sector, reshape_safe))
5931+
return LOC_INSIDE_RESHAPE;
5932+
return LOC_BEHIND_RESHAPE;
5933+
}
5934+
59025935
static enum stripe_result make_stripe_request(struct mddev *mddev,
59035936
struct r5conf *conf, struct stripe_request_ctx *ctx,
59045937
sector_t logical_sector, struct bio *bi)
@@ -5913,28 +5946,14 @@ static enum stripe_result make_stripe_request(struct mddev *mddev,
59135946
seq = read_seqcount_begin(&conf->gen_lock);
59145947

59155948
if (unlikely(conf->reshape_progress != MaxSector)) {
5916-
/*
5917-
* Spinlock is needed as reshape_progress may be
5918-
* 64bit on a 32bit platform, and so it might be
5919-
* possible to see a half-updated value
5920-
* Of course reshape_progress could change after
5921-
* the lock is dropped, so once we get a reference
5922-
* to the stripe that we think it is, we will have
5923-
* to check again.
5924-
*/
5925-
spin_lock_irq(&conf->device_lock);
5926-
if (ahead_of_reshape(mddev, logical_sector,
5927-
conf->reshape_progress)) {
5928-
previous = 1;
5929-
} else {
5930-
if (ahead_of_reshape(mddev, logical_sector,
5931-
conf->reshape_safe)) {
5932-
spin_unlock_irq(&conf->device_lock);
5933-
ret = STRIPE_SCHEDULE_AND_RETRY;
5934-
goto out;
5935-
}
5949+
enum reshape_loc loc = get_reshape_loc(mddev, conf,
5950+
logical_sector);
5951+
if (loc == LOC_INSIDE_RESHAPE) {
5952+
ret = STRIPE_SCHEDULE_AND_RETRY;
5953+
goto out;
59365954
}
5937-
spin_unlock_irq(&conf->device_lock);
5955+
if (loc == LOC_AHEAD_OF_RESHAPE)
5956+
previous = 1;
59385957
}
59395958

59405959
new_sector = raid5_compute_sector(conf, logical_sector, previous,
@@ -6112,8 +6131,7 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
61126131
/* Bail out if conflicts with reshape and REQ_NOWAIT is set */
61136132
if ((bi->bi_opf & REQ_NOWAIT) &&
61146133
(conf->reshape_progress != MaxSector) &&
6115-
!ahead_of_reshape(mddev, logical_sector, conf->reshape_progress) &&
6116-
ahead_of_reshape(mddev, logical_sector, conf->reshape_safe)) {
6134+
get_reshape_loc(mddev, conf, logical_sector) == LOC_INSIDE_RESHAPE) {
61176135
bio_wouldblock_error(bi);
61186136
if (rw == WRITE)
61196137
md_write_end(mddev);

0 commit comments

Comments
 (0)