diff options
author | Shaohua Li <shli@kernel.org> | 2012-08-01 18:33:00 -0400 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2012-08-01 18:33:00 -0400 |
commit | 8811b5968f6216e97ccb9fe7b9883af39e339921 (patch) | |
tree | 9693073f6cb1bcd8743d429d7d3330018bc0ce6d | |
parent | 74018dc3063a2c729fc73041c0a9f03aac995920 (diff) |
raid5: make_request use batch stripe release
make_request() does stripe release for every stripe and the stripe usually has
count 1, which makes previous release_stripe() optimization not work. In my
test, this release_stripe() becomes the heaviest pleace to take
conf->device_lock after previous patches applied.
Below patch makes stripe release batch. All the stripes will be released in
unplug. The STRIPE_ON_UNPLUG_LIST bit is to protect concurrent access stripe
lru.
Signed-off-by: Shaohua Li <shli@fusionio.com>
Signed-off-by: NeilBrown <neilb@suse.de>
-rw-r--r-- | drivers/md/raid5.c | 62 | ||||
-rw-r--r-- | drivers/md/raid5.h | 1 |
2 files changed, 60 insertions, 3 deletions
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index bde9da2baa39..978ba9b7a3c4 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c | |||
@@ -471,7 +471,8 @@ get_active_stripe(struct r5conf *conf, sector_t sector, | |||
471 | } else { | 471 | } else { |
472 | if (atomic_read(&sh->count)) { | 472 | if (atomic_read(&sh->count)) { |
473 | BUG_ON(!list_empty(&sh->lru) | 473 | BUG_ON(!list_empty(&sh->lru) |
474 | && !test_bit(STRIPE_EXPANDING, &sh->state)); | 474 | && !test_bit(STRIPE_EXPANDING, &sh->state) |
475 | && !test_bit(STRIPE_ON_UNPLUG_LIST, &sh->state)); | ||
475 | } else { | 476 | } else { |
476 | if (!test_bit(STRIPE_HANDLE, &sh->state)) | 477 | if (!test_bit(STRIPE_HANDLE, &sh->state)) |
477 | atomic_inc(&conf->active_stripes); | 478 | atomic_inc(&conf->active_stripes); |
@@ -3988,6 +3989,62 @@ static struct stripe_head *__get_priority_stripe(struct r5conf *conf) | |||
3988 | return sh; | 3989 | return sh; |
3989 | } | 3990 | } |
3990 | 3991 | ||
3992 | struct raid5_plug_cb { | ||
3993 | struct blk_plug_cb cb; | ||
3994 | struct list_head list; | ||
3995 | }; | ||
3996 | |||
3997 | static void raid5_unplug(struct blk_plug_cb *blk_cb, bool from_schedule) | ||
3998 | { | ||
3999 | struct raid5_plug_cb *cb = container_of( | ||
4000 | blk_cb, struct raid5_plug_cb, cb); | ||
4001 | struct stripe_head *sh; | ||
4002 | struct mddev *mddev = cb->cb.data; | ||
4003 | struct r5conf *conf = mddev->private; | ||
4004 | |||
4005 | if (cb->list.next && !list_empty(&cb->list)) { | ||
4006 | spin_lock_irq(&conf->device_lock); | ||
4007 | while (!list_empty(&cb->list)) { | ||
4008 | sh = list_first_entry(&cb->list, struct stripe_head, lru); | ||
4009 | list_del_init(&sh->lru); | ||
4010 | /* | ||
4011 | * avoid race release_stripe_plug() sees | ||
4012 | * STRIPE_ON_UNPLUG_LIST clear but the stripe | ||
4013 | * is still in our list | ||
4014 | */ | ||
4015 | smp_mb__before_clear_bit(); | ||
4016 | clear_bit(STRIPE_ON_UNPLUG_LIST, &sh->state); | ||
4017 | __release_stripe(conf, sh); | ||
4018 | } | ||
4019 | spin_unlock_irq(&conf->device_lock); | ||
4020 | } | ||
4021 | kfree(cb); | ||
4022 | } | ||
4023 | |||
4024 | static void release_stripe_plug(struct mddev *mddev, | ||
4025 | struct stripe_head *sh) | ||
4026 | { | ||
4027 | struct blk_plug_cb *blk_cb = blk_check_plugged( | ||
4028 | raid5_unplug, mddev, | ||
4029 | sizeof(struct raid5_plug_cb)); | ||
4030 | struct raid5_plug_cb *cb; | ||
4031 | |||
4032 | if (!blk_cb) { | ||
4033 | release_stripe(sh); | ||
4034 | return; | ||
4035 | } | ||
4036 | |||
4037 | cb = container_of(blk_cb, struct raid5_plug_cb, cb); | ||
4038 | |||
4039 | if (cb->list.next == NULL) | ||
4040 | INIT_LIST_HEAD(&cb->list); | ||
4041 | |||
4042 | if (!test_and_set_bit(STRIPE_ON_UNPLUG_LIST, &sh->state)) | ||
4043 | list_add_tail(&sh->lru, &cb->list); | ||
4044 | else | ||
4045 | release_stripe(sh); | ||
4046 | } | ||
4047 | |||
3991 | static void make_request(struct mddev *mddev, struct bio * bi) | 4048 | static void make_request(struct mddev *mddev, struct bio * bi) |
3992 | { | 4049 | { |
3993 | struct r5conf *conf = mddev->private; | 4050 | struct r5conf *conf = mddev->private; |
@@ -4116,8 +4173,7 @@ static void make_request(struct mddev *mddev, struct bio * bi) | |||
4116 | if ((bi->bi_rw & REQ_SYNC) && | 4173 | if ((bi->bi_rw & REQ_SYNC) && |
4117 | !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) | 4174 | !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) |
4118 | atomic_inc(&conf->preread_active_stripes); | 4175 | atomic_inc(&conf->preread_active_stripes); |
4119 | mddev_check_plugged(mddev); | 4176 | release_stripe_plug(mddev, sh); |
4120 | release_stripe(sh); | ||
4121 | } else { | 4177 | } else { |
4122 | /* cannot get stripe for read-ahead, just give-up */ | 4178 | /* cannot get stripe for read-ahead, just give-up */ |
4123 | clear_bit(BIO_UPTODATE, &bi->bi_flags); | 4179 | clear_bit(BIO_UPTODATE, &bi->bi_flags); |
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 2164021f3b5f..9a7b36f0a425 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h | |||
@@ -319,6 +319,7 @@ enum { | |||
319 | STRIPE_BIOFILL_RUN, | 319 | STRIPE_BIOFILL_RUN, |
320 | STRIPE_COMPUTE_RUN, | 320 | STRIPE_COMPUTE_RUN, |
321 | STRIPE_OPS_REQ_PENDING, | 321 | STRIPE_OPS_REQ_PENDING, |
322 | STRIPE_ON_UNPLUG_LIST, | ||
322 | }; | 323 | }; |
323 | 324 | ||
324 | /* | 325 | /* |