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>
This commit is contained in:
parent
db61367038
commit
2a4fd070ee
|
@ -369,13 +369,17 @@ static void blkg_destroy_all(struct request_queue *q)
|
||||||
q->root_rl.blkg = NULL;
|
q->root_rl.blkg = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blkg_rcu_free(struct rcu_head *rcu_head)
|
/*
|
||||||
{
|
* A group is RCU protected, but having an rcu lock does not mean that one
|
||||||
blkg_free(container_of(rcu_head, struct blkcg_gq, rcu_head));
|
* 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.
|
||||||
|
*
|
||||||
void __blkg_release(struct blkcg_gq *blkg)
|
* Having a reference to blkg under an rcu allows accesses to only values
|
||||||
|
* local to groups like group stats and group rate limits.
|
||||||
|
*/
|
||||||
|
void __blkg_release_rcu(struct rcu_head *rcu_head)
|
||||||
{
|
{
|
||||||
|
struct blkcg_gq *blkg = container_of(rcu_head, struct blkcg_gq, rcu_head);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* tell policies that this one is being freed */
|
/* tell policies that this one is being freed */
|
||||||
|
@ -388,21 +392,15 @@ void __blkg_release(struct blkcg_gq *blkg)
|
||||||
|
|
||||||
/* release the blkcg and parent blkg refs this blkg has been holding */
|
/* release the blkcg and parent blkg refs this blkg has been holding */
|
||||||
css_put(&blkg->blkcg->css);
|
css_put(&blkg->blkcg->css);
|
||||||
if (blkg->parent)
|
if (blkg->parent) {
|
||||||
|
spin_lock_irq(blkg->q->queue_lock);
|
||||||
blkg_put(blkg->parent);
|
blkg_put(blkg->parent);
|
||||||
|
spin_unlock_irq(blkg->q->queue_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
blkg_free(blkg);
|
||||||
* 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);
|
EXPORT_SYMBOL_GPL(__blkg_release_rcu);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The next function used by blk_queue_for_each_rl(). It's a bit tricky
|
* The next function used by blk_queue_for_each_rl(). It's a bit tricky
|
||||||
|
|
|
@ -266,7 +266,7 @@ static inline void blkg_get(struct blkcg_gq *blkg)
|
||||||
blkg->refcnt++;
|
blkg->refcnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __blkg_release(struct blkcg_gq *blkg);
|
void __blkg_release_rcu(struct rcu_head *rcu);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* blkg_put - put a blkg reference
|
* blkg_put - put a blkg reference
|
||||||
|
@ -279,7 +279,7 @@ static inline void blkg_put(struct blkcg_gq *blkg)
|
||||||
lockdep_assert_held(blkg->q->queue_lock);
|
lockdep_assert_held(blkg->q->queue_lock);
|
||||||
WARN_ON_ONCE(blkg->refcnt <= 0);
|
WARN_ON_ONCE(blkg->refcnt <= 0);
|
||||||
if (!--blkg->refcnt)
|
if (!--blkg->refcnt)
|
||||||
__blkg_release(blkg);
|
call_rcu(&blkg->rcu_head, __blkg_release_rcu);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q,
|
struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q,
|
||||||
|
|
Loading…
Reference in New Issue