diff options
author | Shaohua Li <shli@kernel.org> | 2013-08-29 03:40:32 -0400 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2013-09-01 20:31:29 -0400 |
commit | bfc90cb0936f5b972706625f38f72c7cb726c20a (patch) | |
tree | 131194fdf489b6dd9eb3e165e6cd1c68bb4c629e /drivers/md/raid5.c | |
parent | 4d77e3ba88d085836f1e8e475e3131844dd89d04 (diff) |
raid5: only wakeup necessary threads
If there are not enough stripes to handle, we'd better not always
queue all available work_structs. If one worker can only handle small
or even none stripes, it will impact request merge and create lock
contention.
With this patch, the number of work_struct running will depend on
pending stripes number. Note: some statistics info used in the patch
are accessed without locking protection. This should doesn't matter,
we just try best to avoid queue unnecessary work_struct.
Signed-off-by: Shaohua Li <shli@fusionio.com>
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers/md/raid5.c')
-rw-r--r-- | drivers/md/raid5.c | 41 |
1 files changed, 35 insertions, 6 deletions
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 663a8e58d439..7ff4f252ca1a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c | |||
@@ -77,6 +77,7 @@ static struct workqueue_struct *raid5_wq; | |||
77 | #define BYPASS_THRESHOLD 1 | 77 | #define BYPASS_THRESHOLD 1 |
78 | #define NR_HASH (PAGE_SIZE / sizeof(struct hlist_head)) | 78 | #define NR_HASH (PAGE_SIZE / sizeof(struct hlist_head)) |
79 | #define HASH_MASK (NR_HASH - 1) | 79 | #define HASH_MASK (NR_HASH - 1) |
80 | #define MAX_STRIPE_BATCH 8 | ||
80 | 81 | ||
81 | static inline struct hlist_head *stripe_hash(struct r5conf *conf, sector_t sect) | 82 | static inline struct hlist_head *stripe_hash(struct r5conf *conf, sector_t sect) |
82 | { | 83 | { |
@@ -209,6 +210,7 @@ static void raid5_wakeup_stripe_thread(struct stripe_head *sh) | |||
209 | { | 210 | { |
210 | struct r5conf *conf = sh->raid_conf; | 211 | struct r5conf *conf = sh->raid_conf; |
211 | struct r5worker_group *group; | 212 | struct r5worker_group *group; |
213 | int thread_cnt; | ||
212 | int i, cpu = sh->cpu; | 214 | int i, cpu = sh->cpu; |
213 | 215 | ||
214 | if (!cpu_online(cpu)) { | 216 | if (!cpu_online(cpu)) { |
@@ -220,6 +222,8 @@ static void raid5_wakeup_stripe_thread(struct stripe_head *sh) | |||
220 | struct r5worker_group *group; | 222 | struct r5worker_group *group; |
221 | group = conf->worker_groups + cpu_to_group(cpu); | 223 | group = conf->worker_groups + cpu_to_group(cpu); |
222 | list_add_tail(&sh->lru, &group->handle_list); | 224 | list_add_tail(&sh->lru, &group->handle_list); |
225 | group->stripes_cnt++; | ||
226 | sh->group = group; | ||
223 | } | 227 | } |
224 | 228 | ||
225 | if (conf->worker_cnt_per_group == 0) { | 229 | if (conf->worker_cnt_per_group == 0) { |
@@ -229,8 +233,20 @@ static void raid5_wakeup_stripe_thread(struct stripe_head *sh) | |||
229 | 233 | ||
230 | group = conf->worker_groups + cpu_to_group(sh->cpu); | 234 | group = conf->worker_groups + cpu_to_group(sh->cpu); |
231 | 235 | ||
232 | for (i = 0; i < conf->worker_cnt_per_group; i++) | 236 | group->workers[0].working = true; |
233 | queue_work_on(sh->cpu, raid5_wq, &group->workers[i].work); | 237 | /* at least one worker should run to avoid race */ |
238 | queue_work_on(sh->cpu, raid5_wq, &group->workers[0].work); | ||
239 | |||
240 | thread_cnt = group->stripes_cnt / MAX_STRIPE_BATCH - 1; | ||
241 | /* wakeup more workers */ | ||
242 | for (i = 1; i < conf->worker_cnt_per_group && thread_cnt > 0; i++) { | ||
243 | if (group->workers[i].working == false) { | ||
244 | group->workers[i].working = true; | ||
245 | queue_work_on(sh->cpu, raid5_wq, | ||
246 | &group->workers[i].work); | ||
247 | thread_cnt--; | ||
248 | } | ||
249 | } | ||
234 | } | 250 | } |
235 | 251 | ||
236 | static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh) | 252 | static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh) |
@@ -589,6 +605,10 @@ get_active_stripe(struct r5conf *conf, sector_t sector, | |||
589 | !test_bit(STRIPE_EXPANDING, &sh->state)) | 605 | !test_bit(STRIPE_EXPANDING, &sh->state)) |
590 | BUG(); | 606 | BUG(); |
591 | list_del_init(&sh->lru); | 607 | list_del_init(&sh->lru); |
608 | if (sh->group) { | ||
609 | sh->group->stripes_cnt--; | ||
610 | sh->group = NULL; | ||
611 | } | ||
592 | } | 612 | } |
593 | } | 613 | } |
594 | } while (sh == NULL); | 614 | } while (sh == NULL); |
@@ -4153,15 +4173,18 @@ static struct stripe_head *__get_priority_stripe(struct r5conf *conf, int group) | |||
4153 | { | 4173 | { |
4154 | struct stripe_head *sh = NULL, *tmp; | 4174 | struct stripe_head *sh = NULL, *tmp; |
4155 | struct list_head *handle_list = NULL; | 4175 | struct list_head *handle_list = NULL; |
4176 | struct r5worker_group *wg = NULL; | ||
4156 | 4177 | ||
4157 | if (conf->worker_cnt_per_group == 0) { | 4178 | if (conf->worker_cnt_per_group == 0) { |
4158 | handle_list = &conf->handle_list; | 4179 | handle_list = &conf->handle_list; |
4159 | } else if (group != ANY_GROUP) { | 4180 | } else if (group != ANY_GROUP) { |
4160 | handle_list = &conf->worker_groups[group].handle_list; | 4181 | handle_list = &conf->worker_groups[group].handle_list; |
4182 | wg = &conf->worker_groups[group]; | ||
4161 | } else { | 4183 | } else { |
4162 | int i; | 4184 | int i; |
4163 | for (i = 0; i < conf->group_cnt; i++) { | 4185 | for (i = 0; i < conf->group_cnt; i++) { |
4164 | handle_list = &conf->worker_groups[i].handle_list; | 4186 | handle_list = &conf->worker_groups[i].handle_list; |
4187 | wg = &conf->worker_groups[i]; | ||
4165 | if (!list_empty(handle_list)) | 4188 | if (!list_empty(handle_list)) |
4166 | break; | 4189 | break; |
4167 | } | 4190 | } |
@@ -4208,11 +4231,16 @@ static struct stripe_head *__get_priority_stripe(struct r5conf *conf, int group) | |||
4208 | if (conf->bypass_count < 0) | 4231 | if (conf->bypass_count < 0) |
4209 | conf->bypass_count = 0; | 4232 | conf->bypass_count = 0; |
4210 | } | 4233 | } |
4234 | wg = NULL; | ||
4211 | } | 4235 | } |
4212 | 4236 | ||
4213 | if (!sh) | 4237 | if (!sh) |
4214 | return NULL; | 4238 | return NULL; |
4215 | 4239 | ||
4240 | if (wg) { | ||
4241 | wg->stripes_cnt--; | ||
4242 | sh->group = NULL; | ||
4243 | } | ||
4216 | list_del_init(&sh->lru); | 4244 | list_del_init(&sh->lru); |
4217 | atomic_inc(&sh->count); | 4245 | atomic_inc(&sh->count); |
4218 | BUG_ON(atomic_read(&sh->count) != 1); | 4246 | BUG_ON(atomic_read(&sh->count) != 1); |
@@ -4919,8 +4947,8 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio) | |||
4919 | return handled; | 4947 | return handled; |
4920 | } | 4948 | } |
4921 | 4949 | ||
4922 | #define MAX_STRIPE_BATCH 8 | 4950 | static int handle_active_stripes(struct r5conf *conf, int group, |
4923 | static int handle_active_stripes(struct r5conf *conf, int group) | 4951 | struct r5worker *worker) |
4924 | { | 4952 | { |
4925 | struct stripe_head *batch[MAX_STRIPE_BATCH], *sh; | 4953 | struct stripe_head *batch[MAX_STRIPE_BATCH], *sh; |
4926 | int i, batch_size = 0; | 4954 | int i, batch_size = 0; |
@@ -4963,7 +4991,8 @@ static void raid5_do_work(struct work_struct *work) | |||
4963 | 4991 | ||
4964 | released = release_stripe_list(conf); | 4992 | released = release_stripe_list(conf); |
4965 | 4993 | ||
4966 | batch_size = handle_active_stripes(conf, group_id); | 4994 | batch_size = handle_active_stripes(conf, group_id, worker); |
4995 | worker->working = false; | ||
4967 | if (!batch_size && !released) | 4996 | if (!batch_size && !released) |
4968 | break; | 4997 | break; |
4969 | handled += batch_size; | 4998 | handled += batch_size; |
@@ -5025,7 +5054,7 @@ static void raid5d(struct md_thread *thread) | |||
5025 | handled++; | 5054 | handled++; |
5026 | } | 5055 | } |
5027 | 5056 | ||
5028 | batch_size = handle_active_stripes(conf, ANY_GROUP); | 5057 | batch_size = handle_active_stripes(conf, ANY_GROUP, NULL); |
5029 | if (!batch_size && !released) | 5058 | if (!batch_size && !released) |
5030 | break; | 5059 | break; |
5031 | handled += batch_size; | 5060 | handled += batch_size; |