diff options
author | Tejun Heo <tj@kernel.org> | 2013-05-14 16:52:31 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2013-05-14 16:52:31 -0400 |
commit | 2a4fd070ee8561d918a3776388331bb7e92ea59e (patch) | |
tree | 53eb9c6df1ea0e02cb3f3442fb85f9e25a4691ed /block | |
parent | db61367038dcd222476881cb09fd54661b3cd508 (diff) |
blkcg: move bulk of blkcg_gq release operations to the RCU callback
Currently, when the last reference of a blkcg_gq is put, all then
release operations sans the actual freeing happen directly in
blkg_put(). As blkg_put() may be called under queue_lock, all
pd_exit_fn()s may be too. This makes it impossible for pd_exit_fn()s
to use del_timer_sync() on timers which grab the queue_lock which is
an irq-safe lock due to the deadlock possibility described in the
comment on top of del_timer_sync().
This can be easily avoided by perfoming the release operations in the
RCU callback instead of directly from blkg_put(). This patch moves
the blkcg_gq release operations to the RCU callback.
As this leaves __blkg_release() with only call_rcu() invocation,
blkg_rcu_free() is renamed to __blkg_release_rcu(), exported and
call_rcu() invocation is now done directly from blkg_put() instead of
going through __blkg_release() which is removed.
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Diffstat (limited to 'block')
-rw-r--r-- | block/blk-cgroup.c | 34 | ||||
-rw-r--r-- | block/blk-cgroup.h | 4 |
2 files changed, 18 insertions, 20 deletions
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 6bbe0a939596..d0747605f56c 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c | |||
@@ -369,13 +369,17 @@ static void blkg_destroy_all(struct request_queue *q) | |||
369 | q->root_rl.blkg = NULL; | 369 | q->root_rl.blkg = NULL; |
370 | } | 370 | } |
371 | 371 | ||
372 | static void blkg_rcu_free(struct rcu_head *rcu_head) | 372 | /* |
373 | { | 373 | * A group is RCU protected, but having an rcu lock does not mean that one |
374 | blkg_free(container_of(rcu_head, struct blkcg_gq, rcu_head)); | 374 | * can access all the fields of blkg and assume these are valid. For |
375 | } | 375 | * example, don't try to follow throtl_data and request queue links. |
376 | 376 | * | |
377 | void __blkg_release(struct blkcg_gq *blkg) | 377 | * Having a reference to blkg under an rcu allows accesses to only values |
378 | * local to groups like group stats and group rate limits. | ||
379 | */ | ||
380 | void __blkg_release_rcu(struct rcu_head *rcu_head) | ||
378 | { | 381 | { |
382 | struct blkcg_gq *blkg = container_of(rcu_head, struct blkcg_gq, rcu_head); | ||
379 | int i; | 383 | int i; |
380 | 384 | ||
381 | /* tell policies that this one is being freed */ | 385 | /* tell policies that this one is being freed */ |
@@ -388,21 +392,15 @@ void __blkg_release(struct blkcg_gq *blkg) | |||
388 | 392 | ||
389 | /* release the blkcg and parent blkg refs this blkg has been holding */ | 393 | /* release the blkcg and parent blkg refs this blkg has been holding */ |
390 | css_put(&blkg->blkcg->css); | 394 | css_put(&blkg->blkcg->css); |
391 | if (blkg->parent) | 395 | if (blkg->parent) { |
396 | spin_lock_irq(blkg->q->queue_lock); | ||
392 | blkg_put(blkg->parent); | 397 | blkg_put(blkg->parent); |
398 | spin_unlock_irq(blkg->q->queue_lock); | ||
399 | } | ||
393 | 400 | ||
394 | /* | 401 | blkg_free(blkg); |
395 | * A group is freed in rcu manner. But having an rcu lock does not | ||
396 | * mean that one can access all the fields of blkg and assume these | ||
397 | * are valid. For example, don't try to follow throtl_data and | ||
398 | * request queue links. | ||
399 | * | ||
400 | * Having a reference to blkg under an rcu allows acess to only | ||
401 | * values local to groups like group stats and group rate limits | ||
402 | */ | ||
403 | call_rcu(&blkg->rcu_head, blkg_rcu_free); | ||
404 | } | 402 | } |
405 | EXPORT_SYMBOL_GPL(__blkg_release); | 403 | EXPORT_SYMBOL_GPL(__blkg_release_rcu); |
406 | 404 | ||
407 | /* | 405 | /* |
408 | * The next function used by blk_queue_for_each_rl(). It's a bit tricky | 406 | * The next function used by blk_queue_for_each_rl(). It's a bit tricky |
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index e15f731d2cdd..8056c03a3382 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h | |||
@@ -266,7 +266,7 @@ static inline void blkg_get(struct blkcg_gq *blkg) | |||
266 | blkg->refcnt++; | 266 | blkg->refcnt++; |
267 | } | 267 | } |
268 | 268 | ||
269 | void __blkg_release(struct blkcg_gq *blkg); | 269 | void __blkg_release_rcu(struct rcu_head *rcu); |
270 | 270 | ||
271 | /** | 271 | /** |
272 | * blkg_put - put a blkg reference | 272 | * blkg_put - put a blkg reference |
@@ -279,7 +279,7 @@ static inline void blkg_put(struct blkcg_gq *blkg) | |||
279 | lockdep_assert_held(blkg->q->queue_lock); | 279 | lockdep_assert_held(blkg->q->queue_lock); |
280 | WARN_ON_ONCE(blkg->refcnt <= 0); | 280 | WARN_ON_ONCE(blkg->refcnt <= 0); |
281 | if (!--blkg->refcnt) | 281 | if (!--blkg->refcnt) |
282 | __blkg_release(blkg); | 282 | call_rcu(&blkg->rcu_head, __blkg_release_rcu); |
283 | } | 283 | } |
284 | 284 | ||
285 | struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, | 285 | struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, |