aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShaohua Li <shli@kernel.org>2013-08-29 03:40:32 -0400
committerNeilBrown <neilb@suse.de>2013-09-01 20:31:29 -0400
commitbfc90cb0936f5b972706625f38f72c7cb726c20a (patch)
tree131194fdf489b6dd9eb3e165e6cd1c68bb4c629e
parent4d77e3ba88d085836f1e8e475e3131844dd89d04 (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>
-rw-r--r--drivers/md/raid5.c41
-rw-r--r--drivers/md/raid5.h3
2 files changed, 38 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
81static inline struct hlist_head *stripe_hash(struct r5conf *conf, sector_t sect) 82static 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
236static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh) 252static 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 4950static int handle_active_stripes(struct r5conf *conf, int group,
4923static 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;
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 435b12d58165..2113ffa82c7a 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -213,6 +213,7 @@ struct stripe_head {
213 enum reconstruct_states reconstruct_state; 213 enum reconstruct_states reconstruct_state;
214 spinlock_t stripe_lock; 214 spinlock_t stripe_lock;
215 int cpu; 215 int cpu;
216 struct r5worker_group *group;
216 /** 217 /**
217 * struct stripe_operations 218 * struct stripe_operations
218 * @target - STRIPE_OP_COMPUTE_BLK target 219 * @target - STRIPE_OP_COMPUTE_BLK target
@@ -369,12 +370,14 @@ struct disk_info {
369struct r5worker { 370struct r5worker {
370 struct work_struct work; 371 struct work_struct work;
371 struct r5worker_group *group; 372 struct r5worker_group *group;
373 bool working;
372}; 374};
373 375
374struct r5worker_group { 376struct r5worker_group {
375 struct list_head handle_list; 377 struct list_head handle_list;
376 struct r5conf *conf; 378 struct r5conf *conf;
377 struct r5worker *workers; 379 struct r5worker *workers;
380 int stripes_cnt;
378}; 381};
379 382
380struct r5conf { 383struct r5conf {