Skip to content

Commit

Permalink
blkcg: move bulk of blkcg_gq release operations to the RCU callback
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
htejun committed May 14, 2013
1 parent db61367 commit 2a4fd07
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 20 deletions.
34 changes: 16 additions & 18 deletions block/blk-cgroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,13 +369,17 @@ static void blkg_destroy_all(struct request_queue *q)
q->root_rl.blkg = NULL;
}

static void blkg_rcu_free(struct rcu_head *rcu_head)
{
blkg_free(container_of(rcu_head, struct blkcg_gq, rcu_head));
}

void __blkg_release(struct blkcg_gq *blkg)
/*
* A group is RCU protected, 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 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;

/* tell policies that this one is being freed */
Expand All @@ -388,21 +392,15 @@ void __blkg_release(struct blkcg_gq *blkg)

/* release the blkcg and parent blkg refs this blkg has been holding */
css_put(&blkg->blkcg->css);
if (blkg->parent)
if (blkg->parent) {
spin_lock_irq(blkg->q->queue_lock);
blkg_put(blkg->parent);
spin_unlock_irq(blkg->q->queue_lock);
}

/*
* 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);
blkg_free(blkg);
}
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
Expand Down
4 changes: 2 additions & 2 deletions block/blk-cgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ static inline void blkg_get(struct blkcg_gq *blkg)
blkg->refcnt++;
}

void __blkg_release(struct blkcg_gq *blkg);
void __blkg_release_rcu(struct rcu_head *rcu);

/**
* blkg_put - put a blkg reference
Expand All @@ -279,7 +279,7 @@ static inline void blkg_put(struct blkcg_gq *blkg)
lockdep_assert_held(blkg->q->queue_lock);
WARN_ON_ONCE(blkg->refcnt <= 0);
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,
Expand Down

0 comments on commit 2a4fd07

Please sign in to comment.