Revert "blk-throttle: fix race between blkcg_bio_issue_check() and cgroup_rmdir()"
This reverts commit4c6994806f
. Destroying blkgs is tricky because of the nature of the relationship. A blkg should go away when either a blkcg or a request_queue goes away. However, blkg's pin the blkcg to ensure they remain valid. To break this cycle, when a blkcg is offlined, blkgs put back their css ref. This eventually lets css_free() get called which frees the blkcg. The above commit (4c6994806f
) breaks this order of events by trying to destroy blkgs in css_free(). As the blkgs still hold references to the blkcg, css_free() is never called. The race between blkcg_bio_issue_check() and cgroup_rmdir() will be addressed in the following patch by delaying destruction of a blkg until all writeback associated with the blkcg has been finished. Fixes:4c6994806f
("blk-throttle: fix race between blkcg_bio_issue_check() and cgroup_rmdir()") Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Dennis Zhou <dennisszhou@gmail.com> Cc: Jiufei Xue <jiufei.xue@linux.alibaba.com> Cc: Joseph Qi <joseph.qi@linux.alibaba.com> Cc: Tejun Heo <tj@kernel.org> Cc: Jens Axboe <axboe@kernel.dk> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Родитель
52bd456a66
Коммит
6b06546206
|
@ -310,28 +310,11 @@ struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blkg_pd_offline(struct blkcg_gq *blkg)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
lockdep_assert_held(blkg->q->queue_lock);
|
|
||||||
lockdep_assert_held(&blkg->blkcg->lock);
|
|
||||||
|
|
||||||
for (i = 0; i < BLKCG_MAX_POLS; i++) {
|
|
||||||
struct blkcg_policy *pol = blkcg_policy[i];
|
|
||||||
|
|
||||||
if (blkg->pd[i] && !blkg->pd[i]->offline &&
|
|
||||||
pol->pd_offline_fn) {
|
|
||||||
pol->pd_offline_fn(blkg->pd[i]);
|
|
||||||
blkg->pd[i]->offline = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blkg_destroy(struct blkcg_gq *blkg)
|
static void blkg_destroy(struct blkcg_gq *blkg)
|
||||||
{
|
{
|
||||||
struct blkcg *blkcg = blkg->blkcg;
|
struct blkcg *blkcg = blkg->blkcg;
|
||||||
struct blkcg_gq *parent = blkg->parent;
|
struct blkcg_gq *parent = blkg->parent;
|
||||||
|
int i;
|
||||||
|
|
||||||
lockdep_assert_held(blkg->q->queue_lock);
|
lockdep_assert_held(blkg->q->queue_lock);
|
||||||
lockdep_assert_held(&blkcg->lock);
|
lockdep_assert_held(&blkcg->lock);
|
||||||
|
@ -340,6 +323,13 @@ static void blkg_destroy(struct blkcg_gq *blkg)
|
||||||
WARN_ON_ONCE(list_empty(&blkg->q_node));
|
WARN_ON_ONCE(list_empty(&blkg->q_node));
|
||||||
WARN_ON_ONCE(hlist_unhashed(&blkg->blkcg_node));
|
WARN_ON_ONCE(hlist_unhashed(&blkg->blkcg_node));
|
||||||
|
|
||||||
|
for (i = 0; i < BLKCG_MAX_POLS; i++) {
|
||||||
|
struct blkcg_policy *pol = blkcg_policy[i];
|
||||||
|
|
||||||
|
if (blkg->pd[i] && pol->pd_offline_fn)
|
||||||
|
pol->pd_offline_fn(blkg->pd[i]);
|
||||||
|
}
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
blkg_rwstat_add_aux(&parent->stat_bytes, &blkg->stat_bytes);
|
blkg_rwstat_add_aux(&parent->stat_bytes, &blkg->stat_bytes);
|
||||||
blkg_rwstat_add_aux(&parent->stat_ios, &blkg->stat_ios);
|
blkg_rwstat_add_aux(&parent->stat_ios, &blkg->stat_ios);
|
||||||
|
@ -382,7 +372,6 @@ static void blkg_destroy_all(struct request_queue *q)
|
||||||
struct blkcg *blkcg = blkg->blkcg;
|
struct blkcg *blkcg = blkg->blkcg;
|
||||||
|
|
||||||
spin_lock(&blkcg->lock);
|
spin_lock(&blkcg->lock);
|
||||||
blkg_pd_offline(blkg);
|
|
||||||
blkg_destroy(blkg);
|
blkg_destroy(blkg);
|
||||||
spin_unlock(&blkcg->lock);
|
spin_unlock(&blkcg->lock);
|
||||||
}
|
}
|
||||||
|
@ -1058,25 +1047,25 @@ static struct cftype blkcg_legacy_files[] = {
|
||||||
* @css: css of interest
|
* @css: css of interest
|
||||||
*
|
*
|
||||||
* This function is called when @css is about to go away and responsible
|
* This function is called when @css is about to go away and responsible
|
||||||
* for offlining all blkgs pd and killing all wbs associated with @css.
|
* for shooting down all blkgs associated with @css. blkgs should be
|
||||||
* blkgs pd offline should be done while holding both q and blkcg locks.
|
* removed while holding both q and blkcg locks. As blkcg lock is nested
|
||||||
* As blkcg lock is nested inside q lock, this function performs reverse
|
* inside q lock, this function performs reverse double lock dancing.
|
||||||
* double lock dancing.
|
|
||||||
*
|
*
|
||||||
* This is the blkcg counterpart of ioc_release_fn().
|
* This is the blkcg counterpart of ioc_release_fn().
|
||||||
*/
|
*/
|
||||||
static void blkcg_css_offline(struct cgroup_subsys_state *css)
|
static void blkcg_css_offline(struct cgroup_subsys_state *css)
|
||||||
{
|
{
|
||||||
struct blkcg *blkcg = css_to_blkcg(css);
|
struct blkcg *blkcg = css_to_blkcg(css);
|
||||||
struct blkcg_gq *blkg;
|
|
||||||
|
|
||||||
spin_lock_irq(&blkcg->lock);
|
spin_lock_irq(&blkcg->lock);
|
||||||
|
|
||||||
hlist_for_each_entry(blkg, &blkcg->blkg_list, blkcg_node) {
|
while (!hlist_empty(&blkcg->blkg_list)) {
|
||||||
|
struct blkcg_gq *blkg = hlist_entry(blkcg->blkg_list.first,
|
||||||
|
struct blkcg_gq, blkcg_node);
|
||||||
struct request_queue *q = blkg->q;
|
struct request_queue *q = blkg->q;
|
||||||
|
|
||||||
if (spin_trylock(q->queue_lock)) {
|
if (spin_trylock(q->queue_lock)) {
|
||||||
blkg_pd_offline(blkg);
|
blkg_destroy(blkg);
|
||||||
spin_unlock(q->queue_lock);
|
spin_unlock(q->queue_lock);
|
||||||
} else {
|
} else {
|
||||||
spin_unlock_irq(&blkcg->lock);
|
spin_unlock_irq(&blkcg->lock);
|
||||||
|
@ -1090,43 +1079,11 @@ static void blkcg_css_offline(struct cgroup_subsys_state *css)
|
||||||
wb_blkcg_offline(blkcg);
|
wb_blkcg_offline(blkcg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* blkcg_destroy_all_blkgs - destroy all blkgs associated with a blkcg
|
|
||||||
* @blkcg: blkcg of interest
|
|
||||||
*
|
|
||||||
* This function is called when blkcg css is about to free and responsible for
|
|
||||||
* destroying all blkgs associated with @blkcg.
|
|
||||||
* blkgs should be removed while holding both q and blkcg locks. As blkcg lock
|
|
||||||
* is nested inside q lock, this function performs reverse double lock dancing.
|
|
||||||
*/
|
|
||||||
static void blkcg_destroy_all_blkgs(struct blkcg *blkcg)
|
|
||||||
{
|
|
||||||
spin_lock_irq(&blkcg->lock);
|
|
||||||
while (!hlist_empty(&blkcg->blkg_list)) {
|
|
||||||
struct blkcg_gq *blkg = hlist_entry(blkcg->blkg_list.first,
|
|
||||||
struct blkcg_gq,
|
|
||||||
blkcg_node);
|
|
||||||
struct request_queue *q = blkg->q;
|
|
||||||
|
|
||||||
if (spin_trylock(q->queue_lock)) {
|
|
||||||
blkg_destroy(blkg);
|
|
||||||
spin_unlock(q->queue_lock);
|
|
||||||
} else {
|
|
||||||
spin_unlock_irq(&blkcg->lock);
|
|
||||||
cpu_relax();
|
|
||||||
spin_lock_irq(&blkcg->lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spin_unlock_irq(&blkcg->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blkcg_css_free(struct cgroup_subsys_state *css)
|
static void blkcg_css_free(struct cgroup_subsys_state *css)
|
||||||
{
|
{
|
||||||
struct blkcg *blkcg = css_to_blkcg(css);
|
struct blkcg *blkcg = css_to_blkcg(css);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
blkcg_destroy_all_blkgs(blkcg);
|
|
||||||
|
|
||||||
mutex_lock(&blkcg_pol_mutex);
|
mutex_lock(&blkcg_pol_mutex);
|
||||||
|
|
||||||
list_del(&blkcg->all_blkcgs_node);
|
list_del(&blkcg->all_blkcgs_node);
|
||||||
|
@ -1480,11 +1437,8 @@ void blkcg_deactivate_policy(struct request_queue *q,
|
||||||
|
|
||||||
list_for_each_entry(blkg, &q->blkg_list, q_node) {
|
list_for_each_entry(blkg, &q->blkg_list, q_node) {
|
||||||
if (blkg->pd[pol->plid]) {
|
if (blkg->pd[pol->plid]) {
|
||||||
if (!blkg->pd[pol->plid]->offline &&
|
if (pol->pd_offline_fn)
|
||||||
pol->pd_offline_fn) {
|
|
||||||
pol->pd_offline_fn(blkg->pd[pol->plid]);
|
pol->pd_offline_fn(blkg->pd[pol->plid]);
|
||||||
blkg->pd[pol->plid]->offline = true;
|
|
||||||
}
|
|
||||||
pol->pd_free_fn(blkg->pd[pol->plid]);
|
pol->pd_free_fn(blkg->pd[pol->plid]);
|
||||||
blkg->pd[pol->plid] = NULL;
|
blkg->pd[pol->plid] = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,6 @@ struct blkg_policy_data {
|
||||||
/* the blkg and policy id this per-policy data belongs to */
|
/* the blkg and policy id this per-policy data belongs to */
|
||||||
struct blkcg_gq *blkg;
|
struct blkcg_gq *blkg;
|
||||||
int plid;
|
int plid;
|
||||||
bool offline;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче