blkcg: unify blkgs for different policies
Subsystems (2)
Tejun Heo
maintainerPATCH 1/1 blkcg: unify blkgs for different policies
Hey, again.
Currently, blkcg policies have and manage their own blkgs, so blkgs are per cgroup-queue-policy combination instead of cgroup-queue combination. This leads to nasty problems. It isn't clear which part are common to both policies. There are unused duplicates in common part of blkg and it isn't trivial to tell which part is being used. The separation also leads to duplicate logic in both policies which makes the code difficult to follow, prone to subtle bugs and, most importantly, hinders proper layering between blkcg core and policy implementations.
Because locking, blkg management, elvswitch and policy [de]registration are tightly woven, it is challenging to untangle - doing proper in-place policy data replacement requires locking improvements which in turn is painful to do when policy implementations are doing their own things with blkgs.
As a transitional step, all blkgs other than root one are shot down on policy [de]registration and root blkg is updated in place. This is hackish but should get us through locking update after which we can implement in-place update for all blkgs safely. While this does introduce race window while policies are being [de]registered, this isn't anything new (e.g. none of stat update functions synchronize against policy update) and shouldn't cause any actual problem given blk-throttle can't be built as module and cfq-iosched is default iosched on most installations.
This patchset was pretty painful but I think/hope things will be eaiser from here on. Note that this patchset does add ~180 LOC. Some of them are comments and it's expected to shrink again with further cleanups and removal of transitional stuff.
Changes to come are:
* locking simplification
* proper in-place update of policy data for all blkgs on policy change
* fix broken blkcg switch after throttling.
* use unified stats updated under queue lock and drop percpu stats which should fix locking / context bug across percpu allocation.
* make set of applied policies per-queue
* move stats and conf into their owning policies and let blkcg core provide generic framework / helper instead of hard coding all the possible ones. This should be accompanied by cgroup updates to allow changing files in cgroupfs. Not sure how this will turn out yet.
This patchset contains the following 11 patches.
0001-blkcg-let-blkio_group-point-to-blkio_cgroup-directly.patch 0002-block-relocate-elevator-initialized-test-from-blk_cl.patch 0003-blkcg-add-blkcg_-init-drain-exit-_queue.patch 0004-blkcg-clear-all-request_queues-on-blkcg-policy-un-re.patch 0005-blkcg-let-blkcg-core-handle-policy-private-data-allo.patch 0006-blkcg-move-refcnt-to-blkcg-core.patch 0007-blkcg-make-blkg-pd-an-array-and-move-configuration-a.patch 0008-blkcg-don-t-use-blkg-plid-in-stat-related-functions.patch 0009-blkcg-move-per-queue-blkg-list-heads-and-counters-to.patch 0010-blkcg-let-blkcg-core-manage-per-queue-blkg-list-and-.patch 0011-blkcg-unify-blkg-s-for-blkcg-policies.patch
0001-0003 are prep patches.
0004 shoots down all non-root blkgs on policy [de]registration.
0005 separates per-policy data from common part of blkg and allocate them separately from blkcg core.
0006-0010 collect common data fields and logic from policy implementations into blkcg core.
0011 unifies blkgs so that there's one blkg per cgroup-queue pair.
This patchset is on top of
v3.3-rc2 62aa2b537c6f5957afd98e29f96897419ed5ebab
+ [1] blkcg: kill policy node and blkg->dev, take#4
and is also available in the following git branch.
git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc.git blkcg-unified-blkg
diffstat follows.
block/blk-cgroup.c | 674 +++++++++++++++++++++++++++++++++++-------------- block/blk-cgroup.h | 211 +++++++++++---- block/blk-core.c | 26 + block/blk-sysfs.c | 6 block/blk-throttle.c | 232 +++------------- block/blk.h | 2 block/cfq-iosched.c | 274 +++++-------------- block/cfq.h | 96 ++++-- block/elevator.c | 2 include/linux/blkdev.h | 7 10 files changed, 853 insertions(+), 677 deletions(-)
Thanks.
--
tejun
Tejun Heo
maintainerPATCH 1/11 blkcg: let blkio_group point to blkio_cgroup directly
Currently, blkg points to the associated blkcg via its css_id. This unnecessarily complicates dereferencing blkcg. Let blkg hold a reference to the associated blkcg and point directly to it and disable css_id on blkio_subsys.
This change requires splitting blkiocg_destroy() into blkiocg_pre_destroy() and blkiocg_destroy() so that all blkg's can be destroyed and all the blkcg references held by them dropped during cgroup removal.
--- block/blk-cgroup.c | 43 ++++++++++++++++++++++++------------------- block/blk-cgroup.h | 2 +- block/blk-throttle.c | 3 +++ block/cfq-iosched.c | 4 ++++ 4 files changed, 32 insertions(+), 20 deletions(-)
block/blk-cgroup.c +24 -19
@@ -37,6 +37,7 @@ static int blkiocg_can_attach(struct cgroup_subsys *, struct cgroup *, struct cgroup_taskset *);static void blkiocg_attach (struct cgroup_subsys *,struct cgroup *,struct cgroup_taskset *); +static int blkiocg_pre_destroy (struct cgroup_subsys *,struct cgroup *);static void blkiocg_destroy (struct cgroup_subsys *,struct cgroup *);static int blkiocg_populate (struct cgroup_subsys *,struct cgroup *);
@@ -51,10 +52,10 @@ struct cgroup_subsys blkio_subsys = { .create = blkiocg_create, .can_attach = blkiocg_can_attach, .attach = blkiocg_attach, + .pre_destroy = blkiocg_pre_destroy, .destroy = blkiocg_destroy, .populate = blkiocg_populate, .subsys_id = blkio_subsys_id, - .use_id = 1, .module = THIS_MODULE, };EXPORT_SYMBOL_GPL (blkio_subsys);
@@ -442,6 +443,7 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg,if (blkg)return blkg; +/* blkg holds a reference to blkcg */ if (!css_tryget (&blkcg->css))return ERR_PTR (-EINVAL );
@@ -463,15 +465,16 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg,spin_lock_init (&new_blkg->stats_lock);rcu_assign_pointer (new_blkg->q, q); - new_blkg->blkcg_id =css_id (&blkcg->css); + new_blkg->blkcg = blkcg; new_blkg->plid = plid;cgroup_path (blkcg->css.cgroup, new_blkg->path,sizeof (new_blkg->path)); + }else { +css_put (&blkcg->css); }rcu_read_lock ();spin_lock_irq (q->queue_lock); -css_put (&blkcg->css);/* did bypass get turned on inbetween? */ if (unlikely (blk_queue_bypass (q)) && !for_root) {
@@ -500,6 +503,7 @@ out:if (new_blkg) {free_percpu (new_blkg->stats_cpu);kfree (new_blkg); +css_put (&blkcg->css); }return blkg; }
@@ -508,7 +512,6 @@ EXPORT_SYMBOL_GPL(blkg_lookup_create);static void __blkiocg_del_blkio_group (struct blkio_group *blkg) {hlist_del_init_rcu (&blkg->blkcg_node); - blkg->blkcg_id =0 ; }/*
@@ -517,24 +520,17 @@ static void __blkiocg_del_blkio_group(struct blkio_group *blkg)*/ int blkiocg_del_blkio_group (struct blkio_group *blkg) { -struct blkio_cgroup *blkcg; +struct blkio_cgroup *blkcg = blkg->blkcg;unsigned long flags; -struct cgroup_subsys_state *css;int ret =1 ; -rcu_read_lock (); - css =css_lookup (&blkio_subsys, blkg->blkcg_id); -if (css) { - blkcg = container_of(css, struct blkio_cgroup, css); -spin_lock_irqsave (&blkcg->lock, flags); -if (!hlist_unhashed (&blkg->blkcg_node)) { -__blkiocg_del_blkio_group (blkg); - ret =0 ; - } -spin_unlock_irqrestore (&blkcg->lock, flags); +spin_lock_irqsave (&blkcg->lock, flags); +if (!hlist_unhashed (&blkg->blkcg_node)) { +__blkiocg_del_blkio_group (blkg); + ret =0 ; } +spin_unlock_irqrestore (&blkcg->lock, flags); -rcu_read_unlock ();return ret; }EXPORT_SYMBOL_GPL (blkiocg_del_blkio_group);
@@ -1376,7 +1372,8 @@ static int blkiocg_populate(struct cgroup_subsys *subsys, struct cgroup *cgroup) ARRAY_SIZE(blkio_files)); } -static void blkiocg_destroy (struct cgroup_subsys *subsys,struct cgroup *cgroup) +static int blkiocg_pre_destroy (struct cgroup_subsys *subsys, +struct cgroup *cgroup) {struct blkio_cgroup *blkcg =cgroup_to_blkio_cgroup (cgroup);unsigned long flags;
@@ -1385,6 +1382,7 @@ static void blkiocg_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup)struct blkio_policy_type *blkiop;rcu_read_lock (); + do { spin_lock_irqsave(&blkcg->lock, flags);
@@ -1414,8 +1412,15 @@ static void blkiocg_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup)spin_unlock (&blkio_list_lock); }while (1 ); -free_css_id (&blkio_subsys, &blkcg->css);rcu_read_unlock (); + +return 0 ; +} + +static void blkiocg_destroy (struct cgroup_subsys *subsys,struct cgroup *cgroup) +{ +struct blkio_cgroup *blkcg =cgroup_to_blkio_cgroup (cgroup); +if (blkcg != &blkio_root_cgroup)kfree (blkcg); }
block/blk-cgroup.h +1 -1
@@ -163,7 +163,7 @@ struct blkio_group {/* Pointer to the associated request_queue, RCU protected */ struct request_queue __rcu *q;struct hlist_node blkcg_node; -unsigned short blkcg_id; +struct blkio_cgroup *blkcg;/* Store cgroup path */ char path[128 ];/* policy which owns this blk group */
block/blk-throttle.c +3 -0
@@ -169,6 +169,9 @@ static void throtl_put_tg(struct throtl_grp *tg)if (!atomic_dec_and_test (&tg->ref))return ; +/* release the extra blkcg reference this blkg has been holding */ +css_put (&tg->blkg.blkcg->css); +/* * A group is freed in rcu manner. But having an rcu lock does not * mean that one can access all the fields of blkg and assume these
block/cfq-iosched.c +4 -0
@@ -1133,6 +1133,10 @@ static void cfq_put_cfqg(struct cfq_group *cfqg) cfqg->ref--;if (cfqg->ref)return ; + +/* release the extra blkcg reference this blkg has been holding */ +css_put (&cfqg->blkg.blkcg->css); +for_each_cfqg_st (cfqg, i, j, st)BUG_ON (!RB_EMPTY_ROOT (&st->rb));free_percpu (cfqg->blkg.stats_cpu);
Signature · 1 line
1.7.7.3
Re: [PATCH 01/11] blkcg: let blkio_group point to blkio_cgroup directly
Quoted 12 lines · Tejun Heo
Currently, blkg points to the associated blkcg via its
css_id. This unnecessarily complicates dereferencing blkcg. Let blkg hold a reference to the associated blkcg and point directly to it and disablecss_idonblkio_subsys.This change requires splitting
blkiocg_destroy()intoblkiocg_pre_destroy()andblkiocg_destroy()so that all blkg's can be destroyed and all the blkcg references held by them dropped during cgroup removal.Signed-off-by Tejun Heo <[email protected]>Cc Vivek Goyal <[email protected]>
[..]
block/blk-cgroup.c static void blkiocg_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup) · line 1425
Quoted 6 lines
block/blk-cgroup.c +4
-0+static void blkiocg_destroy (struct cgroup_subsys *subsys,struct cgroup *cgroup) +{ +struct blkio_cgroup *blkcg =cgroup_to_blkio_cgroup (cgroup); +if (blkcg != &blkio_root_cgroup)kfree (blkcg);
Hi Tejun,
What makes sure that all the blkg are gone and they have dropped their reference to blkcg? IIUC, pre-destroy will just make sure to decouple blkg from request queue as well as blkcg list and also drop joint cgroup and request queue reference.
But there could well be some IO queued in the group which might have its own reference and will be dropped later when IO completes. So at the time of blkiocg_destroy() it is not guranteed that there are no reference holders to blkcg?
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH 01/11] blkcg: let blkio_group point to blkio_cgroup directly
Hello,
Quoted 12 lines · Vivek Goyal
block/blk-cgroup.c static void blkiocg_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup) · line 1425
Quoted 6 lines
block/blk-cgroup.c +4
-0+static void blkiocg_destroy (struct cgroup_subsys *subsys,struct cgroup *cgroup) +{ +struct blkio_cgroup *blkcg =cgroup_to_blkio_cgroup (cgroup); +if (blkcg != &blkio_root_cgroup)kfree (blkcg);What makes sure that all the blkg are gone and they have dropped their reference to blkcg? IIUC, pre-destroy will just make sure to decouple blkg from request queue as well as blkcg list and also drop joint cgroup and request queue reference.
Quoted 4 lines
But there could well be some IO queued in the group which might have its own reference and will be dropped later when IO completes. So at the time of
blkiocg_destroy()it is not guranteed that there are no reference holders to blkcg?
Yeah, and that would wait till all css refs held by blkgs are gone, right?
Signature · 1 line
tejun
Re: [PATCH 01/11] blkcg: let blkio_group point to blkio_cgroup directly
Quoted 15 lines · Tejun Heo
Hello,
Quoted 12 lines · Vivek Goyal
block/blk-cgroup.c static void blkiocg_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup) · line 1425
Quoted 6 lines
block/blk-cgroup.c +4
-0+static void blkiocg_destroy (struct cgroup_subsys *subsys,struct cgroup *cgroup) +{ +struct blkio_cgroup *blkcg =cgroup_to_blkio_cgroup (cgroup); +if (blkcg != &blkio_root_cgroup)kfree (blkcg);What makes sure that all the blkg are gone and they have dropped their reference to blkcg? IIUC, pre-destroy will just make sure to decouple blkg from request queue as well as blkcg list and also drop joint cgroup and request queue reference.
Quoted 7 lines
Quoted 4 lines
But there could well be some IO queued in the group which might have its own reference and will be dropped later when IO completes. So at the time of
blkiocg_destroy()it is not guranteed that there are no reference holders to blkcg?Yeah, and that would wait till all css refs held by blkgs are gone, right?
Ok, I missed that. So ->destroy() is not called till all the css refs are gone. So directory removal will wait for all the pending IO in the group to finish.
I have some minor concerns here. A low prio group might not be able to dispatch IO for quite some amount of time. Especially with CFQ, in presence of sync IO, async IO can be starved for a very long time. So if some task dumped bunch of low prio IO and exited and now we are removing the cgroup, this task might have to wait for significant amount of time. (I am worried about "hung task waited for 120 seconds kind of messages").
Also it is little unintutive to the user that why rmdir should be delayed.
Anyway, this is not a very strong concern. Just something to keep in mind.
Thanks
Vivek
Tejun Heo
maintainerPATCH 2/11 block: relocate elevator initialized test from blk_cleanup_queue() to blk_drain_queue()
In reply to Tejun Heoblk_cleanup_queue() may be called for a queue which doesn't have elevator initialized to skip invoking blk_drain_queue(). blk_drain_queue() will be used for other purposes too and may be called for such half-initialized queues from other paths. Move q->elevator test into blk_drain_queue().
--- block/blk-core.c | 16 +++++++++------- 1 files changed, 9 insertions(+), 7 deletions(-)
block/blk-core.c +9 -7
@@ -359,6 +359,13 @@ EXPORT_SYMBOL(blk_put_queue);*/ void blk_drain_queue (struct request_queue *q,bool drain_all) { +/* +* The caller might be trying to tear down @q before its elevator +* is initialized, in which case we don't want to call into it. +*/ +if (!q->elevator) +return ; +while (true) {bool drain = false;int i;
@@ -468,13 +475,8 @@ void blk_cleanup_queue(struct request_queue *q)spin_unlock_irq (lock);mutex_unlock (&q->sysfs_lock); -/* -* Drain all requests queued before DEAD marking. The caller might -* be trying to tear down @q before its elevator is initialized, in -* which case we don't want to call into draining. -*/ -if (q->elevator) -blk_drain_queue (q, true); +/* drain all requests queued before DEAD marking */ +blk_drain_queue (q, true);/* @q won't process any more request, flush async actions */ del_timer_sync (&q->backing_dev_info.laptop_mode_wb_timer);
Signature · 1 line
1.7.7.3
Re: [PATCH 02/11] block: relocate elevator initialized test from blk_cleanup_queue() to blk_drain_queue()
Quoted 17 lines · Tejun Heo
blk_cleanup_queue()may be called for a queue which doesn't have elevator initialized to skip invokingblk_drain_queue().blk_drain_queue()will be used for other purposes too and may be called for such half-initialized queues from other paths. Moveq->elevatortest intoblk_drain_queue().Signed-off-by Tejun Heo <[email protected]>Cc Vivek Goyal <[email protected]>--- block/blk-core.c | 16 +++++++++------- 1 files changed, 9 insertions(+), 7 deletions(-)block/blk-core.c +0
-0
block/blk-core.c EXPORT_SYMBOL(blk_put_queue); · line 368
Quoted 10 lines
block/blk-core.c +7
-0@@ -359,6 +359,13 @@ EXPORT_SYMBOL(blk_put_queue);*/ void blk_drain_queue (struct request_queue *q,bool drain_all) { +/* +* The caller might be trying to tear down @q before its elevator +* is initialized, in which case we don't want to call into it. +*/ +if (!q->elevator) +return ; +
Shouldn't blk_throtl_drain() be called irrespective of the fact whether elevator is initilialized or not? On bio based drivers, there will be no elevator but bios can still be throttled.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH 02/11] block: relocate elevator initialized test from blk_cleanup_queue() to blk_drain_queue()
Hello,
Quoted 7 lines · Vivek Goyal
Quoted 7 lines
block/blk-core.c +3
-0@@ -359,6 +359,13 @@ EXPORT_SYMBOL(blk_put_queue);*/ void blk_drain_queue (struct request_queue *q,bool drain_all) { +/* +* The caller might be trying to tear down @q before its elevator +* is initialized, in which case we don't want to call into it.
Quoted 8 lines
block/blk-core.c EXPORT_SYMBOL(blk_put_queue); · line 368
Quoted 4 lines
block/blk-core.c +4
-0+*/ +if (!q->elevator) +return ; +Shouldn't
blk_throtl_drain()be called irrespective of the fact whether elevator is initilialized or not? On bio based drivers, there will be no elevator but bios can still be throttled.
Hmmm... probably, but doesn't that mean the code is already broken for bio based drivers using throtl?
Thanks.
Signature · 1 line
tejun
Re: [PATCH 02/11] block: relocate elevator initialized test from blk_cleanup_queue() to blk_drain_queue()
Quoted 19 lines · Tejun Heo
Hello,
Quoted 7 lines · Vivek Goyal
Quoted 7 lines
block/blk-core.c +3
-0@@ -359,6 +359,13 @@ EXPORT_SYMBOL(blk_put_queue);*/ void blk_drain_queue (struct request_queue *q,bool drain_all) { +/* +* The caller might be trying to tear down @q before its elevator +* is initialized, in which case we don't want to call into it. Quoted 8 lines
block/blk-core.c EXPORT_SYMBOL(blk_put_queue); · line 368
Quoted 4 lines
block/blk-core.c +4
-0+*/ +if (!q->elevator) +return ; +Shouldn't
blk_throtl_drain()be called irrespective of the fact whether elevator is initilialized or not? On bio based drivers, there will be no elevator but bios can still be throttled.
Quoted 2 lines
Hmmm... probably, but doesn't that mean the code is already broken for bio based drivers using throtl?
Yes looks like it is already broken for bio based drivers. So it is a fix independent of this patch/patch series.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH 02/11] block: relocate elevator initialized test from blk_cleanup_queue() to blk_drain_queue()
Quoted 5 lines · Vivek Goyal
Quoted 2 lines
Hmmm... probably, but doesn't that mean the code is already broken for bio based drivers using throtl?
Yes looks like it is already broken for bio based drivers. So it is a fix independent of this patch/patch series.
Yeah, I'll write up a separate fix patch for mainline.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 3/11 blkcg: add blkcg_{init|drain|exit}_queue()
In reply to Tejun HeoCurrently block core calls directly into blk-throttle for init, drain and exit. This patch adds blkcg_{init|drain|exit}_queue() which wraps the blk-throttle functions. This is to give more control and visiblity to blkcg core layer for proper layering. Further patches will add logic common to blkcg policies to the functions.
While at it, collapse blk_throtl_release() into blk_throtl_exit(). There's no reason to keep them separate.
--- block/blk-cgroup.c | 42 ++++++++++++++++++++++++++++++++++++++++++ block/blk-cgroup.h | 7 +++++++ block/blk-core.c | 7 ++++--- block/blk-sysfs.c | 4 ++-- block/blk-throttle.c | 3 --- block/blk.h | 2 -- 6 files changed, 55 insertions(+), 10 deletions(-)
block/blk-cgroup.c +42 -0
@@ -20,6 +20,7 @@#include <linux/genhd.h> #include <linux/delay.h> #include "blk-cgroup.h" +#include "blk.h" #define MAX_KEY_LEN 100
@@ -1448,6 +1449,47 @@ done:return &blkcg->css; } +/** +* blkcg_init_queue - initialize blkcg part of request queue +* @q: request_queue to initialize +* +* Called from blk_alloc_queue_node(). Responsible for initializing blkcg +* part of new request_queue @q. +* +* RETURNS: +* 0 on success, -errno on failure. +*/ +int blkcg_init_queue (struct request_queue *q) +{ +might_sleep (); + +return blk_throtl_init (q); +} + +/** +* blkcg_drain_queue - drain blkcg part of request_queue +* @q: request_queue to drain +* +* Called from blk_drain_queue(). Responsible for draining blkcg part. +*/ +void blkcg_drain_queue (struct request_queue *q) +{ +lockdep_assert_held (q->queue_lock); + +blk_throtl_drain (q); +} + +/** +* blkcg_exit_queue - exit and release blkcg part of request_queue +* @q: request_queue being released +* +* Called from blk_release_queue(). Responsible for exiting blkcg part. +*/ +void blkcg_exit_queue (struct request_queue *q) +{ +blk_throtl_exit (q); +} +/* * We cannot support shared io contexts, as we have no mean to support * two tasks with the same ioc in two different groups without major rework
block/blk-cgroup.h +7 -0
@@ -215,6 +215,10 @@ struct blkio_policy_type {enum blkio_policy_id plid; }; +extern int blkcg_init_queue (struct request_queue *q); +extern void blkcg_drain_queue (struct request_queue *q); +extern void blkcg_exit_queue (struct request_queue *q); +/* Blkio controller policy registration */ extern void blkio_policy_register (struct blkio_policy_type *);extern void blkio_policy_unregister (struct blkio_policy_type *);
@@ -233,6 +237,9 @@ struct blkio_group {struct blkio_policy_type { }; +static inline int blkcg_init_queue (struct request_queue *q) {return 0 ; } +static inline void blkcg_drain_queue (struct request_queue *q) { } +static inline void blkcg_exit_queue (struct request_queue *q) { }static inline void blkio_policy_register (struct blkio_policy_type *blkiop) { }static inline void blkio_policy_unregister (struct blkio_policy_type *blkiop) { }static inline void blkg_destroy_all (struct request_queue *q) { }
block/blk-core.c +4 -3
@@ -34,6 +34,7 @@#include <trace/events/block.h> #include "blk.h" +#include "blk-cgroup.h" EXPORT_TRACEPOINT_SYMBOL_GPL (block_bio_remap);EXPORT_TRACEPOINT_SYMBOL_GPL (block_rq_remap);
@@ -280,7 +281,7 @@ EXPORT_SYMBOL(blk_stop_queue);* * This function does not cancel any asynchronous activity arising * out of elevator or throttling code. That would require elevaotor_exit() -* and blk_throtl_exit() to be called with queue lock initialized. +* and blkcg_exit_queue() to be called with queue lock initialized. * */ void blk_sync_queue (struct request_queue *q)
@@ -373,7 +374,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)spin_lock_irq (q->queue_lock);elv_drain_elevator (q); -blk_throtl_drain (q); +blkcg_drain_queue (q);/* * This function might be called on a queue which failed
@@ -561,7 +562,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)*/ q->queue_lock = &q->__queue_lock; -if (blk_throtl_init (q)) +if (blkcg_init_queue (q)) goto fail_id;return q;
block/blk-sysfs.c +2 -2
@@ -9,6 +9,7 @@#include <linux/blktrace_api.h> #include "blk.h" +#include "blk-cgroup.h" struct queue_sysfs_entry {struct attribute attr;
@@ -486,7 +487,7 @@ static void blk_release_queue(struct kobject *kobj)elevator_exit (q->elevator); } -blk_throtl_exit (q); +blkcg_exit_queue (q);if (rl->rq_pool)mempool_destroy (rl->rq_pool);
@@ -494,7 +495,6 @@ static void blk_release_queue(struct kobject *kobj)if (q->queue_tags)__blk_queue_free_tags (q); -blk_throtl_release (q);blk_trace_shutdown (q);bdi_destroy (&q->backing_dev_info);
block/blk-throttle.c +0 -3
@@ -1226,10 +1226,7 @@ void blk_throtl_exit(struct request_queue *q)* it. */ throtl_shutdown_wq (q); -} -void blk_throtl_release (struct request_queue *q) -{kfree (q->td); }
block/blk.h +0 -2
@@ -234,7 +234,6 @@ extern bool blk_throtl_bio(struct request_queue *q, struct bio *bio);extern void blk_throtl_drain (struct request_queue *q);extern int blk_throtl_init (struct request_queue *q);extern void blk_throtl_exit (struct request_queue *q); -extern void blk_throtl_release (struct request_queue *q);#else /* CONFIG_BLK_DEV_THROTTLING */ static inline bool blk_throtl_bio (struct request_queue *q,struct bio *bio) {
@@ -243,7 +242,6 @@ static inline bool blk_throtl_bio(struct request_queue *q, struct bio *bio)static inline void blk_throtl_drain (struct request_queue *q) { }static inline int blk_throtl_init (struct request_queue *q) {return 0 ; }static inline void blk_throtl_exit (struct request_queue *q) { } -static inline void blk_throtl_release (struct request_queue *q) { }#endif /* CONFIG_BLK_DEV_THROTTLING */ #endif /* BLK_INTERNAL_H */
Signature · 1 line
1.7.7.3
Tejun Heo
maintainerPATCH 4/11 blkcg: clear all request_queues on blkcg policy [un]registrations
In reply to Tejun HeoKeep track of all request_queues which have blkcg initialized and turn on bypass and invoke blkcg_clear_queue() on all before making changes to blkcg policies.
This is to prepare for moving blkg management into blkcg core. Note that this uses more brute force than necessary. Finer grained shoot down will be implemented later and given that policy [un]registration almost never happens on running systems (blk-throtl can't be built as a module and cfq usually is the builtin default iosched), this shouldn't be a problem for the time being.
--- block/blk-cgroup.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- include/linux/blkdev.h | 3 +++ 2 files changed, 50 insertions(+), 1 deletions(-)
block/blk-cgroup.c +47 -1
@@ -27,6 +27,9 @@static DEFINE_SPINLOCK (blkio_list_lock);static LIST_HEAD (blkio_list); +static DEFINE_MUTEX (all_q_mutex); +static LIST_HEAD (all_q_list); +struct blkio_cgroup blkio_root_cgroup = { .weight =2 *BLKIO_WEIGHT_DEFAULT };EXPORT_SYMBOL_GPL (blkio_root_cgroup);
@@ -1461,9 +1464,20 @@ done:*/ int blkcg_init_queue (struct request_queue *q) { +int ret; +might_sleep (); -return blk_throtl_init (q); + ret =blk_throtl_init (q); +if (ret) +return ret; + +mutex_lock (&all_q_mutex); +INIT_LIST_HEAD (&q->all_q_node); +list_add_tail (&q->all_q_node, &all_q_list); +mutex_unlock (&all_q_mutex); + +return 0 ; }/**
@@ -1487,6 +1501,10 @@ void blkcg_drain_queue(struct request_queue *q)*/ void blkcg_exit_queue (struct request_queue *q) { +mutex_lock (&all_q_mutex); +list_del_init (&q->all_q_node); +mutex_unlock (&all_q_mutex); +blk_throtl_exit (q); }
@@ -1532,8 +1550,33 @@ static void blkiocg_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, } } +static void blkcg_bypass_start (void ) + __acquires(&all_q_mutex) +{ +struct request_queue *q; + +mutex_lock (&all_q_mutex); + +list_for_each_entry (q, &all_q_list, all_q_node) { +blk_queue_bypass_start (q); +blkg_destroy_all (q); + } +} + +static void blkcg_bypass_end (void ) + __releases(&all_q_mutex) +{ +struct request_queue *q; + +list_for_each_entry (q, &all_q_list, all_q_node) +blk_queue_bypass_end (q); + +mutex_unlock (&all_q_mutex); +} +void blkio_policy_register (struct blkio_policy_type *blkiop) { +blkcg_bypass_start ();spin_lock (&blkio_list_lock);BUG_ON (blkio_policy[blkiop->plid]);
@@ -1541,11 +1584,13 @@ void blkio_policy_register(struct blkio_policy_type *blkiop)list_add_tail (&blkiop->list, &blkio_list);spin_unlock (&blkio_list_lock); +blkcg_bypass_end (); }EXPORT_SYMBOL_GPL (blkio_policy_register);void blkio_policy_unregister (struct blkio_policy_type *blkiop) { +blkcg_bypass_start ();spin_lock (&blkio_list_lock);BUG_ON (blkio_policy[blkiop->plid] != blkiop);
@@ -1553,5 +1598,6 @@ void blkio_policy_unregister(struct blkio_policy_type *blkiop)list_del_init (&blkiop->list);spin_unlock (&blkio_list_lock); +blkcg_bypass_end (); }EXPORT_SYMBOL_GPL (blkio_policy_unregister);
include/linux/blkdev.h +3 -0
@@ -397,6 +397,9 @@ struct request_queue {struct bsg_class_device bsg_dev;#endif +#ifdef CONFIG_BLK_CGROUP +struct list_head all_q_node; +#endif #ifdef CONFIG_BLK_DEV_THROTTLING /* Throttle data */ struct throtl_data *td;
Signature · 1 line
1.7.7.3
Tejun Heo
maintainerPATCH 5/11 blkcg: let blkcg core handle policy private data allocation
In reply to Tejun HeoCurrently, blkg's are embedded in private data blkcg policy private data structure and thus allocated and freed by policies. This leads to duplicate codes in policies, hinders implementing common part in blkcg core with strong semantics, and forces duplicate blkg's for the same cgroup-q association.
This patch introduces struct blkg_policy_data which is a separate data structure chained from blkg. Policies specifies the amount of private data it needs in its blkio_policy_type->pdata_size and blkcg core takes care of allocating them along with blkg which can be accessed using blkg_to_pdata(). blkg can be determined from pdata using pdata_to_blkg(). blkio_alloc_group_fn() method is accordingly updated to blkio_init_group_fn().
For consistency, tg_of_blkg() and cfqg_of_blkg() are replaced with blkg_to_tg() and blkg_to_cfqg() respectively, and functions to map in the reverse direction are added.
Except that policy specific data now lives in a separate data structure from blkg, this patch doesn't introduce any functional difference.
This will be used to unify blkg's for different policies.
--- block/blk-cgroup.c | 86 +++++++++++++++++++++++++++++++++--------- block/blk-cgroup.h | 53 ++++++++++++++++++++++++- block/blk-throttle.c | 79 +++++++++++++++++++------------------- block/cfq-iosched.c | 102 +++++++++++++++++++++++++------------------------ 4 files changed, 209 insertions(+), 111 deletions(-)
block/blk-cgroup.c +67 -19
@@ -422,6 +422,70 @@ void blkiocg_update_io_merged_stats(struct blkio_group *blkg, bool direction, }EXPORT_SYMBOL_GPL (blkiocg_update_io_merged_stats); +/** +* blkg_free - free a blkg +* @blkg: blkg to free +* +* Free @blkg which may be partially allocated. +*/ +static void blkg_free (struct blkio_group *blkg) +{ +if (blkg) { +free_percpu (blkg->stats_cpu); +kfree (blkg->pd); +kfree (blkg); + } +} + +/** +* blkg_alloc - allocate a blkg +* @blkcg: block cgroup the new blkg is associated with +* @q: request_queue the new blkg is associated with +* @pol: policy the new blkg is associated with +* +* Allocate a new blkg assocating @blkcg and @q for @pol. +* +* FIXME: Should be called with queue locked but currently isn't due to +* percpu stat breakage. +*/ +static struct blkio_group *blkg_alloc (struct blkio_cgroup *blkcg, +struct request_queue *q, +struct blkio_policy_type *pol) +{ +struct blkio_group *blkg; + +/* alloc and init base part */ + blkg =kzalloc_node (sizeof (*blkg),GFP_ATOMIC , q->node); +if (!blkg) +return NULL ; + +spin_lock_init (&blkg->stats_lock); +rcu_assign_pointer (blkg->q, q); + blkg->blkcg = blkcg; + blkg->plid = pol->plid; +cgroup_path (blkcg->css.cgroup, blkg->path,sizeof (blkg->path)); + +/* alloc per-policy data */ + blkg->pd =kzalloc_node (sizeof (*blkg->pd) + pol->pdata_size,GFP_ATOMIC , + q->node); +if (!blkg->pd) { +blkg_free (blkg); +return NULL ; + } + +/* broken, read comment in the callsite */ + blkg->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); +if (!blkg->stats_cpu) { +blkg_free (blkg); +return NULL ; + } + +/* attach pd to blkg and invoke per-policy init */ + blkg->pd->blkg = blkg; + pol->ops.blkio_init_group_fn (blkg); +return blkg; +} + struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, struct request_queue *q, enum blkio_policy_id plid,
@@ -463,19 +527,7 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg,spin_unlock_irq (q->queue_lock);rcu_read_unlock (); - new_blkg = pol->ops.blkio_alloc_group_fn (q, blkcg); -if (new_blkg) { - new_blkg->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); - -spin_lock_init (&new_blkg->stats_lock); -rcu_assign_pointer (new_blkg->q, q); - new_blkg->blkcg = blkcg; - new_blkg->plid = plid; -cgroup_path (blkcg->css.cgroup, new_blkg->path, -sizeof (new_blkg->path)); - }else { -css_put (&blkcg->css); - } + new_blkg =blkg_alloc (blkcg, q, pol);rcu_read_lock ();spin_lock_irq (q->queue_lock);
@@ -492,7 +544,7 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, goto out;/* did alloc fail? */ -if (unlikely (!new_blkg || !new_blkg->stats_cpu)) { +if (unlikely (!new_blkg)) { blkg =ERR_PTR (-ENOMEM ); goto out; }
@@ -504,11 +556,7 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, pol->ops.blkio_link_group_fn (q, blkg);spin_unlock (&blkcg->lock); out: -if (new_blkg) { -free_percpu (new_blkg->stats_cpu); -kfree (new_blkg); -css_put (&blkcg->css); - } +blkg_free (new_blkg);return blkg; }EXPORT_SYMBOL_GPL (blkg_lookup_create);
block/blk-cgroup.h +50 -3
91 lines not on this page · raw message
block/blk-throttle.c +40 -39
226 lines not on this page · raw message
block/cfq-iosched.c +52 -50
292 lines not on this page · raw message
Signature · 1 line
1.7.7.3
Tejun Heo
maintainerPATCH 6/11 blkcg: move refcnt to blkcg core
In reply to Tejun HeoCurrently, blkcg policy implementations manage blkg refcnt duplicating mostly identical code in both policies. This patch moves refcnt to blkg and let blkcg core handle refcnt and freeing of blkgs.
* cfq blkgs now also get freed via RCU.
* cfq blkgs lose RB_EMPTY_ROOT() sanity check on blkg free. If necessary, we can add blkio_exit_group_fn() to resurrect this.
--- block/blk-cgroup.c | 24 ++++++++++++++++++++ block/blk-cgroup.h | 35 ++++++++++++++++++++++++++++++ block/blk-throttle.c | 58 +++---------------------------------------------- block/cfq-iosched.c | 58 ++++++++----------------------------------------- 4 files changed, 73 insertions(+), 102 deletions(-)
block/blk-cgroup.c +24 -0
@@ -463,6 +463,7 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg,rcu_assign_pointer (blkg->q, q); blkg->blkcg = blkcg; blkg->plid = pol->plid; + blkg->refcnt =1 ;cgroup_path (blkcg->css.cgroup, blkg->path,sizeof (blkg->path));/* alloc per-policy data */
@@ -633,6 +634,29 @@ void blkg_destroy_all(struct request_queue *q) } } +static void blkg_rcu_free (struct rcu_head *rcu_head) +{ +blkg_free (container_of(rcu_head ,struct blkio_group ,rcu_head )); +} + +void __blkg_release (struct blkio_group *blkg) +{ +/* release the extra blkcg reference this blkg has been holding */ +css_put (&blkg->blkcg->css); + +/* +* A group is freed in rcu manner. But having an rcu lock does not +* mean that one can access all the fields of blkg and assume these +* are valid. For example, don't try to follow throtl_data and +* request queue links. +* +* Having a reference to blkg under an rcu allows acess to only +* values local to groups like group stats and group rate limits +*/ +call_rcu (&blkg->rcu_head, blkg_rcu_free); +} +EXPORT_SYMBOL_GPL (__blkg_release); +static void blkio_reset_stats_cpu (struct blkio_group *blkg) {struct blkio_group_stats_cpu *stats_cpu;
block/blk-cgroup.h +35 -0
@@ -177,6 +177,8 @@ struct blkio_group {char path[128 ];/* policy which owns this blk group */ enum blkio_policy_id plid; +/* reference count */ +int refcnt;/* Configuration */ struct blkio_group_conf conf;
@@ -188,6 +190,8 @@ struct blkio_group {struct blkio_group_stats_cpu __percpu *stats_cpu;struct blkg_policy_data *pd; + +struct rcu_head rcu_head; };typedef void (blkio_init_group_fn )(struct blkio_group *blkg);
@@ -272,6 +276,35 @@ static inline char *blkg_path(struct blkio_group *blkg) return blkg->path; } +/** +* blkg_get - get a blkg reference +* @blkg: blkg to get +* +* The caller should be holding queue_lock and an existing reference. +*/ +static inline void blkg_get (struct blkio_group *blkg) +{ +lockdep_assert_held (blkg->q->queue_lock); +WARN_ON_ONCE (!blkg->refcnt); + blkg->refcnt++; +} + +void __blkg_release (struct blkio_group *blkg); + +/** +* blkg_put - put a blkg reference +* @blkg: blkg to put +* +* The caller should be holding queue_lock. +*/ +static inline void blkg_put (struct blkio_group *blkg) +{ +lockdep_assert_held (blkg->q->queue_lock); +WARN_ON_ONCE (blkg->refcnt <=0 ); +if (!--blkg->refcnt) +__blkg_release (blkg); +} +#else struct blkio_group {
@@ -292,6 +325,8 @@ static inline void *blkg_to_pdata(struct blkio_group *blkg,static inline struct blkio_group *pdata_to_blkg (void *pdata,struct blkio_policy_type *pol) {return NULL ; }static inline char *blkg_path (struct blkio_group *blkg) {return NULL ; } +static inline void blkg_get (struct blkio_group *blkg) { } +static inline void blkg_put (struct blkio_group *blkg) { }#endif
block/blk-throttle.c +4 -54
100 lines not on this page · raw message
block/cfq-iosched.c +10 -48
126 lines not on this page · raw message
Signature · 1 line
1.7.7.3
Re: [PATCH 06/11] blkcg: move refcnt to blkcg core
Quoted 5 lines · Tejun Heo
Currently, blkcg policy implementations manage blkg refcnt duplicating mostly identical code in both policies. This patch moves refcnt to blkg and let blkcg core handle refcnt and freeing of blkgs.
* cfq blkgs now also get freed via RCU.
This can lead to situation where cfq root group (policy data) is still around (yet to be freed after rcu perioed) but cfq has gone away (cfq_exit_queue() followed by cfq_exit()). Does it matter? If some future code is accessing cfqg under rcu, it can become a problem.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH 06/11] blkcg: move refcnt to blkcg core
Hello,
Quoted 11 lines · Vivek Goyal
Quoted 5 lines · Tejun Heo
Currently, blkcg policy implementations manage blkg refcnt duplicating mostly identical code in both policies. This patch moves refcnt to blkg and let blkcg core handle refcnt and freeing of blkgs.
* cfq blkgs now also get freed via RCU.
This can lead to situation where cfq root group (policy data) is still around (yet to be freed after rcu perioed) but cfq has gone away (
cfq_exit_queue()followed bycfq_exit()). Does it matter? If some future code is accessing cfqg under rcu, it can become a problem.
If RCU is being used, the scope of access allowed under it should be defined strictly. It's basically adding another layer of locking and like any other locking, if the boundary isn't clearly defined, it's gonna cause a problem. For blkcg, from the current usage, it's enough to guarantee access to blkg proper including policy specific data and under that scope, it doesn't matter whether request or cgroup goes away.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 7/11 blkcg: make blkg->pd an array and move configuration and stats into it
In reply to Tejun HeoTo prepare for unifying blkgs for different policies, make blkg->pd an array with BLKIO_NR_POLICIES elements and move blkg->conf, ->stats, and ->stats_cpu into blkg_policy_data.
This patch doesn't introduce any functional difference.
--- block/blk-cgroup.c | 150 ++++++++++++++++++++++++++++++++-------------------- block/blk-cgroup.h | 18 +++--- 2 files changed, 102 insertions(+), 66 deletions(-)
block/blk-cgroup.c +93 -57
@@ -184,12 +184,14 @@ static void blkio_check_and_dec_stat(uint64_t *stat, bool direction, bool sync)static void blkio_set_start_group_wait_time (struct blkio_group *blkg,struct blkio_group *curr_blkg) { -if (blkio_blkg_waiting (&blkg->stats)) +struct blkg_policy_data *pd = blkg->pd[blkg->plid]; + +if (blkio_blkg_waiting (&pd->stats))return ;if (blkg == curr_blkg)return ; - blkg->stats.start_group_wait_time =sched_clock (); -blkio_mark_blkg_waiting (&blkg->stats); + pd->stats.start_group_wait_time =sched_clock (); +blkio_mark_blkg_waiting (&pd->stats); }/* This should be called with the blkg->stats_lock held. */
@@ -222,24 +224,26 @@ static void blkio_end_empty_time(struct blkio_group_stats *stats)void blkiocg_update_set_idle_time_stats (struct blkio_group *blkg) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];unsigned long flags;spin_lock_irqsave (&blkg->stats_lock, flags); -BUG_ON (blkio_blkg_idling (&blkg->stats)); - blkg->stats.start_idle_time =sched_clock (); -blkio_mark_blkg_idling (&blkg->stats); +BUG_ON (blkio_blkg_idling (&pd->stats)); + pd->stats.start_idle_time =sched_clock (); +blkio_mark_blkg_idling (&pd->stats);spin_unlock_irqrestore (&blkg->stats_lock, flags); }EXPORT_SYMBOL_GPL (blkiocg_update_set_idle_time_stats);void blkiocg_update_idle_time_stats (struct blkio_group *blkg) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];unsigned long flags;unsigned long long now;struct blkio_group_stats *stats;spin_lock_irqsave (&blkg->stats_lock, flags); - stats = &blkg->stats; + stats = &pd->stats;if (blkio_blkg_idling (stats)) { now =sched_clock ();if (time_after64 (now, stats->start_idle_time))
@@ -252,11 +256,12 @@ EXPORT_SYMBOL_GPL(blkiocg_update_idle_time_stats);void blkiocg_update_avg_queue_size_stats (struct blkio_group *blkg) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];unsigned long flags;struct blkio_group_stats *stats;spin_lock_irqsave (&blkg->stats_lock, flags); - stats = &blkg->stats; + stats = &pd->stats; stats->avg_queue_size_sum += stats->stat_arr[BLKIO_STAT_QUEUED ][BLKIO_STAT_READ ] + stats->stat_arr[BLKIO_STAT_QUEUED ][BLKIO_STAT_WRITE ];
@@ -268,11 +273,12 @@ EXPORT_SYMBOL_GPL(blkiocg_update_avg_queue_size_stats);void blkiocg_set_start_empty_time (struct blkio_group *blkg) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];unsigned long flags;struct blkio_group_stats *stats;spin_lock_irqsave (&blkg->stats_lock, flags); - stats = &blkg->stats; + stats = &pd->stats;if (stats->stat_arr[BLKIO_STAT_QUEUED ][BLKIO_STAT_READ ] || stats->stat_arr[BLKIO_STAT_QUEUED ][BLKIO_STAT_WRITE ]) {
@@ -299,7 +305,9 @@ EXPORT_SYMBOL_GPL(blkiocg_set_start_empty_time);void blkiocg_update_dequeue_stats (struct blkio_group *blkg,unsigned long dequeue) { - blkg->stats.dequeue += dequeue; +struct blkg_policy_data *pd = blkg->pd[blkg->plid]; + + pd->stats.dequeue += dequeue; }EXPORT_SYMBOL_GPL (blkiocg_update_dequeue_stats);#else
@@ -312,12 +320,13 @@ void blkiocg_update_io_add_stats(struct blkio_group *blkg, struct blkio_group *curr_blkg, bool direction, bool sync) { + struct blkg_policy_data *pd = blkg->pd[blkg->plid];unsigned long flags;spin_lock_irqsave (&blkg->stats_lock, flags); -blkio_add_stat (blkg->stats.stat_arr[BLKIO_STAT_QUEUED ],1 , direction, +blkio_add_stat (pd->stats.stat_arr[BLKIO_STAT_QUEUED ],1 , direction, sync); -blkio_end_empty_time (&blkg->stats); +blkio_end_empty_time (&pd->stats);blkio_set_start_group_wait_time (blkg, curr_blkg);spin_unlock_irqrestore (&blkg->stats_lock, flags); }
@@ -326,10 +335,11 @@ EXPORT_SYMBOL_GPL(blkiocg_update_io_add_stats);void blkiocg_update_io_remove_stats (struct blkio_group *blkg,bool direction,bool sync) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];unsigned long flags;spin_lock_irqsave (&blkg->stats_lock, flags); -blkio_check_and_dec_stat (blkg->stats.stat_arr[BLKIO_STAT_QUEUED ], +blkio_check_and_dec_stat (pd->stats.stat_arr[BLKIO_STAT_QUEUED ], direction, sync);spin_unlock_irqrestore (&blkg->stats_lock, flags); }
@@ -338,12 +348,13 @@ EXPORT_SYMBOL_GPL(blkiocg_update_io_remove_stats);void blkiocg_update_timeslice_used (struct blkio_group *blkg,unsigned long time,unsigned long unaccounted_time) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];unsigned long flags;spin_lock_irqsave (&blkg->stats_lock, flags); - blkg->stats.time += time; + pd->stats.time += time;#ifdef CONFIG_DEBUG_BLK_CGROUP - blkg->stats.unaccounted_time += unaccounted_time; + pd->stats.unaccounted_time += unaccounted_time;#endif spin_unlock_irqrestore (&blkg->stats_lock, flags); }
@@ -356,6 +367,7 @@ EXPORT_SYMBOL_GPL(blkiocg_update_timeslice_used);void blkiocg_update_dispatch_stats (struct blkio_group *blkg,uint64_t bytes,bool direction,bool sync) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];struct blkio_group_stats_cpu *stats_cpu;unsigned long flags;
@@ -366,7 +378,7 @@ void blkiocg_update_dispatch_stats(struct blkio_group *blkg,*/ local_irq_save (flags); - stats_cpu =this_cpu_ptr (blkg->stats_cpu); + stats_cpu =this_cpu_ptr (pd->stats_cpu);u64_stats_update_begin (&stats_cpu->syncp); stats_cpu->sectors += bytes >>9 ;
@@ -382,12 +394,13 @@ EXPORT_SYMBOL_GPL(blkiocg_update_dispatch_stats);void blkiocg_update_completion_stats (struct blkio_group *blkg,uint64_t start_time,uint64_t io_start_time,bool direction,bool sync) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];struct blkio_group_stats *stats;unsigned long flags;unsigned long long now =sched_clock ();spin_lock_irqsave (&blkg->stats_lock, flags); - stats = &blkg->stats; + stats = &pd->stats;if (time_after64 (now, io_start_time))blkio_add_stat (stats->stat_arr[BLKIO_STAT_SERVICE_TIME ], now - io_start_time, direction, sync);
@@ -402,6 +415,7 @@ EXPORT_SYMBOL_GPL(blkiocg_update_completion_stats);void blkiocg_update_io_merged_stats (struct blkio_group *blkg,bool direction,bool sync) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];struct blkio_group_stats_cpu *stats_cpu;unsigned long flags;
@@ -412,7 +426,7 @@ void blkiocg_update_io_merged_stats(struct blkio_group *blkg, bool direction,*/ local_irq_save (flags); - stats_cpu =this_cpu_ptr (blkg->stats_cpu); + stats_cpu =this_cpu_ptr (pd->stats_cpu);u64_stats_update_begin (&stats_cpu->syncp); blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_MERGED], 1,
@@ -430,11 +444,17 @@ EXPORT_SYMBOL_GPL(blkiocg_update_io_merged_stats);*/ static void blkg_free (struct blkio_group *blkg) { -if (blkg) { -free_percpu (blkg->stats_cpu); -kfree (blkg->pd); -kfree (blkg); +struct blkg_policy_data *pd; + +if (!blkg) +return ; + + pd = blkg->pd[blkg->plid]; +if (pd) { +free_percpu (pd->stats_cpu); +kfree (pd); } +kfree (blkg); }/**
@@ -453,6 +473,7 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, struct blkio_policy_type *pol) {struct blkio_group *blkg; +struct blkg_policy_data *pd;/* alloc and init base part */ blkg =kzalloc_node (sizeof (*blkg),GFP_ATOMIC , q->node);
@@ -466,23 +487,26 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, blkg->refcnt =1 ;cgroup_path (blkcg->css.cgroup, blkg->path,sizeof (blkg->path)); -/* alloc per-policy data */ - blkg->pd =kzalloc_node (sizeof (*blkg->pd) + pol->pdata_size,GFP_ATOMIC , - q->node); -if (!blkg->pd) { +/* alloc per-policy data and attach it to blkg */ + pd =kzalloc_node (sizeof (*pd) + pol->pdata_size,GFP_ATOMIC , + q->node); +if (!pd) {blkg_free (blkg);return NULL ; } + blkg->pd[pol->plid] = pd; + pd->blkg = blkg; +/* broken, read comment in the callsite */ - blkg->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); -if (!blkg->stats_cpu) { + + pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); +if (!pd->stats_cpu) {blkg_free (blkg);return NULL ; } -/* attach pd to blkg and invoke per-policy init */ - blkg->pd->blkg = blkg; +/* invoke per-policy init */ pol->ops.blkio_init_group_fn (blkg);return blkg; }
@@ -659,6 +683,7 @@ EXPORT_SYMBOL_GPL(__blkg_release);static void blkio_reset_stats_cpu (struct blkio_group *blkg) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];struct blkio_group_stats_cpu *stats_cpu;int i, j, k;/*
@@ -673,7 +698,7 @@ static void blkio_reset_stats_cpu(struct blkio_group *blkg)* unless this becomes a real issue. */ for_each_possible_cpu (i) { - stats_cpu =per_cpu_ptr (blkg->stats_cpu, i); + stats_cpu =per_cpu_ptr (pd->stats_cpu, i); stats_cpu->sectors =0 ;for (j =0 ; j <BLKIO_STAT_CPU_NR ; j++)for (k =0 ; k <BLKIO_STAT_TOTAL ; k++)
@@ -698,8 +723,10 @@ blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) blkcg =cgroup_to_blkio_cgroup (cgroup);spin_lock_irq (&blkcg->lock);hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +spin_lock (&blkg->stats_lock); - stats = &blkg->stats; + stats = &pd->stats;#ifdef CONFIG_DEBUG_BLK_CGROUP idling =blkio_blkg_idling (stats); waiting =blkio_blkg_waiting (stats);
@@ -779,13 +806,14 @@ static uint64_t blkio_fill_stat(char *str, int chars_left, uint64_t val,static uint64_t blkio_read_stat_cpu (struct blkio_group *blkg,enum stat_type_cpu type,enum stat_sub_type sub_type) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];int cpu;struct blkio_group_stats_cpu *stats_cpu;u64 val =0 , tval;for_each_possible_cpu (cpu) {unsigned int start; - stats_cpu =per_cpu_ptr (blkg->stats_cpu, cpu); + stats_cpu =per_cpu_ptr (pd->stats_cpu, cpu); do { start = u64_stats_fetch_begin(&stats_cpu->syncp);
@@ -837,20 +865,21 @@ static uint64_t blkio_get_stat(struct blkio_group *blkg, struct cgroup_map_cb *cb, const char *dname, enum stat_type type) { + struct blkg_policy_data *pd = blkg->pd[blkg->plid];uint64_t disk_total;char key_str[MAX_KEY_LEN ];enum stat_sub_type sub_type;if (type ==BLKIO_STAT_TIME )return blkio_fill_stat (key_str,MAX_KEY_LEN -1 , - blkg->stats.time, cb, dname); + pd->stats.time, cb, dname); #ifdef CONFIG_DEBUG_BLK_CGROUP if (type == BLKIO_STAT_UNACCOUNTED_TIME) return blkio_fill_stat(key_str, MAX_KEY_LEN - 1, - blkg->stats.unaccounted_time, cb, dname); + pd->stats.unaccounted_time, cb, dname); if (type == BLKIO_STAT_AVG_QUEUE_SIZE) { - uint64_t sum = blkg->stats.avg_queue_size_sum; - uint64_t samples = blkg->stats.avg_queue_size_samples; + uint64_t sum = pd->stats.avg_queue_size_sum; + uint64_t samples = pd->stats.avg_queue_size_samples; if (samples) do_div(sum, samples); else
@@ -860,26 +889,26 @@ static uint64_t blkio_get_stat(struct blkio_group *blkg, }if (type ==BLKIO_STAT_GROUP_WAIT_TIME )return blkio_fill_stat (key_str,MAX_KEY_LEN -1 , - blkg->stats.group_wait_time, cb, dname); + pd->stats.group_wait_time, cb, dname);if (type ==BLKIO_STAT_IDLE_TIME )return blkio_fill_stat (key_str,MAX_KEY_LEN -1 , - blkg->stats.idle_time, cb, dname); + pd->stats.idle_time, cb, dname);if (type ==BLKIO_STAT_EMPTY_TIME )return blkio_fill_stat (key_str,MAX_KEY_LEN -1 , - blkg->stats.empty_time, cb, dname); + pd->stats.empty_time, cb, dname);if (type ==BLKIO_STAT_DEQUEUE )return blkio_fill_stat (key_str,MAX_KEY_LEN -1 , - blkg->stats.dequeue, cb, dname); + pd->stats.dequeue, cb, dname);#endif for (sub_type =BLKIO_STAT_READ ; sub_type <BLKIO_STAT_TOTAL ; sub_type++) {blkio_get_key_name (sub_type, dname, key_str,MAX_KEY_LEN , false); - cb->fill (cb, key_str, blkg->stats.stat_arr[type][sub_type]); + cb->fill (cb, key_str, pd->stats.stat_arr[type][sub_type]); } - disk_total = blkg->stats.stat_arr[type][BLKIO_STAT_READ ] + - blkg->stats.stat_arr[type][BLKIO_STAT_WRITE ]; + disk_total = pd->stats.stat_arr[type][BLKIO_STAT_READ ] + + pd->stats.stat_arr[type][BLKIO_STAT_WRITE ];blkio_get_key_name (BLKIO_STAT_TOTAL , dname, key_str,MAX_KEY_LEN , false); cb->fill (cb, key_str, disk_total);
@@ -891,6 +920,7 @@ static int blkio_policy_parse_and_set(char *buf, enum blkio_policy_id plid, {struct gendisk *disk =NULL ;struct blkio_group *blkg =NULL ; +struct blkg_policy_data *pd;char *s[4 ], *p, *major_s =NULL , *minor_s =NULL ;unsigned long major, minor;int i =0 , ret = -EINVAL ;
@@ -950,35 +980,37 @@ static int blkio_policy_parse_and_set(char *buf, enum blkio_policy_id plid, goto out_unlock; } + pd = blkg->pd[plid]; +switch (plid) {case BLKIO_POLICY_PROP :if ((temp <BLKIO_WEIGHT_MIN && temp >0 ) || temp >BLKIO_WEIGHT_MAX ) goto out_unlock; - blkg->conf.weight = temp; + pd->conf.weight = temp;blkio_update_group_weight (blkg, temp ?: blkcg->weight);break ;case BLKIO_POLICY_THROTL :switch (fileid) {case BLKIO_THROTL_read_bps_device: - blkg->conf.bps[READ ] = temp; + pd->conf.bps[READ ] = temp;blkio_update_group_bps (blkg, temp ?:-1 , fileid);break ;case BLKIO_THROTL_write_bps_device: - blkg->conf.bps[WRITE ] = temp; + pd->conf.bps[WRITE ] = temp;blkio_update_group_bps (blkg, temp ?:-1 , fileid);break ;case BLKIO_THROTL_read_iops_device:if (temp >THROTL_IOPS_MAX ) goto out_unlock; - blkg->conf.iops[READ ] = temp; + pd->conf.iops[READ ] = temp;blkio_update_group_iops (blkg, temp ?:-1 , fileid);break ;case BLKIO_THROTL_write_iops_device:if (temp >THROTL_IOPS_MAX ) goto out_unlock; - blkg->conf.iops[WRITE ] = temp; + pd->conf.iops[WRITE ] = temp;blkio_update_group_iops (blkg, temp ?:-1 , fileid);break ; }
@@ -1026,31 +1058,32 @@ static int blkiocg_file_write(struct cgroup *cgrp, struct cftype *cft,static void blkio_print_group_conf (struct cftype *cft,struct blkio_group *blkg,struct seq_file *m) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid];const char *dname =dev_name (blkg->q->backing_dev_info.dev);int fileid =BLKIOFILE_ATTR (cft->private);int rw =WRITE ;switch (blkg->plid) {case BLKIO_POLICY_PROP : -if (blkg->conf.weight) +if (pd->conf.weight)seq_printf (m,"%s\t%u\n" , - dname, blkg->conf.weight); + dname, pd->conf.weight);break ;case BLKIO_POLICY_THROTL :switch (fileid) {case BLKIO_THROTL_read_bps_device: rw =READ ;case BLKIO_THROTL_write_bps_device: -if (blkg->conf.bps[rw]) +if (pd->conf.bps[rw])seq_printf (m,"%s\t%llu\n" , - dname, blkg->conf.bps[rw]); + dname, pd->conf.bps[rw]);break ;case BLKIO_THROTL_read_iops_device: rw =READ ;case BLKIO_THROTL_write_iops_device: -if (blkg->conf.iops[rw]) +if (pd->conf.iops[rw])seq_printf (m,"%s\t%u\n" , - dname, blkg->conf.iops[rw]); + dname, pd->conf.iops[rw]);break ; }break ;
@@ -1232,9 +1265,12 @@ static int blkio_weight_write(struct blkio_cgroup *blkcg, int plid, u64 val)spin_lock_irq (&blkcg->lock); blkcg->weight = (unsigned int )val; -hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) -if (blkg->plid == plid && !blkg->conf.weight) +hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) { +struct blkg_policy_data *pd = blkg->pd[blkg->plid]; + +if (blkg->plid == plid && !pd->conf.weight)blkio_update_group_weight (blkg, blkcg->weight); + }spin_unlock_irq (&blkcg->lock);spin_unlock (&blkio_list_lock);
block/blk-cgroup.h +9 -9
@@ -164,6 +164,13 @@ struct blkg_policy_data {/* the blkg this per-policy data belongs to */ struct blkio_group *blkg; +/* Configuration */ +struct blkio_group_conf conf; + +struct blkio_group_stats stats; +/* Per cpu stats pointer */ +struct blkio_group_stats_cpu __percpu *stats_cpu; +/* pol->pdata_size bytes of private data used by policy impl */ char pdata[]__aligned(__alignof__(unsigned long long)) ; };
@@ -180,16 +187,9 @@ struct blkio_group {/* reference count */ int refcnt; -/* Configuration */ -struct blkio_group_conf conf; -/* Need to serialize the stats in the case of reset/update */ spinlock_t stats_lock; -struct blkio_group_stats stats; -/* Per cpu stats pointer */ -struct blkio_group_stats_cpu __percpu *stats_cpu; - -struct blkg_policy_data *pd; +struct blkg_policy_data *pd[BLKIO_NR_POLICIES ];struct rcu_head rcu_head; };
@@ -249,7 +249,7 @@ extern void blkg_destroy_all(struct request_queue *q);static inline void *blkg_to_pdata (struct blkio_group *blkg,struct blkio_policy_type *pol) { -return blkg ? blkg->pd->pdata :NULL ; +return blkg ? blkg->pd[pol->plid]->pdata :NULL ; }/**
Signature · 1 line
1.7.7.3
Tejun Heo
maintainerPATCH 8/11 blkcg: don't use blkg->plid in stat related functions
In reply to Tejun Heoblkg is scheduled to be unified for all policies and thus there won't be one-to-one mapping from blkg to policy. Update stat related functions to take explicit @pol or @plid arguments and not use blkg->plid.
This is painful for now but most of specific stat interface functions will be replaced with a handful of generic helpers.
--- block/blk-cgroup.c | 150 ++++++++++++++++++++++++++++---------------------- block/blk-cgroup.h | 80 +++++++++++++++++---------- block/blk-throttle.c | 4 +- block/cfq-iosched.c | 44 +++++++++----- block/cfq.h | 96 +++++++++++++++++++------------- 5 files changed, 224 insertions(+), 150 deletions(-)
block/blk-cgroup.c +85 -65
@@ -78,14 +78,14 @@ struct blkio_cgroup *task_blkio_cgroup(struct task_struct *tsk) }EXPORT_SYMBOL_GPL (task_blkio_cgroup); -static inline void -blkio_update_group_weight (struct blkio_group *blkg,unsigned int weight) +static inline void blkio_update_group_weight (struct blkio_group *blkg, +int plid,unsigned int weight) {struct blkio_policy_type *blkiop;list_for_each_entry (blkiop, &blkio_list, list) {/* If this policy does not own the blkg, do not send updates */ -if (blkiop->plid != blkg->plid) +if (blkiop->plid != plid)continue ;if (blkiop->ops.blkio_update_group_weight_fn) blkiop->ops.blkio_update_group_weight_fn(blkg->q,
@@ -93,15 +93,15 @@ blkio_update_group_weight(struct blkio_group *blkg, unsigned int weight) } } -static inline void blkio_update_group_bps (struct blkio_group *blkg,u64 bps, -int fileid) +static inline void blkio_update_group_bps (struct blkio_group *blkg,int plid, +u64 bps,int fileid) {struct blkio_policy_type *blkiop;list_for_each_entry (blkiop, &blkio_list, list) {/* If this policy does not own the blkg, do not send updates */ -if (blkiop->plid != blkg->plid) +if (blkiop->plid != plid)continue ; if (fileid == BLKIO_THROTL_read_bps_device
@@ -117,14 +117,15 @@ static inline void blkio_update_group_bps(struct blkio_group *blkg, u64 bps, }static inline void blkio_update_group_iops (struct blkio_group *blkg, -unsigned int iops,int fileid) +int plid,unsigned int iops, +int fileid) {struct blkio_policy_type *blkiop;list_for_each_entry (blkiop, &blkio_list, list) {/* If this policy does not own the blkg, do not send updates */ -if (blkiop->plid != blkg->plid) +if (blkiop->plid != plid)continue ; if (fileid == BLKIO_THROTL_read_iops_device
@@ -182,9 +183,10 @@ static void blkio_check_and_dec_stat(uint64_t *stat, bool direction, bool sync)#ifdef CONFIG_DEBUG_BLK_CGROUP /* This should be called with the blkg->stats_lock held. */ static void blkio_set_start_group_wait_time (struct blkio_group *blkg, -struct blkio_group *curr_blkg) +struct blkio_policy_type *pol, +struct blkio_group *curr_blkg) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];if (blkio_blkg_waiting (&pd->stats))return ;
@@ -222,9 +224,10 @@ static void blkio_end_empty_time(struct blkio_group_stats *stats)blkio_clear_blkg_empty (stats); } -void blkiocg_update_set_idle_time_stats (struct blkio_group *blkg) +void blkiocg_update_set_idle_time_stats (struct blkio_group *blkg, +struct blkio_policy_type *pol) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];unsigned long flags;spin_lock_irqsave (&blkg->stats_lock, flags);
@@ -235,9 +238,10 @@ void blkiocg_update_set_idle_time_stats(struct blkio_group *blkg) }EXPORT_SYMBOL_GPL (blkiocg_update_set_idle_time_stats); -void blkiocg_update_idle_time_stats (struct blkio_group *blkg) +void blkiocg_update_idle_time_stats (struct blkio_group *blkg, +struct blkio_policy_type *pol) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];unsigned long flags;unsigned long long now;struct blkio_group_stats *stats;
@@ -254,9 +258,10 @@ void blkiocg_update_idle_time_stats(struct blkio_group *blkg) }EXPORT_SYMBOL_GPL (blkiocg_update_idle_time_stats); -void blkiocg_update_avg_queue_size_stats (struct blkio_group *blkg) +void blkiocg_update_avg_queue_size_stats (struct blkio_group *blkg, +struct blkio_policy_type *pol) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];unsigned long flags;struct blkio_group_stats *stats;
@@ -271,9 +276,10 @@ void blkiocg_update_avg_queue_size_stats(struct blkio_group *blkg) }EXPORT_SYMBOL_GPL (blkiocg_update_avg_queue_size_stats); -void blkiocg_set_start_empty_time (struct blkio_group *blkg) +void blkiocg_set_start_empty_time (struct blkio_group *blkg, +struct blkio_policy_type *pol) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];unsigned long flags;struct blkio_group_stats *stats;
@@ -303,39 +309,43 @@ void blkiocg_set_start_empty_time(struct blkio_group *blkg)EXPORT_SYMBOL_GPL (blkiocg_set_start_empty_time);void blkiocg_update_dequeue_stats (struct blkio_group *blkg, -unsigned long dequeue) +struct blkio_policy_type *pol, +unsigned long dequeue) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid]; pd->stats.dequeue += dequeue; }EXPORT_SYMBOL_GPL (blkiocg_update_dequeue_stats);#else static inline void blkio_set_start_group_wait_time (struct blkio_group *blkg, -struct blkio_group *curr_blkg) {} -static inline void blkio_end_empty_time (struct blkio_group_stats *stats) {} +struct blkio_policy_type *pol, +struct blkio_group *curr_blkg) { } +static inline void blkio_end_empty_time (struct blkio_group_stats *stats) { }#endif void blkiocg_update_io_add_stats (struct blkio_group *blkg, -struct blkio_group *curr_blkg,bool direction, -bool sync) +struct blkio_policy_type *pol, +struct blkio_group *curr_blkg,bool direction, +bool sync) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];unsigned long flags;spin_lock_irqsave (&blkg->stats_lock, flags);blkio_add_stat (pd->stats.stat_arr[BLKIO_STAT_QUEUED ],1 , direction, sync);blkio_end_empty_time (&pd->stats); -blkio_set_start_group_wait_time (blkg, curr_blkg); +blkio_set_start_group_wait_time (blkg, pol, curr_blkg);spin_unlock_irqrestore (&blkg->stats_lock, flags); }EXPORT_SYMBOL_GPL (blkiocg_update_io_add_stats);void blkiocg_update_io_remove_stats (struct blkio_group *blkg, -bool direction,bool sync) +struct blkio_policy_type *pol, +bool direction,bool sync) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];unsigned long flags;spin_lock_irqsave (&blkg->stats_lock, flags);
@@ -345,10 +355,12 @@ void blkiocg_update_io_remove_stats(struct blkio_group *blkg, }EXPORT_SYMBOL_GPL (blkiocg_update_io_remove_stats); -void blkiocg_update_timeslice_used (struct blkio_group *blkg,unsigned long time, -unsigned long unaccounted_time) +void blkiocg_update_timeslice_used (struct blkio_group *blkg, +struct blkio_policy_type *pol, +unsigned long time, +unsigned long unaccounted_time) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];unsigned long flags;spin_lock_irqsave (&blkg->stats_lock, flags);
@@ -365,9 +377,10 @@ EXPORT_SYMBOL_GPL(blkiocg_update_timeslice_used);* is valid. */ void blkiocg_update_dispatch_stats (struct blkio_group *blkg, -uint64_t bytes,bool direction,bool sync) +struct blkio_policy_type *pol, +uint64_t bytes,bool direction,bool sync) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];struct blkio_group_stats_cpu *stats_cpu;unsigned long flags;
@@ -392,9 +405,12 @@ void blkiocg_update_dispatch_stats(struct blkio_group *blkg,EXPORT_SYMBOL_GPL (blkiocg_update_dispatch_stats);void blkiocg_update_completion_stats (struct blkio_group *blkg, -uint64_t start_time,uint64_t io_start_time,bool direction,bool sync) +struct blkio_policy_type *pol, +uint64_t start_time, +uint64_t io_start_time,bool direction, +bool sync) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];struct blkio_group_stats *stats;unsigned long flags;unsigned long long now =sched_clock ();
@@ -412,10 +428,11 @@ void blkiocg_update_completion_stats(struct blkio_group *blkg,EXPORT_SYMBOL_GPL (blkiocg_update_completion_stats);/* Merged stats are per cpu. */ -void blkiocg_update_io_merged_stats (struct blkio_group *blkg,bool direction, -bool sync) +void blkiocg_update_io_merged_stats (struct blkio_group *blkg, +struct blkio_policy_type *pol, +bool direction,bool sync) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[pol->plid];struct blkio_group_stats_cpu *stats_cpu;unsigned long flags;
@@ -681,9 +698,9 @@ void __blkg_release(struct blkio_group *blkg) }EXPORT_SYMBOL_GPL (__blkg_release); -static void blkio_reset_stats_cpu (struct blkio_group *blkg) +static void blkio_reset_stats_cpu (struct blkio_group *blkg,int plid) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[plid];struct blkio_group_stats_cpu *stats_cpu;int i, j, k;/*
@@ -754,7 +771,7 @@ blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val)spin_unlock (&blkg->stats_lock);/* Reset Per cpu stats which don't take blkg->stats_lock */ -blkio_reset_stats_cpu (blkg); +blkio_reset_stats_cpu (blkg, blkg->plid); }spin_unlock_irq (&blkcg->lock);
@@ -803,10 +820,10 @@ static uint64_t blkio_fill_stat(char *str, int chars_left, uint64_t val, } -static uint64_t blkio_read_stat_cpu (struct blkio_group *blkg, +static uint64_t blkio_read_stat_cpu (struct blkio_group *blkg,int plid,enum stat_type_cpu type,enum stat_sub_type sub_type) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[plid];int cpu;struct blkio_group_stats_cpu *stats_cpu;u64 val =0 , tval;
@@ -829,7 +846,7 @@ static uint64_t blkio_read_stat_cpu(struct blkio_group *blkg,return val; } -static uint64_t blkio_get_stat_cpu (struct blkio_group *blkg, +static uint64_t blkio_get_stat_cpu (struct blkio_group *blkg,int plid,struct cgroup_map_cb *cb,const char *dname,enum stat_type_cpu type) {
@@ -838,7 +855,7 @@ static uint64_t blkio_get_stat_cpu(struct blkio_group *blkg,enum stat_sub_type sub_type;if (type ==BLKIO_STAT_CPU_SECTORS ) { - val =blkio_read_stat_cpu (blkg, type,0 ); + val =blkio_read_stat_cpu (blkg, plid, type,0 );return blkio_fill_stat (key_str,MAX_KEY_LEN -1 , val, cb, dname); }
@@ -847,12 +864,12 @@ static uint64_t blkio_get_stat_cpu(struct blkio_group *blkg, sub_type++) {blkio_get_key_name (sub_type, dname, key_str,MAX_KEY_LEN , false); - val =blkio_read_stat_cpu (blkg, type, sub_type); + val =blkio_read_stat_cpu (blkg, plid, type, sub_type); cb->fill (cb, key_str, val); } - disk_total =blkio_read_stat_cpu (blkg, type,BLKIO_STAT_READ ) + -blkio_read_stat_cpu (blkg, type,BLKIO_STAT_WRITE ); + disk_total =blkio_read_stat_cpu (blkg, plid, type,BLKIO_STAT_READ ) + +blkio_read_stat_cpu (blkg, plid, type,BLKIO_STAT_WRITE );blkio_get_key_name (BLKIO_STAT_TOTAL , dname, key_str,MAX_KEY_LEN , false);
@@ -861,11 +878,11 @@ static uint64_t blkio_get_stat_cpu(struct blkio_group *blkg, }/* This should be called with blkg->stats_lock held */ -static uint64_t blkio_get_stat (struct blkio_group *blkg, +static uint64_t blkio_get_stat (struct blkio_group *blkg,int plid,struct cgroup_map_cb *cb,const char *dname,enum stat_type type) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[plid];uint64_t disk_total;char key_str[MAX_KEY_LEN ];enum stat_sub_type sub_type;
@@ -989,29 +1006,29 @@ static int blkio_policy_parse_and_set(char *buf, enum blkio_policy_id plid, goto out_unlock; pd->conf.weight = temp; -blkio_update_group_weight (blkg, temp ?: blkcg->weight); +blkio_update_group_weight (blkg, plid, temp ?: blkcg->weight);break ;case BLKIO_POLICY_THROTL :switch (fileid) {case BLKIO_THROTL_read_bps_device: pd->conf.bps[READ ] = temp; -blkio_update_group_bps (blkg, temp ?:-1 , fileid); +blkio_update_group_bps (blkg, plid, temp ?:-1 , fileid);break ;case BLKIO_THROTL_write_bps_device: pd->conf.bps[WRITE ] = temp; -blkio_update_group_bps (blkg, temp ?:-1 , fileid); +blkio_update_group_bps (blkg, plid, temp ?:-1 , fileid);break ;case BLKIO_THROTL_read_iops_device:if (temp >THROTL_IOPS_MAX ) goto out_unlock; pd->conf.iops[READ ] = temp; -blkio_update_group_iops (blkg, temp ?:-1 , fileid); +blkio_update_group_iops (blkg, plid, temp ?:-1 , fileid);break ;case BLKIO_THROTL_write_iops_device:if (temp >THROTL_IOPS_MAX ) goto out_unlock; pd->conf.iops[WRITE ] = temp; -blkio_update_group_iops (blkg, temp ?:-1 , fileid); +blkio_update_group_iops (blkg, plid, temp ?:-1 , fileid);break ; }break ;
@@ -1058,12 +1075,13 @@ static int blkiocg_file_write(struct cgroup *cgrp, struct cftype *cft,static void blkio_print_group_conf (struct cftype *cft,struct blkio_group *blkg,struct seq_file *m) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; -const char *dname =dev_name (blkg->q->backing_dev_info.dev); +int plid =BLKIOFILE_POLICY (cft->private);int fileid =BLKIOFILE_ATTR (cft->private); +struct blkg_policy_data *pd = blkg->pd[plid]; +const char *dname =dev_name (blkg->q->backing_dev_info.dev);int rw =WRITE ; -switch (blkg->plid) { +switch (plid) {case BLKIO_POLICY_PROP :if (pd->conf.weight) seq_printf(m, "%s\t%u\n",
@@ -1155,15 +1173,17 @@ static int blkio_read_blkg_stats(struct blkio_cgroup *blkcg,rcu_read_lock ();hlist_for_each_entry_rcu (blkg, n, &blkcg->blkg_list, blkcg_node) {const char *dname =dev_name (blkg->q->backing_dev_info.dev); +int plid =BLKIOFILE_POLICY (cft->private); -if (BLKIOFILE_POLICY (cft->private) != blkg->plid) +if (plid != blkg->plid)continue ; -if (pcpu) - cgroup_total +=blkio_get_stat_cpu (blkg, cb, dname, - type); -else { +if (pcpu) { + cgroup_total +=blkio_get_stat_cpu (blkg, plid, + cb, dname, type); + }else {spin_lock_irq (&blkg->stats_lock); - cgroup_total +=blkio_get_stat (blkg, cb, dname, type); + cgroup_total +=blkio_get_stat (blkg, plid, + cb, dname, type);spin_unlock_irq (&blkg->stats_lock); } }
@@ -1269,7 +1289,7 @@ static int blkio_weight_write(struct blkio_cgroup *blkcg, int plid, u64 val)struct blkg_policy_data *pd = blkg->pd[blkg->plid];if (blkg->plid == plid && !pd->conf.weight) -blkio_update_group_weight (blkg, blkcg->weight); +blkio_update_group_weight (blkg, plid, blkcg->weight); }spin_unlock_irq (&blkcg->lock);
block/blk-cgroup.h +50 -30
113 lines not on this page · raw message
block/blk-throttle.c +3 -1
@@ -588,7 +588,8 @@ static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio) tg->bytes_disp[rw] += bio->bi_size; tg->io_disp[rw]++; -blkiocg_update_dispatch_stats (tg_to_blkg (tg), bio->bi_size, rw, sync); +blkiocg_update_dispatch_stats (tg_to_blkg (tg), &blkio_policy_throtl, + bio->bi_size, rw, sync); } static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg,
@@ -1000,6 +1001,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)if (tg) {if (tg_no_rule_group (tg, rw)) {blkiocg_update_dispatch_stats (tg_to_blkg (tg), + &blkio_policy_throtl, bio->bi_size, rw,rw_is_sync (bio->bi_rw)); goto out_unlock_rcu;
block/cfq-iosched.c +28 -16
124 lines not on this page · raw message
block/cfq.h +58 -38
155 lines not on this page · raw message
Signature · 1 line
1.7.7.3
Tejun Heo
maintainerPATCH 9/11 blkcg: move per-queue blkg list heads and counters to queue and blkg
In reply to Tejun HeoCurrently, specific policy implementations are responsible for maintaining list and number of blkgs. This duplicates code unnecessarily, and hinders factoring common code and providing blkcg API with better defined semantics.
After this patch, request_queue hosts list heads and counters and blkg has list nodes for both policies. This patch only relocates the necessary fields and the next patch will actually move management code into blkcg core.
Note that request_queue->blkg_list[] and ->nr_blkgs[] are hardcoded to have 2 elements. This is to avoid include dependency and will be removed by the next patch.
This patch doesn't introduce any behavior change.
--- block/blk-cgroup.c | 2 + block/blk-cgroup.h | 1 + block/blk-core.c | 4 +++ block/blk-throttle.c | 49 ++++++++++++++++++++++------------------------- block/cfq-iosched.c | 46 +++++++++++++++++--------------------------- include/linux/blkdev.h | 5 ++++ 6 files changed, 53 insertions(+), 54 deletions(-)
block/blk-cgroup.c +2 -0
@@ -499,6 +499,8 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg,spin_lock_init (&blkg->stats_lock);rcu_assign_pointer (blkg->q, q); +INIT_LIST_HEAD (&blkg->q_node[0 ]); +INIT_LIST_HEAD (&blkg->q_node[1 ]); blkg->blkcg = blkcg; blkg->plid = pol->plid; blkg->refcnt =1 ;
block/blk-cgroup.h +1 -0
@@ -178,6 +178,7 @@ struct blkg_policy_data {struct blkio_group {/* Pointer to the associated request_queue, RCU protected */ struct request_queue __rcu *q; +struct list_head q_node[BLKIO_NR_POLICIES ];struct hlist_node blkcg_node;struct blkio_cgroup *blkcg;/* Store cgroup path */
block/blk-core.c +4 -0
@@ -546,6 +546,10 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)setup_timer (&q->timeout, blk_rq_timed_out_timer, (unsigned long ) q);INIT_LIST_HEAD (&q->timeout_list);INIT_LIST_HEAD (&q->icq_list); +#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE ) +INIT_LIST_HEAD (&q->blkg_list[0 ]); +INIT_LIST_HEAD (&q->blkg_list[1 ]); +#endif INIT_LIST_HEAD (&q->flush_queue[0 ]);INIT_LIST_HEAD (&q->flush_queue[1 ]);INIT_LIST_HEAD (&q->flush_data_in_flight);
block/blk-throttle.c +23 -26
129 lines not on this page · raw message
block/cfq-iosched.c +18 -28
98 lines not on this page · raw message
include/linux/blkdev.h +5 -0
@@ -362,6 +362,11 @@ struct request_queue {struct list_head timeout_list;struct list_head icq_list; +#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE ) +/* XXX: array size hardcoded to avoid include dependency (temporary) */ +struct list_head blkg_list[2 ]; +int nr_blkgs[2 ]; +#endif struct queue_limits limits;
Signature · 1 line
1.7.7.3
Re: [PATCH 09/11] blkcg: move per-queue blkg list heads and counters to queue and blkg
On Wed, Feb 01, 2012 at 01:19:14PM -0800, Tejun Heo wrote:
[..]
block/blk-core.c struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) · line 549
Quoted 9 lines
block/blk-core.c +1
-0@@ -546,6 +546,10 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)setup_timer (&q->timeout, blk_rq_timed_out_timer, (unsigned long ) q);INIT_LIST_HEAD (&q->timeout_list);INIT_LIST_HEAD (&q->icq_list); +#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE )
BLK_CGROUP_MODULE not required as you got rid of this possiblity in the first patch itself
block/blk-core.c struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) · line 551
Quoted 3 lines
block/blk-core.c +1
-0struct list_head icq_list; +#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE )
Ditto.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH 09/11] blkcg: move per-queue blkg list heads and counters to queue and blkg
Hello,
Quoted 17 lines · Vivek Goyal
On Wed, Feb 01, 2012 at 01:19:14PM -0800, Tejun Heo wrote:
[..]
block/blk-core.c struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) · line 549
Quoted 9 lines
block/blk-core.c +1
-0@@ -546,6 +546,10 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q); INIT_LIST_HEAD(&q->timeout_list); INIT_LIST_HEAD(&q->icq_list); +#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE )
BLK_CGROUP_MODULEnot required as you got rid of this possiblity in the first patch itselfQuoted 1 line
Quoted 4 lines
Quoted 2 lines
struct
list_headicq_list;
+#ifdefined(CONFIG_BLK_CGROUP)||defined(CONFIG_BLK_CGROUP_MODULE)Ditto.
Ooh, right. Will remove them. Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 10/11 blkcg: let blkcg core manage per-queue blkg list and counter
In reply to Tejun HeoWith the previous patch to move blkg list heads and counters to request_queue and blkg, logic to manage them in both policies are almost identical and can be moved to blkcg core.
This patch moves blkg link logic into blkg_lookup_create(), implements common blkg unlink code in blkg_destroy(), and updates blkg_destory_all() so that it's policy specific and can skip root group. The updated blkg_destroy_all() is now used to both clear queue for bypassing and elv switching, and release all blkgs on q exit.
This patch introduces a race window where policy [de]registration may race against queue blkg clearing. This can only be a problem on cfq unload and shouldn't be a real problem in practice (and we have many other places where this race already exists). Future patches will remove these unlikely races.
--- block/blk-cgroup.c | 72 ++++++++++++++++++++++++++++-------- block/blk-cgroup.h | 15 +++----- block/blk-throttle.c | 99 +------------------------------------------------- block/cfq-iosched.c | 98 +++----------------------------------------------- block/elevator.c | 5 ++- 5 files changed, 71 insertions(+), 218 deletions(-)
block/blk-cgroup.c +56 -16
@@ -596,8 +596,11 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg,/* insert */ spin_lock (&blkcg->lock);swap (blkg, new_blkg); +hlist_add_head_rcu (&blkg->blkcg_node, &blkcg->blkg_list); - pol->ops.blkio_link_group_fn (q, blkg); +list_add (&blkg->q_node[plid], &q->blkg_list[plid]); + q->nr_blkgs[plid]++; +spin_unlock (&blkcg->lock); out:blkg_free (new_blkg);
@@ -646,36 +649,69 @@ struct blkio_group *blkg_lookup(struct blkio_cgroup *blkcg, }EXPORT_SYMBOL_GPL (blkg_lookup); -void blkg_destroy_all (struct request_queue *q) +static void blkg_destroy (struct blkio_group *blkg,enum blkio_policy_id plid) +{ +struct request_queue *q = blkg->q; + +lockdep_assert_held (q->queue_lock); + +/* Something wrong if we are trying to remove same group twice */ +WARN_ON_ONCE (list_empty (&blkg->q_node[plid])); +list_del_init (&blkg->q_node[plid]); + +WARN_ON_ONCE (q->nr_blkgs[plid] <=0 ); + q->nr_blkgs[plid]--; + +/* +* Put the reference taken at the time of creation so that when all +* queues are gone, group can be destroyed. +*/ +blkg_put (blkg); +} + +void blkg_destroy_all (struct request_queue *q,enum blkio_policy_id plid, +bool destroy_root) { -struct blkio_policy_type *pol; +struct blkio_group *blkg, *n;while (true) {bool done = true; -spin_lock (&blkio_list_lock);spin_lock_irq (q->queue_lock); -/* -* clear_queue_fn() might return with non-empty group list -* if it raced cgroup removal and lost. cgroup removal is -* guaranteed to make forward progress and retrying after a -* while is enough. This ugliness is scheduled to be -* removed after locking update. -*/ -list_for_each_entry (pol, &blkio_list, list) -if (!pol->ops.blkio_clear_queue_fn (q)) +list_for_each_entry_safe (blkg, n, &q->blkg_list[plid], + q_node[plid]) { +/* skip root? */ +if (!destroy_root && blkg->blkcg == &blkio_root_cgroup) +continue ; + +/* +* If cgroup removal path got to blk_group first +* and removed it from cgroup list, then it will +* take care of destroying cfqg also. +*/ +if (!blkiocg_del_blkio_group (blkg)) +blkg_destroy (blkg, plid); +else done = false; + }spin_unlock_irq (q->queue_lock); -spin_unlock (&blkio_list_lock); +/* +* Group list may not be empty if we raced cgroup removal +* and lost. cgroup removal is guaranteed to make forward +* progress and retrying after a while is enough. This +* ugliness is scheduled to be removed after locking +* update. +*/ if (done)break ;msleep (10 );/* just some random duration I like */ } } +EXPORT_SYMBOL_GPL (blkg_destroy_all);static void blkg_rcu_free (struct rcu_head *rcu_head) {
@@ -1538,11 +1574,13 @@ static int blkiocg_pre_destroy(struct cgroup_subsys *subsys,* this event. */ spin_lock (&blkio_list_lock); +spin_lock_irqsave (q->queue_lock, flags);list_for_each_entry (blkiop, &blkio_list, list) {if (blkiop->plid != blkg->plid)continue ; - blkiop->ops.blkio_unlink_group_fn (q, blkg); +blkg_destroy (blkg, blkiop->plid); } +spin_unlock_irqrestore (q->queue_lock, flags);spin_unlock (&blkio_list_lock); }while (1 );
@@ -1684,12 +1722,14 @@ static void blkcg_bypass_start(void) __acquires(&all_q_mutex) {struct request_queue *q; +int i;mutex_lock (&all_q_mutex);list_for_each_entry (q, &all_q_list, all_q_node) {blk_queue_bypass_start (q); -blkg_destroy_all (q); +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) +blkg_destroy_all (q, i, false); } }
block/blk-cgroup.h +5 -10
@@ -196,11 +196,6 @@ struct blkio_group { };typedef void (blkio_init_group_fn )(struct blkio_group *blkg); -typedef void (blkio_link_group_fn )(struct request_queue *q, -struct blkio_group *blkg); -typedef void (blkio_unlink_group_fn )(struct request_queue *q, -struct blkio_group *blkg); -typedef bool (blkio_clear_queue_fn )(struct request_queue *q);typedef void (blkio_update_group_weight_fn )(struct request_queue *q,struct blkio_group *blkg,unsigned int weight); typedef void (blkio_update_group_read_bps_fn)(struct request_queue *q,
@@ -214,9 +209,6 @@ typedef void (blkio_update_group_write_iops_fn)(struct request_queue *q,struct blkio_policy_ops {blkio_init_group_fn *blkio_init_group_fn; -blkio_link_group_fn *blkio_link_group_fn; -blkio_unlink_group_fn *blkio_unlink_group_fn; -blkio_clear_queue_fn *blkio_clear_queue_fn;blkio_update_group_weight_fn *blkio_update_group_weight_fn;blkio_update_group_read_bps_fn *blkio_update_group_read_bps_fn;blkio_update_group_write_bps_fn *blkio_update_group_write_bps_fn;
@@ -238,7 +230,8 @@ extern void blkcg_exit_queue(struct request_queue *q);/* Blkio controller policy registration */ extern void blkio_policy_register (struct blkio_policy_type *);extern void blkio_policy_unregister (struct blkio_policy_type *); -extern void blkg_destroy_all (struct request_queue *q); +extern void blkg_destroy_all (struct request_queue *q, +enum blkio_policy_id plid,bool destroy_root);/** * blkg_to_pdata - get policy private data
@@ -319,7 +312,9 @@ static inline void blkcg_drain_queue(struct request_queue *q) { }static inline void blkcg_exit_queue (struct request_queue *q) { }static inline void blkio_policy_register (struct blkio_policy_type *blkiop) { }static inline void blkio_policy_unregister (struct blkio_policy_type *blkiop) { } -static inline void blkg_destroy_all (struct request_queue *q) { } +static inline void blkg_destroy_all (struct request_queue *q, +enum blkio_policy_id plid, +bool destory_root) { }static inline void *blkg_to_pdata (struct blkio_group *blkg,struct blkio_policy_type *pol) {return NULL ; }
block/blk-throttle.c +2 -97
126 lines not on this page · raw message
block/cfq-iosched.c +5 -93
134 lines not on this page · raw message
block/elevator.c +3 -2
@@ -923,7 +923,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) {struct elevator_queue *old = q->elevator;bool registered = old->registered; -int err; +int i, err;/* * Turn on BYPASS and drain all requests w/ elevator private data.
@@ -942,7 +942,8 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e)ioc_clear_queue (q);spin_unlock_irq (q->queue_lock); -blkg_destroy_all (q); +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) +blkg_destroy_all (q, i, false);/* allocate, init and register new elevator */ err = -ENOMEM ;
Signature · 1 line
1.7.7.3
Tejun Heo
maintainerPATCH 11/11 blkcg: unify blkg's for blkcg policies
In reply to Tejun HeoCurrently, blkg is per cgroup-queue-policy combination. This is unnatural and leads to various convolutions in partially used duplicate fields in blkg, config / stat access, and general management of blkgs.
This patch make blkg's per cgroup-queue and let them serve all policies. blkgs are now created and destroyed by blkcg core proper. This will allow further consolidation of common management logic into blkcg core and API with better defined semantics and layering.
As a transitional step to untangle blkg management, elvswitch and policy [de]registration, all blkgs except the root blkg are being shot down during elvswitch and bypass. This patch adds blkg_root_update() to update root blkg in place on policy change. This is hacky but should be good enough as interim step until we get locking simplified and switch over to in-place update for all blkgs.
--- block/blk-cgroup.c | 215 +++++++++++++++++++++++++++++------------------- block/blk-cgroup.h | 10 +-- block/blk-core.c | 3 +- block/blk-sysfs.c | 4 +- block/blk-throttle.c | 9 +-- block/cfq-iosched.c | 4 +- block/elevator.c | 5 +- include/linux/blkdev.h | 5 +- 8 files changed, 146 insertions(+), 109 deletions(-)
block/blk-cgroup.c +132 -83
@@ -461,16 +461,20 @@ EXPORT_SYMBOL_GPL(blkiocg_update_io_merged_stats);*/ static void blkg_free (struct blkio_group *blkg) { -struct blkg_policy_data *pd; +int i;if (!blkg)return ; - pd = blkg->pd[blkg->plid]; -if (pd) { -free_percpu (pd->stats_cpu); -kfree (pd); +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkg_policy_data *pd = blkg->pd[i]; + +if (pd) { +free_percpu (pd->stats_cpu); +kfree (pd); + } } +kfree (blkg); }
@@ -486,11 +490,10 @@ static void blkg_free(struct blkio_group *blkg)* percpu stat breakage. */ static struct blkio_group *blkg_alloc (struct blkio_cgroup *blkcg, -struct request_queue *q, -struct blkio_policy_type *pol) +struct request_queue *q) {struct blkio_group *blkg; -struct blkg_policy_data *pd; +int i;/* alloc and init base part */ blkg =kzalloc_node (sizeof (*blkg),GFP_ATOMIC , q->node);
@@ -499,34 +502,83 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg,spin_lock_init (&blkg->stats_lock);rcu_assign_pointer (blkg->q, q); -INIT_LIST_HEAD (&blkg->q_node[0 ]); -INIT_LIST_HEAD (&blkg->q_node[1 ]); +INIT_LIST_HEAD (&blkg->q_node); blkg->blkcg = blkcg; - blkg->plid = pol->plid; blkg->refcnt =1 ;cgroup_path (blkcg->css.cgroup, blkg->path,sizeof (blkg->path)); -/* alloc per-policy data and attach it to blkg */ - pd =kzalloc_node (sizeof (*pd) + pol->pdata_size,GFP_ATOMIC , - q->node); -if (!pd) { -blkg_free (blkg); -return NULL ; +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkio_policy_type *pol = blkio_policy[i]; +struct blkg_policy_data *pd; + +if (!pol) +continue ; + +/* alloc per-policy data and attach it to blkg */ + pd =kzalloc_node (sizeof (*pd) + pol->pdata_size,GFP_ATOMIC , + q->node); +if (!pd) { +blkg_free (blkg); +return NULL ; + } + + blkg->pd[i] = pd; + pd->blkg = blkg; + +/* broken, read comment in the callsite */ + pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); +if (!pd->stats_cpu) { +blkg_free (blkg); +return NULL ; + } } - blkg->pd[pol->plid] = pd; - pd->blkg = blkg; +/* invoke per-policy init */ +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkio_policy_type *pol = blkio_policy[i]; + +if (pol) + pol->ops.blkio_init_group_fn (blkg); + } + +return blkg; +} + +/* +* XXX: This updates blkg policy data in-place for root blkg, which is +* necessary across policy registration as root blkgs aren't shot down. +* This hacky implementation is interim. Eventually, blkg shoot down will +* be replaced by proper in-place update. +*/ +static struct blkio_group *blkg_root_update (struct blkio_group *blkg, +enum blkio_policy_id plid) +{ +struct request_queue *q = blkg->q; +struct blkio_policy_type *pol = blkio_policy[plid]; +struct blkg_policy_data *pd; -/* broken, read comment in the callsite */ +kfree (blkg->pd[plid]); + blkg->pd[plid] =NULL ; + pd =kzalloc (sizeof (*pd) + pol->pdata_size,GFP_ATOMIC ); +if (!pd) +return NULL ; + +spin_unlock_irq (q->queue_lock); +rcu_read_unlock (); pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); +rcu_read_lock (); +spin_lock_irq (q->queue_lock); +if (!pd->stats_cpu) { -blkg_free (blkg); +kfree (pd);return NULL ; } -/* invoke per-policy init */ + blkg->pd[plid] = pd; + pd->blkg = blkg; pol->ops.blkio_init_group_fn (blkg); +return blkg; }
@@ -536,7 +588,6 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, bool for_root) __releases(q->queue_lock) __acquires(q->queue_lock) { -struct blkio_policy_type *pol = blkio_policy[plid];struct blkio_group *blkg, *new_blkg;WARN_ON_ONCE (!rcu_read_lock_held ());
@@ -551,9 +602,12 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg,if (unlikely (blk_queue_bypass (q)) && !for_root)return ERR_PTR (blk_queue_dead (q) ? -EINVAL : -EBUSY ); - blkg =blkg_lookup (blkcg, q, plid); -if (blkg) + blkg =blkg_lookup (blkcg, q); +if (blkg) { +if (for_root) +return blkg_root_update (blkg, plid);return blkg; + }/* blkg holds a reference to blkcg */ if (!css_tryget (&blkcg->css))
@@ -571,7 +625,7 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg,spin_unlock_irq (q->queue_lock);rcu_read_unlock (); - new_blkg =blkg_alloc (blkcg, q, pol); + new_blkg =blkg_alloc (blkcg, q);rcu_read_lock ();spin_lock_irq (q->queue_lock);
@@ -583,7 +637,7 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, }/* did someone beat us to it? */ - blkg =blkg_lookup (blkcg, q, plid); + blkg =blkg_lookup (blkcg, q);if (unlikely (blkg)) goto out;
@@ -598,8 +652,8 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg,swap (blkg, new_blkg);hlist_add_head_rcu (&blkg->blkcg_node, &blkcg->blkg_list); -list_add (&blkg->q_node[plid], &q->blkg_list[plid]); - q->nr_blkgs[plid]++; +list_add (&blkg->q_node, &q->blkg_list); + q->nr_blkgs++;spin_unlock (&blkcg->lock); out:
@@ -636,31 +690,30 @@ EXPORT_SYMBOL_GPL(blkiocg_del_blkio_group);/* called under rcu_read_lock(). */ struct blkio_group *blkg_lookup (struct blkio_cgroup *blkcg, -struct request_queue *q, -enum blkio_policy_id plid) +struct request_queue *q) {struct blkio_group *blkg;struct hlist_node *n;hlist_for_each_entry_rcu (blkg, n, &blkcg->blkg_list, blkcg_node) -if (blkg->q == q && blkg->plid == plid) +if (blkg->q == q)return blkg;return NULL ; }EXPORT_SYMBOL_GPL (blkg_lookup); -static void blkg_destroy (struct blkio_group *blkg,enum blkio_policy_id plid) +static void blkg_destroy (struct blkio_group *blkg) {struct request_queue *q = blkg->q;lockdep_assert_held (q->queue_lock);/* Something wrong if we are trying to remove same group twice */ -WARN_ON_ONCE (list_empty (&blkg->q_node[plid])); -list_del_init (&blkg->q_node[plid]); +WARN_ON_ONCE (list_empty (&blkg->q_node)); +list_del_init (&blkg->q_node); -WARN_ON_ONCE (q->nr_blkgs[plid] <=0 ); - q->nr_blkgs[plid]--; +WARN_ON_ONCE (q->nr_blkgs <=0 ); + q->nr_blkgs--;/* * Put the reference taken at the time of creation so that when all
@@ -669,8 +722,7 @@ static void blkg_destroy(struct blkio_group *blkg, enum blkio_policy_id plid)blkg_put (blkg); } -void blkg_destroy_all (struct request_queue *q,enum blkio_policy_id plid, -bool destroy_root) +void blkg_destroy_all (struct request_queue *q,bool destroy_root) {struct blkio_group *blkg, *n;
@@ -679,8 +731,7 @@ void blkg_destroy_all(struct request_queue *q, enum blkio_policy_id plid,spin_lock_irq (q->queue_lock); -list_for_each_entry_safe (blkg, n, &q->blkg_list[plid], - q_node[plid]) { +list_for_each_entry_safe (blkg, n, &q->blkg_list, q_node) {/* skip root? */ if (!destroy_root && blkg->blkcg == &blkio_root_cgroup)continue ;
@@ -691,7 +742,7 @@ void blkg_destroy_all(struct request_queue *q, enum blkio_policy_id plid,* take care of destroying cfqg also. */ if (!blkiocg_del_blkio_group (blkg)) -blkg_destroy (blkg, plid); +blkg_destroy (blkg);else done = false; }
@@ -776,43 +827,49 @@ blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val)#endif blkcg =cgroup_to_blkio_cgroup (cgroup); +spin_lock (&blkio_list_lock);spin_lock_irq (&blkcg->lock);hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkio_policy_type *pol; + +list_for_each_entry (pol, &blkio_list, list) { +struct blkg_policy_data *pd = blkg->pd[pol->plid]; -spin_lock (&blkg->stats_lock); - stats = &pd->stats; +spin_lock (&blkg->stats_lock); + stats = &pd->stats;#ifdef CONFIG_DEBUG_BLK_CGROUP - idling =blkio_blkg_idling (stats); - waiting =blkio_blkg_waiting (stats); - empty =blkio_blkg_empty (stats); + idling =blkio_blkg_idling (stats); + waiting =blkio_blkg_waiting (stats); + empty =blkio_blkg_empty (stats);#endif -for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) - queued[i] = stats->stat_arr[BLKIO_STAT_QUEUED ][i]; -memset (stats,0 ,sizeof (struct blkio_group_stats )); -for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) - stats->stat_arr[BLKIO_STAT_QUEUED ][i] = queued[i]; +for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) + queued[i] = stats->stat_arr[BLKIO_STAT_QUEUED ][i]; +memset (stats,0 ,sizeof (struct blkio_group_stats )); +for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) + stats->stat_arr[BLKIO_STAT_QUEUED ][i] = queued[i];#ifdef CONFIG_DEBUG_BLK_CGROUP -if (idling) { -blkio_mark_blkg_idling (stats); - stats->start_idle_time = now; - } -if (waiting) { -blkio_mark_blkg_waiting (stats); - stats->start_group_wait_time = now; - } -if (empty) { -blkio_mark_blkg_empty (stats); - stats->start_empty_time = now; - } +if (idling) { +blkio_mark_blkg_idling (stats); + stats->start_idle_time = now; + } +if (waiting) { +blkio_mark_blkg_waiting (stats); + stats->start_group_wait_time = now; + } +if (empty) { +blkio_mark_blkg_empty (stats); + stats->start_empty_time = now; + }#endif -spin_unlock (&blkg->stats_lock); +spin_unlock (&blkg->stats_lock); -/* Reset Per cpu stats which don't take blkg->stats_lock */ -blkio_reset_stats_cpu (blkg, blkg->plid); +/* Reset Per cpu stats which don't take blkg->stats_lock */ +blkio_reset_stats_cpu (blkg, pol->plid); + } }spin_unlock_irq (&blkcg->lock); +spin_unlock (&blkio_list_lock);return 0 ; }
@@ -1157,8 +1214,7 @@ static void blkio_read_conf(struct cftype *cft, struct blkio_cgroup *blkcg,spin_lock_irq (&blkcg->lock);hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) -if (BLKIOFILE_POLICY (cft->private) == blkg->plid) -blkio_print_group_conf (cft, blkg, m); +blkio_print_group_conf (cft, blkg, m);spin_unlock_irq (&blkcg->lock); }
@@ -1213,8 +1269,6 @@ static int blkio_read_blkg_stats(struct blkio_cgroup *blkcg,const char *dname =dev_name (blkg->q->backing_dev_info.dev);int plid =BLKIOFILE_POLICY (cft->private); -if (plid != blkg->plid) -continue ;if (pcpu) { cgroup_total +=blkio_get_stat_cpu (blkg, plid, cb, dname, type);
@@ -1324,9 +1378,9 @@ static int blkio_weight_write(struct blkio_cgroup *blkcg, int plid, u64 val) blkcg->weight = (unsigned int )val;hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[plid]; -if (blkg->plid == plid && !pd->conf.weight) +if (!pd->conf.weight)blkio_update_group_weight (blkg, plid, blkcg->weight); }
@@ -1549,7 +1603,6 @@ static int blkiocg_pre_destroy(struct cgroup_subsys *subsys,unsigned long flags;struct blkio_group *blkg;struct request_queue *q; -struct blkio_policy_type *blkiop;rcu_read_lock ();
@@ -1575,11 +1628,7 @@ static int blkiocg_pre_destroy(struct cgroup_subsys *subsys,*/ spin_lock (&blkio_list_lock);spin_lock_irqsave (q->queue_lock, flags); -list_for_each_entry (blkiop, &blkio_list, list) { -if (blkiop->plid != blkg->plid) -continue ; -blkg_destroy (blkg, blkiop->plid); - } +blkg_destroy (blkg);spin_unlock_irqrestore (q->queue_lock, flags);spin_unlock (&blkio_list_lock); }while (1 );
@@ -1673,6 +1722,8 @@ void blkcg_exit_queue(struct request_queue *q)list_del_init (&q->all_q_node);mutex_unlock (&all_q_mutex); +blkg_destroy_all (q, true); +blk_throtl_exit (q); }
@@ -1722,14 +1773,12 @@ static void blkcg_bypass_start(void) __acquires(&all_q_mutex) {struct request_queue *q; -int i;mutex_lock (&all_q_mutex);list_for_each_entry (q, &all_q_list, all_q_node) {blk_queue_bypass_start (q); -for (i =0 ; i <BLKIO_NR_POLICIES ; i++) -blkg_destroy_all (q, i, false); +blkg_destroy_all (q, false); } }
block/blk-cgroup.h +3 -7
@@ -178,13 +178,11 @@ struct blkg_policy_data {struct blkio_group {/* Pointer to the associated request_queue, RCU protected */ struct request_queue __rcu *q; -struct list_head q_node[BLKIO_NR_POLICIES ]; +struct list_head q_node;struct hlist_node blkcg_node;struct blkio_cgroup *blkcg;/* Store cgroup path */ char path[128 ]; -/* policy which owns this blk group */ -enum blkio_policy_id plid;/* reference count */ int refcnt;
@@ -230,8 +228,7 @@ extern void blkcg_exit_queue(struct request_queue *q);/* Blkio controller policy registration */ extern void blkio_policy_register (struct blkio_policy_type *);extern void blkio_policy_unregister (struct blkio_policy_type *); -extern void blkg_destroy_all (struct request_queue *q, -enum blkio_policy_id plid,bool destroy_root); +extern void blkg_destroy_all (struct request_queue *q,bool destroy_root);/** * blkg_to_pdata - get policy private data
@@ -382,8 +379,7 @@ extern struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup);extern struct blkio_cgroup *task_blkio_cgroup (struct task_struct *tsk);extern int blkiocg_del_blkio_group (struct blkio_group *blkg);extern struct blkio_group *blkg_lookup (struct blkio_cgroup *blkcg, -struct request_queue *q, -enum blkio_policy_id plid); +struct request_queue *q); struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, struct request_queue *q, enum blkio_policy_id plid,
block/blk-core.c +1 -2
@@ -547,8 +547,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)INIT_LIST_HEAD (&q->timeout_list);INIT_LIST_HEAD (&q->icq_list);#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE ) -INIT_LIST_HEAD (&q->blkg_list[0 ]); -INIT_LIST_HEAD (&q->blkg_list[1 ]); +INIT_LIST_HEAD (&q->blkg_list);#endif INIT_LIST_HEAD (&q->flush_queue[0 ]);INIT_LIST_HEAD (&q->flush_queue[1 ]);
block/blk-sysfs.c +2 -2
@@ -480,6 +480,8 @@ static void blk_release_queue(struct kobject *kobj)blk_sync_queue (q); +blkcg_exit_queue (q); +if (q->elevator) {spin_lock_irq (q->queue_lock);ioc_clear_queue (q);
@@ -487,8 +489,6 @@ static void blk_release_queue(struct kobject *kobj)elevator_exit (q->elevator); } -blkcg_exit_queue (q); -if (rl->rq_pool)mempool_destroy (rl->rq_pool);
block/blk-throttle.c +3 -6
@@ -167,7 +167,7 @@ throtl_grp *throtl_lookup_tg(struct throtl_data *td, struct blkio_cgroup *blkcg)if (blkcg == &blkio_root_cgroup)return td->root_tg; -return blkg_to_tg (blkg_lookup (blkcg, td->queue,BLKIO_POLICY_THROTL )); +return blkg_to_tg (blkg_lookup (blkcg, td->queue)); } static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td,
@@ -704,8 +704,7 @@ static void throtl_process_limit_change(struct throtl_data *td)throtl_log (td,"limits changed" ); -list_for_each_entry_safe (blkg, n, &q->blkg_list[BLKIO_POLICY_THROTL ], - q_node[BLKIO_POLICY_THROTL ]) { +list_for_each_entry_safe (blkg, n, &q->blkg_list, q_node) {struct throtl_grp *tg =blkg_to_tg (blkg);if (!tg->limits_changed)
@@ -1054,11 +1053,9 @@ void blk_throtl_exit(struct request_queue *q)throtl_shutdown_wq (q); -blkg_destroy_all (q,BLKIO_POLICY_THROTL , true); -/* If there are other groups */ spin_lock_irq (q->queue_lock); - wait = q->nr_blkgs[BLKIO_POLICY_THROTL ]; + wait = q->nr_blkgs;spin_unlock_irq (q->queue_lock);/*
block/cfq-iosched.c +1 -3
@@ -3468,14 +3468,12 @@ static void cfq_exit_queue(struct elevator_queue *e)spin_unlock_irq (q->queue_lock); -blkg_destroy_all (q,BLKIO_POLICY_PROP , true); -/* * If there are groups which we could not unlink from blkcg list, * wait for a rcu period for them to be freed. */ spin_lock_irq (q->queue_lock); - wait = q->nr_blkgs[BLKIO_POLICY_PROP ]; + wait = q->nr_blkgs;spin_unlock_irq (q->queue_lock);cfq_shutdown_timer_wq (cfqd);
block/elevator.c +2 -3
@@ -923,7 +923,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) {struct elevator_queue *old = q->elevator;bool registered = old->registered; -int i, err; +int err;/* * Turn on BYPASS and drain all requests w/ elevator private data.
@@ -942,8 +942,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e)ioc_clear_queue (q);spin_unlock_irq (q->queue_lock); -for (i =0 ; i <BLKIO_NR_POLICIES ; i++) -blkg_destroy_all (q, i, false); +blkg_destroy_all (q, false);/* allocate, init and register new elevator */ err = -ENOMEM ;
include/linux/blkdev.h +2 -3
@@ -363,9 +363,8 @@ struct request_queue {struct list_head icq_list;#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE ) -/* XXX: array size hardcoded to avoid include dependency (temporary) */ -struct list_head blkg_list[2 ]; -int nr_blkgs[2 ]; +struct list_head blkg_list; +int nr_blkgs;#endif struct queue_limits limits;
Signature · 1 line
1.7.7.3
Tejun Heo
maintainerPATCH 11/11 blkcg: unify blkg's for blkcg policies
Currently, blkg is per cgroup-queue-policy combination. This is unnatural and leads to various convolutions in partially used duplicate fields in blkg, config / stat access, and general management of blkgs.
This patch make blkg's per cgroup-queue and let them serve all policies. blkgs are now created and destroyed by blkcg core proper. This will allow further consolidation of common management logic into blkcg core and API with better defined semantics and layering.
As a transitional step to untangle blkg management, elvswitch and policy [de]registration, all blkgs except the root blkg are being shot down during elvswitch and bypass. This patch adds blkg_root_update() to update root blkg in place on policy change. This is hacky and racy but should be good enough as interim step until we get locking simplified and switch over to proper in-place update for all blkgs.
---
I was thinking root blkgs should be updated on policy changes and commit message and comments were written that way too but for some reason I wrote code which updates on elvswitch, which leads to oops on root blkg access after policy changes (ie. insmod cfq-iosched.ko). Fixed. git branch updated accordingly.
Thanks.
block/blk-cgroup.c | 215 +++++++++++++++++++++++++++++-------------------- block/blk-cgroup.h | 10 -- block/blk-core.c | 3 block/blk-sysfs.c | 4 block/blk-throttle.c | 9 -- block/cfq-iosched.c | 4 block/elevator.c | 5 - include/linux/blkdev.h | 5 - 8 files changed, 143 insertions(+), 112 deletions(-)
block/blk-cgroup.c +129 -86
@@ -461,16 +461,20 @@ EXPORT_SYMBOL_GPL(blkiocg_update_io_merg*/ static void blkg_free (struct blkio_group *blkg) { -struct blkg_policy_data *pd; +int i;if (!blkg)return ; - pd = blkg->pd[blkg->plid]; -if (pd) { -free_percpu (pd->stats_cpu); -kfree (pd); +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkg_policy_data *pd = blkg->pd[i]; + +if (pd) { +free_percpu (pd->stats_cpu); +kfree (pd); + } } +kfree (blkg); }
@@ -486,11 +490,10 @@ static void blkg_free(struct blkio_group* percpu stat breakage. */ static struct blkio_group *blkg_alloc (struct blkio_cgroup *blkcg, -struct request_queue *q, -struct blkio_policy_type *pol) +struct request_queue *q) {struct blkio_group *blkg; -struct blkg_policy_data *pd; +int i;/* alloc and init base part */ blkg =kzalloc_node (sizeof (*blkg),GFP_ATOMIC , q->node);
@@ -499,34 +502,45 @@ static struct blkio_group *blkg_alloc(stspin_lock_init (&blkg->stats_lock);rcu_assign_pointer (blkg->q, q); -INIT_LIST_HEAD (&blkg->q_node[0 ]); -INIT_LIST_HEAD (&blkg->q_node[1 ]); +INIT_LIST_HEAD (&blkg->q_node); blkg->blkcg = blkcg; - blkg->plid = pol->plid; blkg->refcnt =1 ;cgroup_path (blkcg->css.cgroup, blkg->path,sizeof (blkg->path)); -/* alloc per-policy data and attach it to blkg */ - pd =kzalloc_node (sizeof (*pd) + pol->pdata_size,GFP_ATOMIC , - q->node); -if (!pd) { -blkg_free (blkg); -return NULL ; - } +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkio_policy_type *pol = blkio_policy[i]; +struct blkg_policy_data *pd; + +if (!pol) +continue ; - blkg->pd[pol->plid] = pd; - pd->blkg = blkg; +/* alloc per-policy data and attach it to blkg */ + pd =kzalloc_node (sizeof (*pd) + pol->pdata_size,GFP_ATOMIC , + q->node); +if (!pd) { +blkg_free (blkg); +return NULL ; + } -/* broken, read comment in the callsite */ + blkg->pd[i] = pd; + pd->blkg = blkg; - pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); -if (!pd->stats_cpu) { -blkg_free (blkg); -return NULL ; +/* broken, read comment in the callsite */ + pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); +if (!pd->stats_cpu) { +blkg_free (blkg); +return NULL ; + } }/* invoke per-policy init */ - pol->ops.blkio_init_group_fn (blkg); +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkio_policy_type *pol = blkio_policy[i]; + +if (pol) + pol->ops.blkio_init_group_fn (blkg); + } +return blkg; }
@@ -536,7 +550,6 @@ struct blkio_group *blkg_lookup_create(s bool for_root) __releases(q->queue_lock) __acquires(q->queue_lock) { -struct blkio_policy_type *pol = blkio_policy[plid];struct blkio_group *blkg, *new_blkg;WARN_ON_ONCE (!rcu_read_lock_held ());
@@ -551,7 +564,7 @@ struct blkio_group *blkg_lookup_create(sif (unlikely (blk_queue_bypass (q)) && !for_root)return ERR_PTR (blk_queue_dead (q) ? -EINVAL : -EBUSY ); - blkg =blkg_lookup (blkcg, q, plid); + blkg =blkg_lookup (blkcg, q);if (blkg)return blkg;
@@ -571,7 +584,7 @@ struct blkio_group *blkg_lookup_create(sspin_unlock_irq (q->queue_lock);rcu_read_unlock (); - new_blkg =blkg_alloc (blkcg, q, pol); + new_blkg =blkg_alloc (blkcg, q);rcu_read_lock ();spin_lock_irq (q->queue_lock);
@@ -583,7 +596,7 @@ struct blkio_group *blkg_lookup_create(s }/* did someone beat us to it? */ - blkg =blkg_lookup (blkcg, q, plid); + blkg =blkg_lookup (blkcg, q);if (unlikely (blkg)) goto out;
@@ -598,8 +611,8 @@ struct blkio_group *blkg_lookup_create(sswap (blkg, new_blkg);hlist_add_head_rcu (&blkg->blkcg_node, &blkcg->blkg_list); -list_add (&blkg->q_node[plid], &q->blkg_list[plid]); - q->nr_blkgs[plid]++; +list_add (&blkg->q_node, &q->blkg_list); + q->nr_blkgs++;spin_unlock (&blkcg->lock); out:
@@ -636,31 +649,30 @@ EXPORT_SYMBOL_GPL(blkiocg_del_blkio_grou/* called under rcu_read_lock(). */ struct blkio_group *blkg_lookup (struct blkio_cgroup *blkcg, -struct request_queue *q, -enum blkio_policy_id plid) +struct request_queue *q) {struct blkio_group *blkg;struct hlist_node *n;hlist_for_each_entry_rcu (blkg, n, &blkcg->blkg_list, blkcg_node) -if (blkg->q == q && blkg->plid == plid) +if (blkg->q == q)return blkg;return NULL ; }EXPORT_SYMBOL_GPL (blkg_lookup); -static void blkg_destroy (struct blkio_group *blkg,enum blkio_policy_id plid) +static void blkg_destroy (struct blkio_group *blkg) {struct request_queue *q = blkg->q;lockdep_assert_held (q->queue_lock);/* Something wrong if we are trying to remove same group twice */ -WARN_ON_ONCE (list_empty (&blkg->q_node[plid])); -list_del_init (&blkg->q_node[plid]); +WARN_ON_ONCE (list_empty (&blkg->q_node)); +list_del_init (&blkg->q_node); -WARN_ON_ONCE (q->nr_blkgs[plid] <=0 ); - q->nr_blkgs[plid]--; +WARN_ON_ONCE (q->nr_blkgs <=0 ); + q->nr_blkgs--;/* * Put the reference taken at the time of creation so that when all
@@ -669,8 +681,7 @@ static void blkg_destroy(struct blkio_grblkg_put (blkg); } -void blkg_destroy_all (struct request_queue *q,enum blkio_policy_id plid, -bool destroy_root) +void blkg_destroy_all (struct request_queue *q,bool destroy_root) {struct blkio_group *blkg, *n;
@@ -679,8 +690,7 @@ void blkg_destroy_all(struct request_quespin_lock_irq (q->queue_lock); -list_for_each_entry_safe (blkg, n, &q->blkg_list[plid], - q_node[plid]) { +list_for_each_entry_safe (blkg, n, &q->blkg_list, q_node) {/* skip root? */ if (!destroy_root && blkg->blkcg == &blkio_root_cgroup)continue ;
@@ -691,7 +701,7 @@ void blkg_destroy_all(struct request_que* take care of destroying cfqg also. */ if (!blkiocg_del_blkio_group (blkg)) -blkg_destroy (blkg, plid); +blkg_destroy (blkg);else done = false; }
@@ -776,43 +786,49 @@ blkiocg_reset_stats(struct cgroup *cgrou#endif blkcg =cgroup_to_blkio_cgroup (cgroup); +spin_lock (&blkio_list_lock);spin_lock_irq (&blkcg->lock);hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkio_policy_type *pol; -spin_lock (&blkg->stats_lock); - stats = &pd->stats; +list_for_each_entry (pol, &blkio_list, list) { +struct blkg_policy_data *pd = blkg->pd[pol->plid]; + +spin_lock (&blkg->stats_lock); + stats = &pd->stats;#ifdef CONFIG_DEBUG_BLK_CGROUP - idling =blkio_blkg_idling (stats); - waiting =blkio_blkg_waiting (stats); - empty =blkio_blkg_empty (stats); + idling =blkio_blkg_idling (stats); + waiting =blkio_blkg_waiting (stats); + empty =blkio_blkg_empty (stats);#endif -for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) - queued[i] = stats->stat_arr[BLKIO_STAT_QUEUED ][i]; -memset (stats,0 ,sizeof (struct blkio_group_stats )); -for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) - stats->stat_arr[BLKIO_STAT_QUEUED ][i] = queued[i]; +for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) + queued[i] = stats->stat_arr[BLKIO_STAT_QUEUED ][i]; +memset (stats,0 ,sizeof (struct blkio_group_stats )); +for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) + stats->stat_arr[BLKIO_STAT_QUEUED ][i] = queued[i];#ifdef CONFIG_DEBUG_BLK_CGROUP -if (idling) { -blkio_mark_blkg_idling (stats); - stats->start_idle_time = now; - } -if (waiting) { -blkio_mark_blkg_waiting (stats); - stats->start_group_wait_time = now; - } -if (empty) { -blkio_mark_blkg_empty (stats); - stats->start_empty_time = now; - } +if (idling) { +blkio_mark_blkg_idling (stats); + stats->start_idle_time = now; + } +if (waiting) { +blkio_mark_blkg_waiting (stats); + stats->start_group_wait_time = now; + } +if (empty) { +blkio_mark_blkg_empty (stats); + stats->start_empty_time = now; + }#endif -spin_unlock (&blkg->stats_lock); +spin_unlock (&blkg->stats_lock); -/* Reset Per cpu stats which don't take blkg->stats_lock */ -blkio_reset_stats_cpu (blkg, blkg->plid); +/* Reset Per cpu stats which don't take blkg->stats_lock */ +blkio_reset_stats_cpu (blkg, pol->plid); + } }spin_unlock_irq (&blkcg->lock); +spin_unlock (&blkio_list_lock);return 0 ; }
@@ -1157,8 +1173,7 @@ static void blkio_read_conf(struct cftypspin_lock_irq (&blkcg->lock);hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) -if (BLKIOFILE_POLICY (cft->private) == blkg->plid) -blkio_print_group_conf (cft, blkg, m); +blkio_print_group_conf (cft, blkg, m);spin_unlock_irq (&blkcg->lock); }
@@ -1213,8 +1228,6 @@ static int blkio_read_blkg_stats(structconst char *dname =dev_name (blkg->q->backing_dev_info.dev);int plid =BLKIOFILE_POLICY (cft->private); -if (plid != blkg->plid) -continue ;if (pcpu) { cgroup_total +=blkio_get_stat_cpu (blkg, plid, cb, dname, type);
@@ -1324,9 +1337,9 @@ static int blkio_weight_write(struct blk blkcg->weight = (unsigned int )val;hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[plid]; -if (blkg->plid == plid && !pd->conf.weight) +if (!pd->conf.weight)blkio_update_group_weight (blkg, plid, blkcg->weight); }
@@ -1549,7 +1562,6 @@ static int blkiocg_pre_destroy(struct cgunsigned long flags;struct blkio_group *blkg;struct request_queue *q; -struct blkio_policy_type *blkiop;rcu_read_lock ();
@@ -1575,11 +1587,7 @@ static int blkiocg_pre_destroy(struct cg*/ spin_lock (&blkio_list_lock);spin_lock_irqsave (q->queue_lock, flags); -list_for_each_entry (blkiop, &blkio_list, list) { -if (blkiop->plid != blkg->plid) -continue ; -blkg_destroy (blkg, blkiop->plid); - } +blkg_destroy (blkg);spin_unlock_irqrestore (q->queue_lock, flags);spin_unlock (&blkio_list_lock); }while (1 );
@@ -1673,6 +1681,8 @@ void blkcg_exit_queue(struct request_quelist_del_init (&q->all_q_node);mutex_unlock (&all_q_mutex); +blkg_destroy_all (q, true); +blk_throtl_exit (q); }
@@ -1722,14 +1732,12 @@ static void blkcg_bypass_start(void) __acquires(&all_q_mutex) {struct request_queue *q; -int i;mutex_lock (&all_q_mutex);list_for_each_entry (q, &all_q_list, all_q_node) {blk_queue_bypass_start (q); -for (i =0 ; i <BLKIO_NR_POLICIES ; i++) -blkg_destroy_all (q, i, false); +blkg_destroy_all (q, false); } }
@@ -1744,6 +1752,39 @@ static void blkcg_bypass_end(void)mutex_unlock (&all_q_mutex); } +/* +* XXX: This updates blkg policy data in-place for root blkg, which is +* necessary across policy registration as root blkgs aren't shot down. +* This broken and racy implementation is interim. Eventually, blkg shoot +* down will be replaced by proper in-place update. +*/ +static void update_root_blkgs (enum blkio_policy_id plid) +{ +struct blkio_policy_type *pol = blkio_policy[plid]; +struct request_queue *q; + +list_for_each_entry (q, &all_q_list, all_q_node) { +struct blkio_group *blkg =blkg_lookup (&blkio_root_cgroup, q); +struct blkg_policy_data *pd; + +kfree (blkg->pd[plid]); + blkg->pd[plid] =NULL ; + +if (!pol) +continue ; + + pd =kzalloc (sizeof (*pd) + pol->pdata_size,GFP_KERNEL ); +WARN_ON_ONCE (!pd); + + pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); +WARN_ON_ONCE (!pd->stats_cpu); + + blkg->pd[plid] = pd; + pd->blkg = blkg; + pol->ops.blkio_init_group_fn (blkg); + } +} +void blkio_policy_register (struct blkio_policy_type *blkiop) {blkcg_bypass_start ();
@@ -1754,6 +1795,7 @@ void blkio_policy_register(struct blkio_list_add_tail (&blkiop->list, &blkio_list);spin_unlock (&blkio_list_lock); +update_root_blkgs (blkiop->plid);blkcg_bypass_end (); }EXPORT_SYMBOL_GPL (blkio_policy_register);
@@ -1768,6 +1810,7 @@ void blkio_policy_unregister(struct blkilist_del_init (&blkiop->list);spin_unlock (&blkio_list_lock); +update_root_blkgs (blkiop->plid);blkcg_bypass_end (); }EXPORT_SYMBOL_GPL (blkio_policy_unregister);
block/blk-cgroup.h +3 -7
@@ -178,13 +178,11 @@ struct blkg_policy_data {struct blkio_group {/* Pointer to the associated request_queue, RCU protected */ struct request_queue __rcu *q; -struct list_head q_node[BLKIO_NR_POLICIES ]; +struct list_head q_node;struct hlist_node blkcg_node;struct blkio_cgroup *blkcg;/* Store cgroup path */ char path[128 ]; -/* policy which owns this blk group */ -enum blkio_policy_id plid;/* reference count */ int refcnt;
@@ -230,8 +228,7 @@ extern void blkcg_exit_queue(struct requ/* Blkio controller policy registration */ extern void blkio_policy_register (struct blkio_policy_type *);extern void blkio_policy_unregister (struct blkio_policy_type *); -extern void blkg_destroy_all (struct request_queue *q, -enum blkio_policy_id plid,bool destroy_root); +extern void blkg_destroy_all (struct request_queue *q,bool destroy_root);/** * blkg_to_pdata - get policy private data
@@ -382,8 +379,7 @@ extern struct blkio_cgroup *cgroup_to_blextern struct blkio_cgroup *task_blkio_cgroup (struct task_struct *tsk);extern int blkiocg_del_blkio_group (struct blkio_group *blkg);extern struct blkio_group *blkg_lookup (struct blkio_cgroup *blkcg, -struct request_queue *q, -enum blkio_policy_id plid); +struct request_queue *q); struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, struct request_queue *q, enum blkio_policy_id plid,
block/blk-throttle.c +3 -6
@@ -167,7 +167,7 @@ throtl_grp *throtl_lookup_tg(struct throif (blkcg == &blkio_root_cgroup)return td->root_tg; -return blkg_to_tg (blkg_lookup (blkcg, td->queue,BLKIO_POLICY_THROTL )); +return blkg_to_tg (blkg_lookup (blkcg, td->queue)); } static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td,
@@ -704,8 +704,7 @@ static void throtl_process_limit_change(throtl_log (td,"limits changed" ); -list_for_each_entry_safe (blkg, n, &q->blkg_list[BLKIO_POLICY_THROTL ], - q_node[BLKIO_POLICY_THROTL ]) { +list_for_each_entry_safe (blkg, n, &q->blkg_list, q_node) {struct throtl_grp *tg =blkg_to_tg (blkg);if (!tg->limits_changed)
@@ -1054,11 +1053,9 @@ void blk_throtl_exit(struct request_queuthrotl_shutdown_wq (q); -blkg_destroy_all (q,BLKIO_POLICY_THROTL , true); -/* If there are other groups */ spin_lock_irq (q->queue_lock); - wait = q->nr_blkgs[BLKIO_POLICY_THROTL ]; + wait = q->nr_blkgs;spin_unlock_irq (q->queue_lock);/*
block/cfq-iosched.c +1 -3
@@ -3468,14 +3468,12 @@ static void cfq_exit_queue(struct elevatspin_unlock_irq (q->queue_lock); -blkg_destroy_all (q,BLKIO_POLICY_PROP , true); -/* * If there are groups which we could not unlink from blkcg list, * wait for a rcu period for them to be freed. */ spin_lock_irq (q->queue_lock); - wait = q->nr_blkgs[BLKIO_POLICY_PROP ]; + wait = q->nr_blkgs;spin_unlock_irq (q->queue_lock);cfq_shutdown_timer_wq (cfqd);
block/blk-core.c +1 -2
@@ -547,8 +547,7 @@ struct request_queue *blk_alloc_queue_noINIT_LIST_HEAD (&q->timeout_list);INIT_LIST_HEAD (&q->icq_list);#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE ) -INIT_LIST_HEAD (&q->blkg_list[0 ]); -INIT_LIST_HEAD (&q->blkg_list[1 ]); +INIT_LIST_HEAD (&q->blkg_list);#endif INIT_LIST_HEAD (&q->flush_queue[0 ]);INIT_LIST_HEAD (&q->flush_queue[1 ]);
block/elevator.c +2 -3
@@ -923,7 +923,7 @@ static int elevator_switch(struct reques {struct elevator_queue *old = q->elevator;bool registered = old->registered; -int i, err; +int err;/* * Turn on BYPASS and drain all requests w/ elevator private data.
@@ -942,8 +942,7 @@ static int elevator_switch(struct requesioc_clear_queue (q);spin_unlock_irq (q->queue_lock); -for (i =0 ; i <BLKIO_NR_POLICIES ; i++) -blkg_destroy_all (q, i, false); +blkg_destroy_all (q, false);/* allocate, init and register new elevator */ err = -ENOMEM ;
include/linux/blkdev.h +2 -3
@@ -363,9 +363,8 @@ struct request_queue {struct list_head icq_list;#if defined(CONFIG_BLK_CGROUP ) || defined(CONFIG_BLK_CGROUP_MODULE ) -/* XXX: array size hardcoded to avoid include dependency (temporary) */ -struct list_head blkg_list[2 ]; -int nr_blkgs[2 ]; +struct list_head blkg_list; +int nr_blkgs;#endif struct queue_limits limits;
block/blk-sysfs.c +2 -2
@@ -480,6 +480,8 @@ static void blk_release_queue(struct kobblk_sync_queue (q); +blkcg_exit_queue (q); +if (q->elevator) {spin_lock_irq (q->queue_lock);ioc_clear_queue (q);
@@ -487,8 +489,6 @@ static void blk_release_queue(struct kobelevator_exit (q->elevator); } -blkcg_exit_queue (q); -if (rl->rq_pool)mempool_destroy (rl->rq_pool);
Re: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Quoted 10 lines · Tejun Heo
Currently, blkg is per cgroup-queue-policy combination. This is unnatural and leads to various convolutions in partially used duplicate fields in blkg, config / stat access, and general management of blkgs.
This patch make blkg's per cgroup-queue and let them serve all policies. blkgs are now created and destroyed by blkcg core proper. This will allow further consolidation of common management logic into blkcg core and API with better defined semantics and layering.
Quoted 6 lines
As a transitional step to untangle blkg management, elvswitch and policy [de]registration, all blkgs except the root blkg are being shot down during elvswitch and bypass. This patch adds
blkg_root_update()to update root blkg in place on policy change. This is hacky and racy but should be good enough as interim step until we get locking simplified and switch over to proper in-place update for all blkgs.
- So we don't shoot down root group over elevator switch and policy changes because we are not sure if we will be able to alloc new group? It is not like elevator where we don't free the old one till we have made sure that new one is allocated and initialized properly.
- I am assuming that we will change blkg_destroy_all() later to also take policy as argument and only destroy policy data of respective policy and not the whole group. (Well I guess we can destroy the whole group if it was only policy on the group).
[..]
block/blk-cgroup.c static void blkg_free(struct blkio_group · line 493
Quoted 4 lines
static struct
blkio_group*blkg_alloc(structblkio_cgroup*blkcg,block/blk-cgroup.c +1
-2-struct request_queue *q, - struct blkio_policy_type *pol) + struct request_queue *q)
Comment before this function still mentions "pol" as function argument.
[..]
block/blk-cgroup.c blkiocg_reset_stats(struct cgroup *cgrou · line 790
Quoted 6 lines
block/blk-cgroup.c +1
-0@@ -776,43 +786,49 @@ blkiocg_reset_stats(struct cgroup *cgrou#endif blkcg =cgroup_to_blkio_cgroup (cgroup); +spin_lock (&blkio_list_lock);spin_lock_irq (&blkcg->lock);
Isn't blkcg lock enough to protect against policy registration/deregistration. A policy can not add/delete a group to cgroup list without blkcg list.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Hey, Vivek.
Quoted 8 lines · Vivek Goyal
Quoted 6 lines · Tejun Heo
As a transitional step to untangle blkg management, elvswitch and policy [de]registration, all blkgs except the root blkg are being shot down during elvswitch and bypass. This patch adds
blkg_root_update()to update root blkg in place on policy change. This is hacky and racy but should be good enough as interim step until we get locking simplified and switch over to proper in-place update for all blkgs.
Quoted 4 lines
- So we don't shoot down root group over elevator switch and policy changes because we are not sure if we will be able to alloc new group? It is not like elevator where we don't free the old one till we have made sure that new one is allocated and initialized properly.
No, because we policies cache root group and we don't have mechanism to update them. I could have added that but root group management should be moved to blkcg core anyway and in-place update will be applied to all blkgs, so I just chose a dirty shortcut as an interim step.
Quoted 4 lines
- I am assuming that we will change
blkg_destroy_all()later to also take policy as argument and only destroy policy data of respective policy and not the whole group. (Well I guess we can destroy the whole group if it was only policy on the group).
Yeap, that's what's scheduled.
Quoted 7 lines
[..]
block/blk-cgroup.c static void blkg_free(struct blkio_group · line 493
Quoted 4 lines
static struct
blkio_group*blkg_alloc(structblkio_cgroup*blkcg,block/blk-cgroup.c +1
-2-struct request_queue *q, - struct blkio_policy_type *pol) + struct request_queue *q)Comment before this function still mentions "pol" as function argument.
Will update.
Quoted 10 lines
[..]
block/blk-cgroup.c blkiocg_reset_stats(struct cgroup *cgrou · line 790
Quoted 6 lines
block/blk-cgroup.c +1
-0@@ -776,43 +786,49 @@ blkiocg_reset_stats(struct cgroup *cgrou#endif blkcg =cgroup_to_blkio_cgroup (cgroup); +spin_lock (&blkio_list_lock);spin_lock_irq (&blkcg->lock);Isn't blkcg lock enough to protect against policy registration/deregistration. A policy can not add/delete a group to cgroup list without blkcg list.
But pol list can change regardless of that, no?
Thanks.
Signature · 1 line
tejun
Re: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Quoted 5 lines · Tejun Heo
Quoted 5 lines
[..]
Quoted 3 lines
(quoted diff) +0
-0@@ -776,43 +786,49 @@ blkiocg_reset_stats(struct cgroup *cgrou #endif
Quoted 8 lines
Quoted 6 lines
block/blk-cgroup.c blkiocg_reset_stats(struct cgroup *cgrou · line 790
Quoted 3 lines
block/blk-cgroup.c +1
-0blkcg =cgroup_to_blkio_cgroup (cgroup); +spin_lock (&blkio_list_lock);spin_lock_irq (&blkcg->lock);Isn't blkcg lock enough to protect against policy registration/deregistration. A policy can not add/delete a group to cgroup list without blkcg list.
But pol list can change regardless of that, no?
Ok, looks like now it is needed because blkcg lock will just gurantee that blkg is around but blkg->pd[plid] can disappear if you are not holding blkio_list lock (update_root_blkgs).
I am wondering if we should take blkcg->lock if blkg is on blkcg list and is being modified in place. That way, once we are switching elevator, we should be able to shoot down the policy data without taking blkio_list lock.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Quoted 8 lines · Vivek Goyal
Ok, looks like now it is needed because blkcg lock will just gurantee that blkg is around but blkg->pd[plid] can disappear if you are not holding
blkio_listlock (update_root_blkgs).I am wondering if we should take
blkcg->lockif blkg is on blkcg list and is being modified in place. That way, once we are switching elevator, we should be able to shoot down the policy data without takingblkio_listlock.
I think it gotta become something per-queue, not global, and if we make it per-queue, it should be able to live inside queue_lock.
Thanks.
Signature · 1 line
tejun
Re: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Quoted 5 lines · Tejun Heo
Quoted 4 lines · Vivek Goyal
Ok, looks like now it is needed because blkcg lock will just gurantee that blkg is around but blkg->pd[plid] can disappear if you are not holding
blkio_listlock (update_root_blkgs).
Quoted 7 lines
Quoted 4 lines
I am wondering if we should take
blkcg->lockif blkg is on blkcg list and is being modified in place. That way, once we are switching elevator, we should be able to shoot down the policy data without takingblkio_listlock.I think it gotta become something per-queue, not global, and if we make it per-queue, it should be able to live inside
queue_lock.
Hmm... then blkiocg_reset_stats() will run into lock ordering issue. Can't hold queue lock inside blkcg lock. I guess you will do some kind of locking trick again as you did for io context logic.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Quoted 3 lines · Vivek Goyal
Hmm... then
blkiocg_reset_stats()will run into lock ordering issue. Can't hold queue lock inside blkcg lock. I guess you will do some kind of locking trick again as you did for io context logic.
Ummm... I don't know the details yet but decisions like making policies per-queue are much higher level than locking details. There are cases where implementation details become problematic enough and high level decisions are affected but I don't really think that would be the case here. It's not like reset stats is a hot path. In fact, I don't even understand why the hell we have it. I guess it's another blkcg braindamage.
If userland is interested in delta between certain periods, it's supposed to read values at the start and end of the period and subtract. Allowing reset might make sense if there's single bearded admin looking at the raw numbers, but it breaks as soon as tools and automation are involved. How would you decide who owns those counters?
And we sadly can't even remove that at this point. Ugh................
Signature · 1 line
tejun
Re: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Quoted 5 lines · Tejun Heo
Quoted 3 lines · Vivek Goyal
Hmm... then
blkiocg_reset_stats()will run into lock ordering issue. Can't hold queue lock inside blkcg lock. I guess you will do some kind of locking trick again as you did for io context logic.
Quoted 7 lines
Ummm... I don't know the details yet but decisions like making policies per-queue are much higher level than locking details. There are cases where implementation details become problematic enough and high level decisions are affected but I don't really think that would be the case here. It's not like reset stats is a hot path. In fact, I don't even understand why the hell we have it. I guess it's another blkcg braindamage.
git commit of when it was added.
commit 84c124da9ff50bd71fab9c939ee5b7cd8bef2bd9
Author: Divyesh Shah <[email protected]> Date: Fri Apr 9 08:31:19 2010 +0200
..
o add a separate reset_stats interface
..
Quoted 8 lines
If userland is interested in delta between certain periods, it's supposed to read values at the start and end of the period and subtract. Allowing reset might make sense if there's single bearded admin looking at the raw numbers, but it breaks as soon as tools and automation are involved. How would you decide who owns those counters?
Personally I consider it to be a debugging aid. User space applications should do what you suggested and that is take delta between certain periods.
Frankly speaking I have found it useful though. If I am debugging something, I don't have to reboot the system to reset the stats and calculating the difference from zero is easier for humans.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Quoted 7 lines · Vivek Goyal
Personally I consider it to be a debugging aid. User space applications should do what you suggested and that is take delta between certain periods.
Frankly speaking I have found it useful though. If I am debugging something, I don't have to reboot the system to reset the stats and calculating the difference from zero is easier for humans.
There are many things different people may find useful but we shouldn't add userland visible interface with marginal usefulness like this one. Seriously, you can write up a ten line python script to achieve what you want. It's just sad. :(
Signature · 1 line
tejun
Re: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
In reply to Tejun HeoQuoted 17 lines · Tejun Heo
Currently, blkg is per cgroup-queue-policy combination. This is unnatural and leads to various convolutions in partially used duplicate fields in blkg, config / stat access, and general management of blkgs.
This patch make blkg's per cgroup-queue and let them serve all policies. blkgs are now created and destroyed by blkcg core proper. This will allow further consolidation of common management logic into blkcg core and API with better defined semantics and layering.
As a transitional step to untangle blkg management, elvswitch and policy [de]registration, all blkgs except the root blkg are being shot down during elvswitch and bypass. This patch adds
blkg_root_update()to update root blkg in place on policy change. This is hacky and racy but should be good enough as interim step until we get locking simplified and switch over to proper in-place update for all blkgs.
Quoted 8 lines
Signed-off-by Tejun Heo <[email protected]>Cc Vivek Goyal <[email protected]>---
I was thinking root blkgs should be updated on policy changes and commit message and comments were written that way too but for some reason I wrote code which updates on elvswitch, which leads to oops on root blkg access after policy changes (ie. insmod cfq-iosched.ko). Fixed. git branch updated accordingly.
Tejun,
Shouldn't we shoot down root group policy data upon elevator switch? Otherwise once the elevator comes back, it will continue to use old policy data (old stats, overwrite old weigts etc.).
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Hello,
Quoted 3 lines · Vivek Goyal
Shouldn't we shoot down root group policy data upon elevator switch? Otherwise once the elevator comes back, it will continue to use old policy data (old stats, overwrite old weigts etc.).
It's racy and hacky but we do shoot down policy specific data and reinit them. The behavior should be fine although implementation is seriously ugly for now.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH UPDATED 11/11] blkcg: unify blkg's for blkcg policies
Quoted 10 lines · Tejun Heo
Hello,
Quoted 3 lines · Vivek Goyal
Shouldn't we shoot down root group policy data upon elevator switch? Otherwise once the elevator comes back, it will continue to use old policy data (old stats, overwrite old weigts etc.).
It's racy and hacky but we do shoot down policy specific data and reinit them. The behavior should be fine although implementation is seriously ugly for now.
Oops, sorry, got confused again about elvswitch and pol registration. Hmmm... yeah, we should probably be doing about the same thing on elvswitch. I'll look into it.
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 11/11 blkcg: unify blkg's for blkcg policies
In reply to Tejun HeoCurrently, blkg is per cgroup-queue-policy combination. This is unnatural and leads to various convolutions in partially used duplicate fields in blkg, config / stat access, and general management of blkgs.
This patch make blkg's per cgroup-queue and let them serve all policies. blkgs are now created and destroyed by blkcg core proper. This will allow further consolidation of common management logic into blkcg core and API with better defined semantics and layering.
As a transitional step to untangle blkg management, elvswitch and policy [de]registration, all blkgs except the root blkg are being shot down during elvswitch and bypass. This patch adds blkg_root_update() to update root blkg in place on policy change. This is hacky and racy but should be good enough as interim step until we get locking simplified and switch over to proper in-place update for all blkgs.
-v2: Root blkgs need to be updated on elvswitch too and blkg_alloc()
comment wasn't updated according to the function change. Fixed. Both pointed out by Vivek.
---
Modified to update root blkgs on elvswitch too.
Jens, there are a couple other changes but they're all trivial. I didn't want to repost both patchsets for those changes. The git branch has been updated. Ping me if you want the full series reposted.
git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc.git blkcg-unified-blkg
I'd really like to get this into block/core and get some exposure in linux-next. There still are a lot of transitional stuff but I feel that there's now somewhat solid ground to build stuff atop. Vivek, what do you think?
Thanks.
block/blk-cgroup.c | 227 +++++++++++++++++++++++++++++++-------------------- block/blk-cgroup.h | 11 -- block/blk-core.c | 3 block/blk-sysfs.c | 4 block/blk-throttle.c | 9 -- block/cfq-iosched.c | 4 block/elevator.c | 5 - 7 files changed, 151 insertions(+), 112 deletions(-)
block/blk-cgroup.c +139 -88
@@ -461,16 +461,20 @@ EXPORT_SYMBOL_GPL(blkiocg_update_io_merg*/ static void blkg_free (struct blkio_group *blkg) { -struct blkg_policy_data *pd; +int i;if (!blkg)return ; - pd = blkg->pd[blkg->plid]; -if (pd) { -free_percpu (pd->stats_cpu); -kfree (pd); +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkg_policy_data *pd = blkg->pd[i]; + +if (pd) { +free_percpu (pd->stats_cpu); +kfree (pd); + } } +kfree (blkg); }
@@ -478,19 +482,17 @@ static void blkg_free(struct blkio_group* blkg_alloc - allocate a blkg * @blkcg: block cgroup the new blkg is associated with * @q: request_queue the new blkg is associated with -* @pol: policy the new blkg is associated with * -* Allocate a new blkg assocating @blkcg and @q for @pol. +* Allocate a new blkg assocating @blkcg and @q. * * FIXME: Should be called with queue locked but currently isn't due to * percpu stat breakage. */ static struct blkio_group *blkg_alloc (struct blkio_cgroup *blkcg, -struct request_queue *q, -struct blkio_policy_type *pol) +struct request_queue *q) {struct blkio_group *blkg; -struct blkg_policy_data *pd; +int i;/* alloc and init base part */ blkg =kzalloc_node (sizeof (*blkg),GFP_ATOMIC , q->node);
@@ -499,34 +501,45 @@ static struct blkio_group *blkg_alloc(stspin_lock_init (&blkg->stats_lock);rcu_assign_pointer (blkg->q, q); -INIT_LIST_HEAD (&blkg->q_node[0 ]); -INIT_LIST_HEAD (&blkg->q_node[1 ]); +INIT_LIST_HEAD (&blkg->q_node); blkg->blkcg = blkcg; - blkg->plid = pol->plid; blkg->refcnt =1 ;cgroup_path (blkcg->css.cgroup, blkg->path,sizeof (blkg->path)); -/* alloc per-policy data and attach it to blkg */ - pd =kzalloc_node (sizeof (*pd) + pol->pdata_size,GFP_ATOMIC , - q->node); -if (!pd) { -blkg_free (blkg); -return NULL ; - } +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkio_policy_type *pol = blkio_policy[i]; +struct blkg_policy_data *pd; - blkg->pd[pol->plid] = pd; - pd->blkg = blkg; +if (!pol) +continue ; + +/* alloc per-policy data and attach it to blkg */ + pd =kzalloc_node (sizeof (*pd) + pol->pdata_size,GFP_ATOMIC , + q->node); +if (!pd) { +blkg_free (blkg); +return NULL ; + } -/* broken, read comment in the callsite */ + blkg->pd[i] = pd; + pd->blkg = blkg; - pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); -if (!pd->stats_cpu) { -blkg_free (blkg); -return NULL ; +/* broken, read comment in the callsite */ + pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); +if (!pd->stats_cpu) { +blkg_free (blkg); +return NULL ; + } }/* invoke per-policy init */ - pol->ops.blkio_init_group_fn (blkg); +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) { +struct blkio_policy_type *pol = blkio_policy[i]; + +if (pol) + pol->ops.blkio_init_group_fn (blkg); + } +return blkg; }
@@ -536,7 +549,6 @@ struct blkio_group *blkg_lookup_create(s bool for_root) __releases(q->queue_lock) __acquires(q->queue_lock) { -struct blkio_policy_type *pol = blkio_policy[plid];struct blkio_group *blkg, *new_blkg;WARN_ON_ONCE (!rcu_read_lock_held ());
@@ -551,7 +563,7 @@ struct blkio_group *blkg_lookup_create(sif (unlikely (blk_queue_bypass (q)) && !for_root)return ERR_PTR (blk_queue_dead (q) ? -EINVAL : -EBUSY ); - blkg =blkg_lookup (blkcg, q, plid); + blkg =blkg_lookup (blkcg, q);if (blkg)return blkg;
@@ -571,7 +583,7 @@ struct blkio_group *blkg_lookup_create(sspin_unlock_irq (q->queue_lock);rcu_read_unlock (); - new_blkg =blkg_alloc (blkcg, q, pol); + new_blkg =blkg_alloc (blkcg, q);rcu_read_lock ();spin_lock_irq (q->queue_lock);
@@ -583,7 +595,7 @@ struct blkio_group *blkg_lookup_create(s }/* did someone beat us to it? */ - blkg =blkg_lookup (blkcg, q, plid); + blkg =blkg_lookup (blkcg, q);if (unlikely (blkg)) goto out;
@@ -598,8 +610,8 @@ struct blkio_group *blkg_lookup_create(sswap (blkg, new_blkg);hlist_add_head_rcu (&blkg->blkcg_node, &blkcg->blkg_list); -list_add (&blkg->q_node[plid], &q->blkg_list[plid]); - q->nr_blkgs[plid]++; +list_add (&blkg->q_node, &q->blkg_list); + q->nr_blkgs++;spin_unlock (&blkcg->lock); out:
@@ -636,31 +648,30 @@ EXPORT_SYMBOL_GPL(blkiocg_del_blkio_grou/* called under rcu_read_lock(). */ struct blkio_group *blkg_lookup (struct blkio_cgroup *blkcg, -struct request_queue *q, -enum blkio_policy_id plid) +struct request_queue *q) {struct blkio_group *blkg;struct hlist_node *n;hlist_for_each_entry_rcu (blkg, n, &blkcg->blkg_list, blkcg_node) -if (blkg->q == q && blkg->plid == plid) +if (blkg->q == q)return blkg;return NULL ; }EXPORT_SYMBOL_GPL (blkg_lookup); -static void blkg_destroy (struct blkio_group *blkg,enum blkio_policy_id plid) +static void blkg_destroy (struct blkio_group *blkg) {struct request_queue *q = blkg->q;lockdep_assert_held (q->queue_lock);/* Something wrong if we are trying to remove same group twice */ -WARN_ON_ONCE (list_empty (&blkg->q_node[plid])); -list_del_init (&blkg->q_node[plid]); +WARN_ON_ONCE (list_empty (&blkg->q_node)); +list_del_init (&blkg->q_node); -WARN_ON_ONCE (q->nr_blkgs[plid] <=0 ); - q->nr_blkgs[plid]--; +WARN_ON_ONCE (q->nr_blkgs <=0 ); + q->nr_blkgs--;/* * Put the reference taken at the time of creation so that when all
@@ -669,18 +680,49 @@ static void blkg_destroy(struct blkio_grblkg_put (blkg); } -void blkg_destroy_all (struct request_queue *q,enum blkio_policy_id plid, -bool destroy_root) +/* +* XXX: This updates blkg policy data in-place for root blkg, which is +* necessary across elevator switch and policy registration as root blkgs +* aren't shot down. This broken and racy implementation is temporary. +* Eventually, blkg shoot down will be replaced by proper in-place update. +*/ +static void update_root_blkg (struct request_queue *q,enum blkio_policy_id plid) +{ +struct blkio_policy_type *pol = blkio_policy[plid]; +struct blkio_group *blkg =blkg_lookup (&blkio_root_cgroup, q); +struct blkg_policy_data *pd; + +if (!blkg) +return ; + +kfree (blkg->pd[plid]); + blkg->pd[plid] =NULL ; + +if (!pol) +return ; + + pd =kzalloc (sizeof (*pd) + pol->pdata_size,GFP_KERNEL ); +WARN_ON_ONCE (!pd); + + pd->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu); +WARN_ON_ONCE (!pd->stats_cpu); + + blkg->pd[plid] = pd; + pd->blkg = blkg; + pol->ops.blkio_init_group_fn (blkg); +} + +void blkg_destroy_all (struct request_queue *q,bool destroy_root) {struct blkio_group *blkg, *n; +int i;while (true) {bool done = true;spin_lock_irq (q->queue_lock); -list_for_each_entry_safe (blkg, n, &q->blkg_list[plid], - q_node[plid]) { +list_for_each_entry_safe (blkg, n, &q->blkg_list, q_node) {/* skip root? */ if (!destroy_root && blkg->blkcg == &blkio_root_cgroup)continue ;
@@ -691,7 +733,7 @@ void blkg_destroy_all(struct request_que* take care of destroying cfqg also. */ if (!blkiocg_del_blkio_group (blkg)) -blkg_destroy (blkg, plid); +blkg_destroy (blkg);else done = false; }
@@ -710,6 +752,9 @@ void blkg_destroy_all(struct request_quemsleep (10 );/* just some random duration I like */ } + +for (i =0 ; i <BLKIO_NR_POLICIES ; i++) +update_root_blkg (q, i); }EXPORT_SYMBOL_GPL (blkg_destroy_all);
@@ -776,43 +821,49 @@ blkiocg_reset_stats(struct cgroup *cgrou#endif blkcg =cgroup_to_blkio_cgroup (cgroup); +spin_lock (&blkio_list_lock);spin_lock_irq (&blkcg->lock);hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkio_policy_type *pol; + +list_for_each_entry (pol, &blkio_list, list) { +struct blkg_policy_data *pd = blkg->pd[pol->plid]; -spin_lock (&blkg->stats_lock); - stats = &pd->stats; +spin_lock (&blkg->stats_lock); + stats = &pd->stats;#ifdef CONFIG_DEBUG_BLK_CGROUP - idling =blkio_blkg_idling (stats); - waiting =blkio_blkg_waiting (stats); - empty =blkio_blkg_empty (stats); + idling =blkio_blkg_idling (stats); + waiting =blkio_blkg_waiting (stats); + empty =blkio_blkg_empty (stats);#endif -for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) - queued[i] = stats->stat_arr[BLKIO_STAT_QUEUED ][i]; -memset (stats,0 ,sizeof (struct blkio_group_stats )); -for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) - stats->stat_arr[BLKIO_STAT_QUEUED ][i] = queued[i]; +for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) + queued[i] = stats->stat_arr[BLKIO_STAT_QUEUED ][i]; +memset (stats,0 ,sizeof (struct blkio_group_stats )); +for (i =0 ; i <BLKIO_STAT_TOTAL ; i++) + stats->stat_arr[BLKIO_STAT_QUEUED ][i] = queued[i];#ifdef CONFIG_DEBUG_BLK_CGROUP -if (idling) { -blkio_mark_blkg_idling (stats); - stats->start_idle_time = now; - } -if (waiting) { -blkio_mark_blkg_waiting (stats); - stats->start_group_wait_time = now; - } -if (empty) { -blkio_mark_blkg_empty (stats); - stats->start_empty_time = now; - } +if (idling) { +blkio_mark_blkg_idling (stats); + stats->start_idle_time = now; + } +if (waiting) { +blkio_mark_blkg_waiting (stats); + stats->start_group_wait_time = now; + } +if (empty) { +blkio_mark_blkg_empty (stats); + stats->start_empty_time = now; + }#endif -spin_unlock (&blkg->stats_lock); +spin_unlock (&blkg->stats_lock); -/* Reset Per cpu stats which don't take blkg->stats_lock */ -blkio_reset_stats_cpu (blkg, blkg->plid); +/* Reset Per cpu stats which don't take blkg->stats_lock */ +blkio_reset_stats_cpu (blkg, pol->plid); + } }spin_unlock_irq (&blkcg->lock); +spin_unlock (&blkio_list_lock);return 0 ; }
@@ -1157,8 +1208,7 @@ static void blkio_read_conf(struct cftypspin_lock_irq (&blkcg->lock);hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) -if (BLKIOFILE_POLICY (cft->private) == blkg->plid) -blkio_print_group_conf (cft, blkg, m); +blkio_print_group_conf (cft, blkg, m);spin_unlock_irq (&blkcg->lock); }
@@ -1213,8 +1263,6 @@ static int blkio_read_blkg_stats(structconst char *dname =dev_name (blkg->q->backing_dev_info.dev);int plid =BLKIOFILE_POLICY (cft->private); -if (plid != blkg->plid) -continue ;if (pcpu) { cgroup_total +=blkio_get_stat_cpu (blkg, plid, cb, dname, type);
@@ -1324,9 +1372,9 @@ static int blkio_weight_write(struct blk blkcg->weight = (unsigned int )val;hlist_for_each_entry (blkg, n, &blkcg->blkg_list, blkcg_node) { -struct blkg_policy_data *pd = blkg->pd[blkg->plid]; +struct blkg_policy_data *pd = blkg->pd[plid]; -if (blkg->plid == plid && !pd->conf.weight) +if (!pd->conf.weight)blkio_update_group_weight (blkg, plid, blkcg->weight); }
@@ -1549,7 +1597,6 @@ static int blkiocg_pre_destroy(struct cgunsigned long flags;struct blkio_group *blkg;struct request_queue *q; -struct blkio_policy_type *blkiop;rcu_read_lock ();
@@ -1575,11 +1622,7 @@ static int blkiocg_pre_destroy(struct cg*/ spin_lock (&blkio_list_lock);spin_lock_irqsave (q->queue_lock, flags); -list_for_each_entry (blkiop, &blkio_list, list) { -if (blkiop->plid != blkg->plid) -continue ; -blkg_destroy (blkg, blkiop->plid); - } +blkg_destroy (blkg);spin_unlock_irqrestore (q->queue_lock, flags);spin_unlock (&blkio_list_lock); }while (1 );
@@ -1673,6 +1716,8 @@ void blkcg_exit_queue(struct request_quelist_del_init (&q->all_q_node);mutex_unlock (&all_q_mutex); +blkg_destroy_all (q, true); +blk_throtl_exit (q); }
@@ -1722,14 +1767,12 @@ static void blkcg_bypass_start(void) __acquires(&all_q_mutex) {struct request_queue *q; -int i;mutex_lock (&all_q_mutex);list_for_each_entry (q, &all_q_list, all_q_node) {blk_queue_bypass_start (q); -for (i =0 ; i <BLKIO_NR_POLICIES ; i++) -blkg_destroy_all (q, i, false); +blkg_destroy_all (q, false); } }
@@ -1746,6 +1789,8 @@ static void blkcg_bypass_end(void)void blkio_policy_register (struct blkio_policy_type *blkiop) { +struct request_queue *q; +blkcg_bypass_start ();spin_lock (&blkio_list_lock);
@@ -1754,12 +1799,16 @@ void blkio_policy_register(struct blkio_list_add_tail (&blkiop->list, &blkio_list);spin_unlock (&blkio_list_lock); +list_for_each_entry (q, &all_q_list, all_q_node) +update_root_blkg (q, blkiop->plid);blkcg_bypass_end (); }EXPORT_SYMBOL_GPL (blkio_policy_register);void blkio_policy_unregister (struct blkio_policy_type *blkiop) { +struct request_queue *q; +blkcg_bypass_start ();spin_lock (&blkio_list_lock);
@@ -1768,6 +1817,8 @@ void blkio_policy_unregister(struct blkilist_del_init (&blkiop->list);spin_unlock (&blkio_list_lock); +list_for_each_entry (q, &all_q_list, all_q_node) +update_root_blkg (q, blkiop->plid);blkcg_bypass_end (); }EXPORT_SYMBOL_GPL (blkio_policy_unregister);
block/blk-cgroup.h +3 -8
@@ -178,13 +178,11 @@ struct blkg_policy_data {struct blkio_group {/* Pointer to the associated request_queue, RCU protected */ struct request_queue __rcu *q; -struct list_head q_node[BLKIO_NR_POLICIES ]; +struct list_head q_node;struct hlist_node blkcg_node;struct blkio_cgroup *blkcg;/* Store cgroup path */ char path[128 ]; -/* policy which owns this blk group */ -enum blkio_policy_id plid;/* reference count */ int refcnt;
@@ -230,8 +228,7 @@ extern void blkcg_exit_queue(struct requ/* Blkio controller policy registration */ extern void blkio_policy_register (struct blkio_policy_type *);extern void blkio_policy_unregister (struct blkio_policy_type *); -extern void blkg_destroy_all (struct request_queue *q, -enum blkio_policy_id plid,bool destroy_root); +extern void blkg_destroy_all (struct request_queue *q,bool destroy_root);/** * blkg_to_pdata - get policy private data
@@ -313,7 +310,6 @@ static inline void blkcg_exit_queue(strustatic inline void blkio_policy_register (struct blkio_policy_type *blkiop) { }static inline void blkio_policy_unregister (struct blkio_policy_type *blkiop) { }static inline void blkg_destroy_all (struct request_queue *q, -enum blkio_policy_id plid,bool destory_root) { } static inline void *blkg_to_pdata(struct blkio_group *blkg,
@@ -382,8 +378,7 @@ extern struct blkio_cgroup *cgroup_to_blextern struct blkio_cgroup *task_blkio_cgroup (struct task_struct *tsk);extern int blkiocg_del_blkio_group (struct blkio_group *blkg);extern struct blkio_group *blkg_lookup (struct blkio_cgroup *blkcg, -struct request_queue *q, -enum blkio_policy_id plid); +struct request_queue *q); struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, struct request_queue *q, enum blkio_policy_id plid,
block/blk-throttle.c +3 -6
@@ -167,7 +167,7 @@ throtl_grp *throtl_lookup_tg(struct throif (blkcg == &blkio_root_cgroup)return td->root_tg; -return blkg_to_tg (blkg_lookup (blkcg, td->queue,BLKIO_POLICY_THROTL )); +return blkg_to_tg (blkg_lookup (blkcg, td->queue)); } static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td,
@@ -704,8 +704,7 @@ static void throtl_process_limit_change(throtl_log (td,"limits changed" ); -list_for_each_entry_safe (blkg, n, &q->blkg_list[BLKIO_POLICY_THROTL ], - q_node[BLKIO_POLICY_THROTL ]) { +list_for_each_entry_safe (blkg, n, &q->blkg_list, q_node) {struct throtl_grp *tg =blkg_to_tg (blkg);if (!tg->limits_changed)
@@ -1054,11 +1053,9 @@ void blk_throtl_exit(struct request_queuthrotl_shutdown_wq (q); -blkg_destroy_all (q,BLKIO_POLICY_THROTL , true); -/* If there are other groups */ spin_lock_irq (q->queue_lock); - wait = q->nr_blkgs[BLKIO_POLICY_THROTL ]; + wait = q->nr_blkgs;spin_unlock_irq (q->queue_lock);/*
block/cfq-iosched.c +1 -3
@@ -3462,15 +3462,13 @@ static void cfq_exit_queue(struct elevatspin_unlock_irq (q->queue_lock); -blkg_destroy_all (q,BLKIO_POLICY_PROP , true); -#ifdef CONFIG_BLK_CGROUP /* * If there are groups which we could not unlink from blkcg list, * wait for a rcu period for them to be freed. */ spin_lock_irq (q->queue_lock); - wait = q->nr_blkgs[BLKIO_POLICY_PROP ]; + wait = q->nr_blkgs;spin_unlock_irq (q->queue_lock);#endif cfq_shutdown_timer_wq (cfqd);
block/blk-core.c +1 -2
@@ -547,8 +547,7 @@ struct request_queue *blk_alloc_queue_noINIT_LIST_HEAD (&q->timeout_list);INIT_LIST_HEAD (&q->icq_list);#ifdef CONFIG_BLK_CGROUP -INIT_LIST_HEAD (&q->blkg_list[0 ]); -INIT_LIST_HEAD (&q->blkg_list[1 ]); +INIT_LIST_HEAD (&q->blkg_list);#endif INIT_LIST_HEAD (&q->flush_queue[0 ]);INIT_LIST_HEAD (&q->flush_queue[1 ]);
block/elevator.c +2 -3
@@ -876,7 +876,7 @@ static int elevator_switch(struct reques {struct elevator_queue *old = q->elevator;bool registered = old->registered; -int i, err; +int err;/* * Turn on BYPASS and drain all requests w/ elevator private data.
@@ -895,8 +895,7 @@ static int elevator_switch(struct requesioc_clear_queue (q);spin_unlock_irq (q->queue_lock); -for (i =0 ; i <BLKIO_NR_POLICIES ; i++) -blkg_destroy_all (q, i, false); +blkg_destroy_all (q, false);/* allocate, init and register new elevator */ err = -ENOMEM ;
block/blk-sysfs.c +2 -2
@@ -480,6 +480,8 @@ static void blk_release_queue(struct kobblk_sync_queue (q); +blkcg_exit_queue (q); +if (q->elevator) {spin_lock_irq (q->queue_lock);ioc_clear_queue (q);
@@ -487,8 +489,6 @@ static void blk_release_queue(struct kobelevator_exit (q->elevator); } -blkcg_exit_queue (q); -if (rl->rq_pool)mempool_destroy (rl->rq_pool);
Re: [PATCH UPDATED2 11/11] blkcg: unify blkg's for blkcg policies
On Mon, Feb 13, 2012 at 05:33:47PM -0800, Tejun Heo wrote:
[..]
Quoted 7 lines
Modified to update root blkgs on elvswitch too.
Jens, there are a couple other changes but they're all trivial. I didn't want to repost both patchsets for those changes. The git branch has been updated. Ping me if you want the full series reposted.
Quoted 6 lines
git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc.gitblkcg-unified-blkgI'd really like to get this into block/core and get some exposure in linux-next. There still are a lot of transitional stuff but I feel that there's now somewhat solid ground to build stuff atop. Vivek, what do you think?
Hi Tejun,
As this patch series is getting longer (already 2 sets are out there), I think it is reasonable to get some testing going in linux-next. I can think of atleast few more things which you might be planning to push before 3.4 merge window opens.
- Cleanup in-place policy data update logic.
- Do not kill all the groups upon one policy switch. Keep the group around and just kill the policy data of associated policy.
- Get rid if locking complexity and rcu around group removal. Replace with double lock dancing stuff.
- Fix the per cpu alloc stat alloc mess. This is anyway needed irrespective of cleanup.
Thanks
Vivek
Tejun Heo
maintainerRe: [PATCH UPDATED2 11/11] blkcg: unify blkg's for blkcg policies
Hello,
Quoted 7 lines · Vivek Goyal
- Cleanup in-place policy data update logic.
- Do not kill all the groups upon one policy switch. Keep the group around and just kill the policy data of associated policy.
- Get rid if locking complexity and rcu around group removal. Replace with double lock dancing stuff.
Yeap, just sent out this one and fix for stacking. I'll probably do the in-place policy update thing next.
Quoted 2 lines
- Fix the per cpu alloc stat alloc mess. This is anyway needed irrespective of cleanup.
I'll ping Andrew again on the mempool thing on more time.
Thanks.
Signature · 1 line
tejun
On Wed, Feb 01, 2012 at 01:19:05PM -0800, Tejun Heo wrote:
[..]
Quoted 3 lines
* use unified stats updated under queue lock and drop percpu stats which should fix locking / context bug across percpu allocation.
Hi Tejun,
Does that mean that stat updation will happen under queue lock even if there are no throttling rules? That will introduce extra queue lock on fast path those who have throttling compiled in but are not using (common case for distributions).
IMHO, we should keep the lockless per cpu stats and do the allocation in worker thread. I was going through my messages and noticed that for a workload queue lock contention had come down by 11% and in a separate testing I could gain 4-5% with PCIe based flash drive for random reads while I saturated the cpus.
Thanks
Vivek
Tejun Heo
maintainerHello,
Quoted 11 lines · Vivek Goyal
On Wed, Feb 01, 2012 at 01:19:05PM -0800, Tejun Heo wrote:
[..]
Quoted 3 lines
* use unified stats updated under queue lock and drop percpu stats which should fix locking / context bug across percpu allocation.
Does that mean that stat updation will happen under queue lock even if there are no throttling rules? That will introduce extra queue lock on fast path those who have throttling compiled in but are not using (common case for distributions).
No, I don't think extra locking would be necessary at all. We end up grabbing queue_lock anyway. It's just the matter of where to update the stats.
Thanks.
Signature · 1 line
tejun
Quoted 10 lines · Tejun Heo
Hello,
Quoted 7 lines · Vivek Goyal
On Wed, Feb 01, 2012 at 01:19:05PM -0800, Tejun Heo wrote:
[..]
Quoted 3 lines
* use unified stats updated under queue lock and drop percpu stats which should fix locking / context bug across percpu allocation.
Quoted 8 lines
Quoted 4 lines
Does that mean that stat updation will happen under queue lock even if there are no throttling rules? That will introduce extra queue lock on fast path those who have throttling compiled in but are not using (common case for distributions).
No, I don't think extra locking would be necessary at all. We end up grabbing
queue_lockanyway. It's just the matter of where to update the stats.
What about bio based drivers? They might have their own internal locking and not relying on queue lock. And conceptually, we first throttle the bio, update the stats and then call into driver. So are you planning to move blk_throtl_bio() call into request function of individual driver to avoid taking queue lock twice.
In fact even for request based drivers, if bio is being merged onto existing request on plug, we will not take any queue lock. So I am not sure how would you avoid extra queue lock for this case.
Thanks
Vivek
Tejun Heo
maintainerHello,
Quoted 9 lines · Vivek Goyal
What about bio based drivers? They might have their own internal locking and not relying on queue lock. And conceptually, we first throttle the bio, update the stats and then call into driver. So are you planning to move
blk_throtl_bio()call into request function of individual driver to avoid taking queue lock twice.In fact even for request based drivers, if bio is being merged onto existing request on plug, we will not take any queue lock. So I am not sure how would you avoid extra queue lock for this case.
For request based drivers, it isn't an issue. If tracking info across plug merging is necessary, just record the states in the plug structure. The whole purpose of per-task plugging is exactly that - buffering state before going through request_lock after all.
For ->make_request_fn() drivers, I'm not sure yet and will have to look through different bio based drivers before deciding. At the worst, we might require them to call stat update function under queue lock or maybe provide fuzzier update or a way to opt out of per-blkg stats. Let's see.
Thanks.
Signature · 1 line
tejun