diff options
author | NeilBrown <neilb@suse.de> | 2013-08-27 01:52:13 -0400 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2013-08-28 02:58:36 -0400 |
commit | c46501b2deaa06efcaaf82917281941f02c6b307 (patch) | |
tree | bafe1a096cda8f657074d59cec815b7ae717cd96 /drivers/md | |
parent | b721420e8719131896b009b11edbbd27d9b85e98 (diff) |
md/raid5: use seqcount to protect access to shape in make_request.
make_request() access various shape parameters (raid_disks, chunk_size
etc) which might be changed by raid5_start_reshape().
If the later is called at and awkward time during the form, the wrong
stripe_head might be used.
So introduce a 'seqcount' and after finding a stripe_head make sure
there is no reason to expect that we got the wrong one.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/raid5.c | 14 | ||||
-rw-r--r-- | drivers/md/raid5.h | 1 |
2 files changed, 14 insertions, 1 deletions
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index d79ecd903269..4263df11d597 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c | |||
@@ -4408,8 +4408,10 @@ static void make_request(struct mddev *mddev, struct bio * bi) | |||
4408 | for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) { | 4408 | for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) { |
4409 | DEFINE_WAIT(w); | 4409 | DEFINE_WAIT(w); |
4410 | int previous; | 4410 | int previous; |
4411 | int seq; | ||
4411 | 4412 | ||
4412 | retry: | 4413 | retry: |
4414 | seq = read_seqcount_begin(&conf->gen_lock); | ||
4413 | previous = 0; | 4415 | previous = 0; |
4414 | prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE); | 4416 | prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE); |
4415 | if (unlikely(conf->reshape_progress != MaxSector)) { | 4417 | if (unlikely(conf->reshape_progress != MaxSector)) { |
@@ -4442,7 +4444,7 @@ static void make_request(struct mddev *mddev, struct bio * bi) | |||
4442 | previous, | 4444 | previous, |
4443 | &dd_idx, NULL); | 4445 | &dd_idx, NULL); |
4444 | pr_debug("raid456: make_request, sector %llu logical %llu\n", | 4446 | pr_debug("raid456: make_request, sector %llu logical %llu\n", |
4445 | (unsigned long long)new_sector, | 4447 | (unsigned long long)new_sector, |
4446 | (unsigned long long)logical_sector); | 4448 | (unsigned long long)logical_sector); |
4447 | 4449 | ||
4448 | sh = get_active_stripe(conf, new_sector, previous, | 4450 | sh = get_active_stripe(conf, new_sector, previous, |
@@ -4471,6 +4473,13 @@ static void make_request(struct mddev *mddev, struct bio * bi) | |||
4471 | goto retry; | 4473 | goto retry; |
4472 | } | 4474 | } |
4473 | } | 4475 | } |
4476 | if (read_seqcount_retry(&conf->gen_lock, seq)) { | ||
4477 | /* Might have got the wrong stripe_head | ||
4478 | * by accident | ||
4479 | */ | ||
4480 | release_stripe(sh); | ||
4481 | goto retry; | ||
4482 | } | ||
4474 | 4483 | ||
4475 | if (rw == WRITE && | 4484 | if (rw == WRITE && |
4476 | logical_sector >= mddev->suspend_lo && | 4485 | logical_sector >= mddev->suspend_lo && |
@@ -5437,6 +5446,7 @@ static struct r5conf *setup_conf(struct mddev *mddev) | |||
5437 | if (alloc_thread_groups(conf, 0)) | 5446 | if (alloc_thread_groups(conf, 0)) |
5438 | goto abort; | 5447 | goto abort; |
5439 | spin_lock_init(&conf->device_lock); | 5448 | spin_lock_init(&conf->device_lock); |
5449 | seqcount_init(&conf->gen_lock); | ||
5440 | init_waitqueue_head(&conf->wait_for_stripe); | 5450 | init_waitqueue_head(&conf->wait_for_stripe); |
5441 | init_waitqueue_head(&conf->wait_for_overlap); | 5451 | init_waitqueue_head(&conf->wait_for_overlap); |
5442 | INIT_LIST_HEAD(&conf->handle_list); | 5452 | INIT_LIST_HEAD(&conf->handle_list); |
@@ -6249,6 +6259,7 @@ static int raid5_start_reshape(struct mddev *mddev) | |||
6249 | 6259 | ||
6250 | atomic_set(&conf->reshape_stripes, 0); | 6260 | atomic_set(&conf->reshape_stripes, 0); |
6251 | spin_lock_irq(&conf->device_lock); | 6261 | spin_lock_irq(&conf->device_lock); |
6262 | write_seqcount_begin(&conf->gen_lock); | ||
6252 | conf->previous_raid_disks = conf->raid_disks; | 6263 | conf->previous_raid_disks = conf->raid_disks; |
6253 | conf->raid_disks += mddev->delta_disks; | 6264 | conf->raid_disks += mddev->delta_disks; |
6254 | conf->prev_chunk_sectors = conf->chunk_sectors; | 6265 | conf->prev_chunk_sectors = conf->chunk_sectors; |
@@ -6265,6 +6276,7 @@ static int raid5_start_reshape(struct mddev *mddev) | |||
6265 | else | 6276 | else |
6266 | conf->reshape_progress = 0; | 6277 | conf->reshape_progress = 0; |
6267 | conf->reshape_safe = conf->reshape_progress; | 6278 | conf->reshape_safe = conf->reshape_progress; |
6279 | write_seqcount_end(&conf->gen_lock); | ||
6268 | spin_unlock_irq(&conf->device_lock); | 6280 | spin_unlock_irq(&conf->device_lock); |
6269 | 6281 | ||
6270 | /* Add some new drives, as many as will fit. | 6282 | /* Add some new drives, as many as will fit. |
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 105366371fbf..435b12d58165 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h | |||
@@ -400,6 +400,7 @@ struct r5conf { | |||
400 | int prev_chunk_sectors; | 400 | int prev_chunk_sectors; |
401 | int prev_algo; | 401 | int prev_algo; |
402 | short generation; /* increments with every reshape */ | 402 | short generation; /* increments with every reshape */ |
403 | seqcount_t gen_lock; /* lock against generation changes */ | ||
403 | unsigned long reshape_checkpoint; /* Time we last updated | 404 | unsigned long reshape_checkpoint; /* Time we last updated |
404 | * metadata */ | 405 | * metadata */ |
405 | long long min_offset_diff; /* minimum difference between | 406 | long long min_offset_diff; /* minimum difference between |