diff options
author | Tejun Heo <tj@kernel.org> | 2015-08-18 17:55:12 -0400 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2015-08-18 18:49:16 -0400 |
commit | 4fb72036fbf9c28de7a64b1d3f19b4ce9da1c6bf (patch) | |
tree | ec418021883beb54038b9a2c3f9aaaf3c858c171 | |
parent | 001bea73e70efdf48a9e00188cf302f6b6aed2bf (diff) |
blk-throttle: remove asynchrnous percpu stats allocation mechanism
Because percpu allocator couldn't do non-blocking allocations,
blk-throttle was forced to implement an ad-hoc asynchronous allocation
mechanism for its percpu stats for cases where blkg's (blkcg_gq's) are
allocated from an IO path without sleepable context.
Now that percpu allocator can handle gfp_mask and blkg_policy_data
alloc / free are handled by policy methods, the ad-hoc asynchronous
allocation mechanism can be replaced with direct allocation from
tg_stats_alloc_fn(). Rit it out.
This ensures that an active throtl_grp always has valid non-NULL
->stats_cpu. Remove checks on it.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r-- | block/blk-throttle.c | 112 |
1 files changed, 25 insertions, 87 deletions
diff --git a/block/blk-throttle.c b/block/blk-throttle.c index f1dd691c5359..3c869768cfdd 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c | |||
@@ -144,9 +144,6 @@ struct throtl_grp { | |||
144 | 144 | ||
145 | /* Per cpu stats pointer */ | 145 | /* Per cpu stats pointer */ |
146 | struct tg_stats_cpu __percpu *stats_cpu; | 146 | struct tg_stats_cpu __percpu *stats_cpu; |
147 | |||
148 | /* List of tgs waiting for per cpu stats memory to be allocated */ | ||
149 | struct list_head stats_alloc_node; | ||
150 | }; | 147 | }; |
151 | 148 | ||
152 | struct throtl_data | 149 | struct throtl_data |
@@ -168,13 +165,6 @@ struct throtl_data | |||
168 | struct work_struct dispatch_work; | 165 | struct work_struct dispatch_work; |
169 | }; | 166 | }; |
170 | 167 | ||
171 | /* list and work item to allocate percpu group stats */ | ||
172 | static DEFINE_SPINLOCK(tg_stats_alloc_lock); | ||
173 | static LIST_HEAD(tg_stats_alloc_list); | ||
174 | |||
175 | static void tg_stats_alloc_fn(struct work_struct *); | ||
176 | static DECLARE_DELAYED_WORK(tg_stats_alloc_work, tg_stats_alloc_fn); | ||
177 | |||
178 | static void throtl_pending_timer_fn(unsigned long arg); | 168 | static void throtl_pending_timer_fn(unsigned long arg); |
179 | 169 | ||
180 | static inline struct throtl_grp *pd_to_tg(struct blkg_policy_data *pd) | 170 | static inline struct throtl_grp *pd_to_tg(struct blkg_policy_data *pd) |
@@ -256,53 +246,6 @@ static struct throtl_data *sq_to_td(struct throtl_service_queue *sq) | |||
256 | } \ | 246 | } \ |
257 | } while (0) | 247 | } while (0) |
258 | 248 | ||
259 | static void tg_stats_init(struct tg_stats_cpu *tg_stats) | ||
260 | { | ||
261 | blkg_rwstat_init(&tg_stats->service_bytes); | ||
262 | blkg_rwstat_init(&tg_stats->serviced); | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Worker for allocating per cpu stat for tgs. This is scheduled on the | ||
267 | * system_wq once there are some groups on the alloc_list waiting for | ||
268 | * allocation. | ||
269 | */ | ||
270 | static void tg_stats_alloc_fn(struct work_struct *work) | ||
271 | { | ||
272 | static struct tg_stats_cpu *stats_cpu; /* this fn is non-reentrant */ | ||
273 | struct delayed_work *dwork = to_delayed_work(work); | ||
274 | bool empty = false; | ||
275 | |||
276 | alloc_stats: | ||
277 | if (!stats_cpu) { | ||
278 | int cpu; | ||
279 | |||
280 | stats_cpu = alloc_percpu(struct tg_stats_cpu); | ||
281 | if (!stats_cpu) { | ||
282 | /* allocation failed, try again after some time */ | ||
283 | schedule_delayed_work(dwork, msecs_to_jiffies(10)); | ||
284 | return; | ||
285 | } | ||
286 | for_each_possible_cpu(cpu) | ||
287 | tg_stats_init(per_cpu_ptr(stats_cpu, cpu)); | ||
288 | } | ||
289 | |||
290 | spin_lock_irq(&tg_stats_alloc_lock); | ||
291 | |||
292 | if (!list_empty(&tg_stats_alloc_list)) { | ||
293 | struct throtl_grp *tg = list_first_entry(&tg_stats_alloc_list, | ||
294 | struct throtl_grp, | ||
295 | stats_alloc_node); | ||
296 | swap(tg->stats_cpu, stats_cpu); | ||
297 | list_del_init(&tg->stats_alloc_node); | ||
298 | } | ||
299 | |||
300 | empty = list_empty(&tg_stats_alloc_list); | ||
301 | spin_unlock_irq(&tg_stats_alloc_lock); | ||
302 | if (!empty) | ||
303 | goto alloc_stats; | ||
304 | } | ||
305 | |||
306 | static void throtl_qnode_init(struct throtl_qnode *qn, struct throtl_grp *tg) | 249 | static void throtl_qnode_init(struct throtl_qnode *qn, struct throtl_grp *tg) |
307 | { | 250 | { |
308 | INIT_LIST_HEAD(&qn->node); | 251 | INIT_LIST_HEAD(&qn->node); |
@@ -405,7 +348,27 @@ static void throtl_service_queue_exit(struct throtl_service_queue *sq) | |||
405 | 348 | ||
406 | static struct blkg_policy_data *throtl_pd_alloc(gfp_t gfp, int node) | 349 | static struct blkg_policy_data *throtl_pd_alloc(gfp_t gfp, int node) |
407 | { | 350 | { |
408 | return kzalloc_node(sizeof(struct throtl_grp), gfp, node); | 351 | struct throtl_grp *tg; |
352 | int cpu; | ||
353 | |||
354 | tg = kzalloc_node(sizeof(*tg), gfp, node); | ||
355 | if (!tg) | ||
356 | return NULL; | ||
357 | |||
358 | tg->stats_cpu = alloc_percpu_gfp(struct tg_stats_cpu, gfp); | ||
359 | if (!tg->stats_cpu) { | ||
360 | kfree(tg); | ||
361 | return NULL; | ||
362 | } | ||
363 | |||
364 | for_each_possible_cpu(cpu) { | ||
365 | struct tg_stats_cpu *stats_cpu = per_cpu_ptr(tg->stats_cpu, cpu); | ||
366 | |||
367 | blkg_rwstat_init(&stats_cpu->service_bytes); | ||
368 | blkg_rwstat_init(&stats_cpu->serviced); | ||
369 | } | ||
370 | |||
371 | return &tg->pd; | ||
409 | } | 372 | } |
410 | 373 | ||
411 | static void throtl_pd_init(struct blkcg_gq *blkg) | 374 | static void throtl_pd_init(struct blkcg_gq *blkg) |
@@ -413,7 +376,6 @@ static void throtl_pd_init(struct blkcg_gq *blkg) | |||
413 | struct throtl_grp *tg = blkg_to_tg(blkg); | 376 | struct throtl_grp *tg = blkg_to_tg(blkg); |
414 | struct throtl_data *td = blkg->q->td; | 377 | struct throtl_data *td = blkg->q->td; |
415 | struct throtl_service_queue *parent_sq; | 378 | struct throtl_service_queue *parent_sq; |
416 | unsigned long flags; | ||
417 | int rw; | 379 | int rw; |
418 | 380 | ||
419 | /* | 381 | /* |
@@ -448,16 +410,6 @@ static void throtl_pd_init(struct blkcg_gq *blkg) | |||
448 | tg->bps[WRITE] = -1; | 410 | tg->bps[WRITE] = -1; |
449 | tg->iops[READ] = -1; | 411 | tg->iops[READ] = -1; |
450 | tg->iops[WRITE] = -1; | 412 | tg->iops[WRITE] = -1; |
451 | |||
452 | /* | ||
453 | * Ugh... We need to perform per-cpu allocation for tg->stats_cpu | ||
454 | * but percpu allocator can't be called from IO path. Queue tg on | ||
455 | * tg_stats_alloc_list and allocate from work item. | ||
456 | */ | ||
457 | spin_lock_irqsave(&tg_stats_alloc_lock, flags); | ||
458 | list_add(&tg->stats_alloc_node, &tg_stats_alloc_list); | ||
459 | schedule_delayed_work(&tg_stats_alloc_work, 0); | ||
460 | spin_unlock_irqrestore(&tg_stats_alloc_lock, flags); | ||
461 | } | 413 | } |
462 | 414 | ||
463 | /* | 415 | /* |
@@ -487,20 +439,16 @@ static void throtl_pd_online(struct blkcg_gq *blkg) | |||
487 | static void throtl_pd_exit(struct blkcg_gq *blkg) | 439 | static void throtl_pd_exit(struct blkcg_gq *blkg) |
488 | { | 440 | { |
489 | struct throtl_grp *tg = blkg_to_tg(blkg); | 441 | struct throtl_grp *tg = blkg_to_tg(blkg); |
490 | unsigned long flags; | ||
491 | |||
492 | spin_lock_irqsave(&tg_stats_alloc_lock, flags); | ||
493 | list_del_init(&tg->stats_alloc_node); | ||
494 | spin_unlock_irqrestore(&tg_stats_alloc_lock, flags); | ||
495 | |||
496 | free_percpu(tg->stats_cpu); | ||
497 | 442 | ||
498 | throtl_service_queue_exit(&tg->service_queue); | 443 | throtl_service_queue_exit(&tg->service_queue); |
499 | } | 444 | } |
500 | 445 | ||
501 | static void throtl_pd_free(struct blkg_policy_data *pd) | 446 | static void throtl_pd_free(struct blkg_policy_data *pd) |
502 | { | 447 | { |
503 | kfree(pd); | 448 | struct throtl_grp *tg = pd_to_tg(pd); |
449 | |||
450 | free_percpu(tg->stats_cpu); | ||
451 | kfree(tg); | ||
504 | } | 452 | } |
505 | 453 | ||
506 | static void throtl_pd_reset_stats(struct blkcg_gq *blkg) | 454 | static void throtl_pd_reset_stats(struct blkcg_gq *blkg) |
@@ -508,9 +456,6 @@ static void throtl_pd_reset_stats(struct blkcg_gq *blkg) | |||
508 | struct throtl_grp *tg = blkg_to_tg(blkg); | 456 | struct throtl_grp *tg = blkg_to_tg(blkg); |
509 | int cpu; | 457 | int cpu; |
510 | 458 | ||
511 | if (tg->stats_cpu == NULL) | ||
512 | return; | ||
513 | |||
514 | for_each_possible_cpu(cpu) { | 459 | for_each_possible_cpu(cpu) { |
515 | struct tg_stats_cpu *sc = per_cpu_ptr(tg->stats_cpu, cpu); | 460 | struct tg_stats_cpu *sc = per_cpu_ptr(tg->stats_cpu, cpu); |
516 | 461 | ||
@@ -973,10 +918,6 @@ static void throtl_update_dispatch_stats(struct blkcg_gq *blkg, u64 bytes, | |||
973 | struct tg_stats_cpu *stats_cpu; | 918 | struct tg_stats_cpu *stats_cpu; |
974 | unsigned long flags; | 919 | unsigned long flags; |
975 | 920 | ||
976 | /* If per cpu stats are not allocated yet, don't do any accounting. */ | ||
977 | if (tg->stats_cpu == NULL) | ||
978 | return; | ||
979 | |||
980 | /* | 921 | /* |
981 | * Disabling interrupts to provide mutual exclusion between two | 922 | * Disabling interrupts to provide mutual exclusion between two |
982 | * writes on same cpu. It probably is not needed for 64bit. Not | 923 | * writes on same cpu. It probably is not needed for 64bit. Not |
@@ -1302,9 +1243,6 @@ static u64 tg_prfill_cpu_rwstat(struct seq_file *sf, | |||
1302 | struct blkg_rwstat rwstat = { }, tmp; | 1243 | struct blkg_rwstat rwstat = { }, tmp; |
1303 | int i, cpu; | 1244 | int i, cpu; |
1304 | 1245 | ||
1305 | if (tg->stats_cpu == NULL) | ||
1306 | return 0; | ||
1307 | |||
1308 | for_each_possible_cpu(cpu) { | 1246 | for_each_possible_cpu(cpu) { |
1309 | struct tg_stats_cpu *sc = per_cpu_ptr(tg->stats_cpu, cpu); | 1247 | struct tg_stats_cpu *sc = per_cpu_ptr(tg->stats_cpu, cpu); |
1310 | 1248 | ||