Re: [RFC PATCH v2 3/8] blk-cgroup: protect blkgs with blkcg_mutex
"yu kuai" <[email protected]> Wed, 29 Jul 2026 15:54:33 +0800
| Newsgroups | org.kernel.vger.cgroups,org.kernel.vger.linux-block |
|---|---|
| Message-ID | <[email protected]> |
Hi, 在 2026/7/29 15:29, Tao Cui 写道: > > 在 2026/7/24 20:30, Yu Kuai 写道: >> From: Yu Kuai <[email protected]> >> >> queue_lock is still needed by block core users, but blkcg no longer needs >> it for blkg topology now that throttle runtime state has a private lock. >> >> Move queue-local blkg synchronization to q->blkcg_mutex. Hold it while >> looking up, creating and destroying blkgs, while preparing and undoing >> configuration, and while activating or deactivating policies. >> >> Update the BFQ, iocost, iolatency and throttle paths which walk >> q->blkg_list or access per-blkg policy state to use the same lock. >> >> blkcg->lock still protects blkcg-local radix tree and list updates. Some >> lookups under blkcg_mutex can race with blkcg updates done for other >> queues, so keep those lookups in RCU read-side critical sections. In >> particular, protect the parent lookup in blkg_create() and the parent walk >> in blkg_lookup_create(). >> >> Now that blkg association happens from the sleepable submit path, the >> queue_lock -> blkcg_mutex conversion lets blkg creation sleep properly. >> Nowait bio handling (failing submit instead of sleeping) is added in a >> follow-up patch. >> > Hi Yu Kuai, > > Quick question on the async blkg free path: it now holds the new mutex > across the per-policy teardown, which can sleep (iocost does timer shutdown > in there, for instance). Since first-IO blkg creation also takes this mutex > now -- it used to be the queue spinlock -- could a first IO for a cgroup end > up waiting on an unrelated cgroup's blkg being torn down on the workqueue? > Not sure it matters in practice; just wondering whether that's a concern at > all, or if I'm overreading how long the mutex is held. I don't think waiting is a problem here in practice, as you said only the first IO for a cgroup will try to hold the mutex. > > Thanks, > Tao> Signed-off-by: Yu Kuai <[email protected]> >> --- >> block/bfq-cgroup.c | 10 +-- >> block/blk-cgroup.c | 182 ++++++++++++++++++++++-------------------- >> block/blk-cgroup.h | 16 ++-- >> block/blk-core.c | 2 + >> block/blk-iocost.c | 5 +- >> block/blk-iolatency.c | 7 +- >> block/blk-throttle.c | 10 +-- >> 7 files changed, 121 insertions(+), 111 deletions(-) >> >> diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c >> index e82ff03bda02..42614aa78cd4 100644 >> --- a/block/bfq-cgroup.c >> +++ b/block/bfq-cgroup.c >> @@ -426,7 +426,7 @@ static void bfqg_stats_xfer_dead(struct bfq_group *bfqg) >> >> parent = bfqg_parent(bfqg); >> >> - lockdep_assert_held(&bfqg_to_blkg(bfqg)->q->queue_lock); >> + lockdep_assert_held(&bfqg_to_blkg(bfqg)->q->blkcg_mutex); >> >> if (unlikely(!parent)) >> return; >> @@ -874,7 +874,7 @@ static void bfq_reparent_active_queues(struct bfq_data *bfqd, >> * and reparent its children entities. >> * @pd: descriptor of the policy going offline. >> * >> - * blkio already grabs the queue_lock for us, so no need to use >> + * blkio already grabs the blkcg_mutex for us, so no need to use >> * RCU-based magic >> */ >> static void bfq_pd_offline(struct blkg_policy_data *pd) >> @@ -947,8 +947,7 @@ void bfq_end_wr_async(struct bfq_data *bfqd) >> struct blkcg_gq *blkg; >> >> mutex_lock(&q->blkcg_mutex); >> - spin_lock_irq(&q->queue_lock); >> - spin_lock(&bfqd->lock); >> + spin_lock_irq(&bfqd->lock); >> >> list_for_each_entry(blkg, &q->blkg_list, q_node) { >> struct bfq_group *bfqg = blkg_to_bfqg(blkg); >> @@ -957,8 +956,7 @@ void bfq_end_wr_async(struct bfq_data *bfqd) >> } >> bfq_end_wr_async_queues(bfqd, bfqd->root_group); >> >> - spin_unlock(&bfqd->lock); >> - spin_unlock_irq(&q->queue_lock); >> + spin_unlock_irq(&bfqd->lock); >> mutex_unlock(&q->blkcg_mutex); >> } >> >> diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c >> index 618e5566fa52..03e1d4d34921 100644 >> --- a/block/blk-cgroup.c >> +++ b/block/blk-cgroup.c >> @@ -131,9 +131,7 @@ static void blkg_free_workfn(struct work_struct *work) >> blkcg_policy[i]->pd_free_fn(blkg->pd[i]); >> if (blkg->parent) >> blkg_put(blkg->parent); >> - spin_lock_irq(&q->queue_lock); >> list_del_init(&blkg->q_node); >> - spin_unlock_irq(&q->queue_lock); >> mutex_unlock(&q->blkcg_mutex); >> >> /* >> @@ -382,7 +380,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, >> struct blkcg_gq *blkg; >> int i, ret; >> >> - lockdep_assert_held(&disk->queue->queue_lock); >> + lockdep_assert_held(&disk->queue->blkcg_mutex); >> >> /* request_queue is dying, do not create/recreate a blkg */ >> if (blk_queue_dying(disk->queue)) { >> @@ -402,12 +400,15 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, >> >> /* link parent */ >> if (blkcg_parent(blkcg)) { >> + rcu_read_lock(); >> blkg->parent = blkg_lookup(blkcg_parent(blkcg), disk->queue); >> if (WARN_ON_ONCE(!blkg->parent)) { >> + rcu_read_unlock(); >> ret = -ENODEV; >> goto err_free_blkg; >> } >> blkg_get(blkg->parent); >> + rcu_read_unlock(); >> } >> >> /* invoke per-policy init */ >> @@ -419,7 +420,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, >> } >> >> /* insert */ >> - spin_lock(&blkcg->lock); >> + spin_lock_irq(&blkcg->lock); >> ret = radix_tree_insert(&blkcg->blkg_tree, disk->queue->id, blkg); >> if (likely(!ret)) { >> hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list); >> @@ -436,7 +437,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, >> } >> blkg->online = true; >> } >> - spin_unlock(&blkcg->lock); >> + spin_unlock_irq(&blkcg->lock); >> >> if (!ret) >> return blkg; >> @@ -459,7 +460,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, >> * Lookup blkg for the @blkcg - @disk pair. If it doesn't exist, try to >> * create one. blkg creation is performed recursively from blkcg_root such >> * that all non-root blkg's have access to the parent blkg. This function >> - * should be called under RCU read lock and takes @disk->queue->queue_lock. >> + * must be called with @disk->queue->blkcg_mutex held. >> * >> * Returns the blkg or the closest blkg if blkg_create() fails as it walks >> * down from root. >> @@ -491,6 +492,7 @@ static struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, >> struct blkcg *parent = blkcg_parent(blkcg); >> struct blkcg_gq *ret_blkg = q->root_blkg; >> >> + rcu_read_lock(); >> while (parent) { >> blkg = blkg_lookup(parent, q); >> if (blkg) { >> @@ -501,6 +503,7 @@ static struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, >> pos = parent; >> parent = blkcg_parent(parent); >> } >> + rcu_read_unlock(); >> >> blkg = blkg_create(pos, disk, NULL); >> if (IS_ERR(blkg)) { >> @@ -519,7 +522,7 @@ static void blkg_destroy(struct blkcg_gq *blkg) >> struct blkcg *blkcg = blkg->blkcg; >> int i; >> >> - lockdep_assert_held(&blkg->q->queue_lock); >> + lockdep_assert_held(&blkg->q->blkcg_mutex); >> lockdep_assert_held(&blkcg->lock); >> >> /* >> @@ -547,8 +550,8 @@ static void blkg_destroy(struct blkcg_gq *blkg) >> hlist_del_init_rcu(&blkg->blkcg_node); >> >> /* >> - * Both setting lookup hint to and clearing it from @blkg are done >> - * under queue_lock. If it's not pointing to @blkg now, it never >> + * Both setting lookup hint to and clearing it from @blkg are done under >> + * blkcg_mutex. If it's not pointing to @blkg now, it never >> * will. Hint assignment itself can race safely. >> */ >> if (rcu_access_pointer(blkcg->blkg_hint) == blkg) >> @@ -569,24 +572,21 @@ static void blkg_destroy_all(struct gendisk *disk) >> int i; >> >> restart: >> - spin_lock_irq(&q->queue_lock); >> + mutex_lock(&q->blkcg_mutex); >> list_for_each_entry(blkg, &q->blkg_list, q_node) { >> struct blkcg *blkcg = blkg->blkcg; >> >> if (hlist_unhashed(&blkg->blkcg_node)) >> continue; >> >> - spin_lock(&blkcg->lock); >> + spin_lock_irq(&blkcg->lock); >> blkg_destroy(blkg); >> - spin_unlock(&blkcg->lock); >> + spin_unlock_irq(&blkcg->lock); >> >> - /* >> - * in order to avoid holding the spin lock for too long, release >> - * it when a batch of blkgs are destroyed. >> - */ >> + /* Avoid holding blkcg_mutex for too long. */ >> if (!(--count)) { >> count = BLKG_DESTROY_BATCH_SIZE; >> - spin_unlock_irq(&q->queue_lock); >> + mutex_unlock(&q->blkcg_mutex); >> cond_resched(); >> goto restart; >> } >> @@ -605,7 +605,7 @@ static void blkg_destroy_all(struct gendisk *disk) >> } >> >> q->root_blkg = NULL; >> - spin_unlock_irq(&q->queue_lock); >> + mutex_unlock(&q->blkcg_mutex); >> >> wake_up_var(&q->root_blkg); >> } >> @@ -822,8 +822,8 @@ EXPORT_SYMBOL_GPL(blkg_conf_open_bdev); >> * @ctx->blkg to the blkg being configured. >> * >> * blkg_conf_open_bdev() must be called on @ctx beforehand. On success, this >> - * function returns with queue lock held and must be followed by >> - * blkg_conf_close_bdev(). >> + * function returns with blkcg_mutex held and must be followed by >> + * blkg_conf_unprep(). >> */ >> int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, >> struct blkg_conf_ctx *ctx) >> @@ -841,7 +841,6 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, >> >> /* Prevent concurrent with blkcg_deactivate_policy() */ >> mutex_lock(&q->blkcg_mutex); >> - spin_lock_irq(&q->queue_lock); >> >> if (!blkcg_policy_enabled(q, pol)) { >> ret = -EOPNOTSUPP; >> @@ -862,35 +861,34 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, >> struct blkcg_gq *new_blkg; >> >> parent = blkcg_parent(blkcg); >> + rcu_read_lock(); >> while (parent && !blkg_lookup(parent, q)) { >> pos = parent; >> parent = blkcg_parent(parent); >> } >> - >> - /* Drop locks to do new blkg allocation with GFP_KERNEL. */ >> - spin_unlock_irq(&q->queue_lock); >> + rcu_read_unlock(); >> >> new_blkg = blkg_alloc(pos, disk, GFP_NOIO); >> if (unlikely(!new_blkg)) { >> ret = -ENOMEM; >> - goto fail_exit; >> + goto fail_unlock; >> } >> >> if (radix_tree_preload(GFP_KERNEL)) { >> blkg_free(new_blkg); >> ret = -ENOMEM; >> - goto fail_exit; >> + goto fail_unlock; >> } >> >> - spin_lock_irq(&q->queue_lock); >> - >> if (!blkcg_policy_enabled(q, pol)) { >> blkg_free(new_blkg); >> ret = -EOPNOTSUPP; >> goto fail_preloaded; >> } >> >> + rcu_read_lock(); >> blkg = blkg_lookup(pos, q); >> + rcu_read_unlock(); >> if (blkg) { >> blkg_free(new_blkg); >> } else { >> @@ -907,15 +905,12 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, >> goto success; >> } >> success: >> - mutex_unlock(&q->blkcg_mutex); >> ctx->blkg = blkg; >> return 0; >> >> fail_preloaded: >> radix_tree_preload_end(); >> fail_unlock: >> - spin_unlock_irq(&q->queue_lock); >> -fail_exit: >> mutex_unlock(&q->blkcg_mutex); >> /* >> * If queue was bypassing, we should retry. Do so after a >> @@ -938,7 +933,7 @@ EXPORT_SYMBOL_GPL(blkg_conf_prep); >> void blkg_conf_unprep(struct blkg_conf_ctx *ctx) >> { >> WARN_ON_ONCE(!ctx->blkg); >> - spin_unlock_irq(&ctx->bdev->bd_disk->queue->queue_lock); >> + mutex_unlock(&ctx->bdev->bd_disk->queue->blkcg_mutex); >> ctx->blkg = NULL; >> } >> EXPORT_SYMBOL_GPL(blkg_conf_unprep); >> @@ -1258,8 +1253,9 @@ static struct blkcg_gq *blkcg_get_first_blkg(struct blkcg *blkcg) >> * blkcg_destroy_blkgs - responsible for shooting down blkgs >> * @blkcg: blkcg of interest >> * >> - * 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. >> + * blkgs should be removed while holding both q->blkcg_mutex and blkcg->lock. >> + * As blkcg->lock is nested inside q->blkcg_mutex, this function performs >> + * reverse double lock dancing. >> * Destroying the blkgs releases the reference held on the blkcg's css allowing >> * blkcg_css_free to eventually be called. >> * >> @@ -1274,13 +1270,13 @@ static void blkcg_destroy_blkgs(struct blkcg *blkcg) >> while ((blkg = blkcg_get_first_blkg(blkcg))) { >> struct request_queue *q = blkg->q; >> >> - spin_lock_irq(&q->queue_lock); >> - spin_lock(&blkcg->lock); >> + mutex_lock(&q->blkcg_mutex); >> + spin_lock_irq(&blkcg->lock); >> >> blkg_destroy(blkg); >> >> - spin_unlock(&blkcg->lock); >> - spin_unlock_irq(&q->queue_lock); >> + spin_unlock_irq(&blkcg->lock); >> + mutex_unlock(&q->blkcg_mutex); >> >> blkg_put(blkg); >> cond_resched(); >> @@ -1472,21 +1468,20 @@ int blkcg_init_disk(struct gendisk *disk) >> preloaded = !radix_tree_preload(GFP_KERNEL); >> >> /* Make sure the root blkg exists. */ >> - /* spin_lock_irq can serve as RCU read-side critical section. */ >> - spin_lock_irq(&q->queue_lock); >> + mutex_lock(&q->blkcg_mutex); >> blkg = blkg_create(&blkcg_root, disk, new_blkg); >> if (IS_ERR(blkg)) >> goto err_unlock; >> q->root_blkg = blkg; >> - spin_unlock_irq(&q->queue_lock); >> >> if (preloaded) >> radix_tree_preload_end(); >> + mutex_unlock(&q->blkcg_mutex); >> >> return 0; >> >> err_unlock: >> - spin_unlock_irq(&q->queue_lock); >> + mutex_unlock(&q->blkcg_mutex); >> if (preloaded) >> radix_tree_preload_end(); >> return PTR_ERR(blkg); >> @@ -1526,6 +1521,42 @@ struct cgroup_subsys io_cgrp_subsys = { >> }; >> EXPORT_SYMBOL_GPL(io_cgrp_subsys); >> >> +static void blkg_free_policy_data(struct blkcg_gq *blkg, >> + const struct blkcg_policy *pol) >> +{ >> + struct blkcg *blkcg = blkg->blkcg; >> + struct blkg_policy_data *pd; >> + bool online = false; >> + >> + lockdep_assert_held(&blkg->q->blkcg_mutex); >> + >> + /* >> + * ->pd_offline_fn() may need blkg->pd[] to stay installed, while >> + * ->pd_free_fn() can sleep. Mark offline under blkcg->lock, run >> + * the offline callback, detach under blkcg->lock, then free. >> + */ >> + spin_lock_irq(&blkcg->lock); >> + pd = blkg->pd[pol->plid]; >> + if (pd) { >> + online = pd->online; >> + pd->online = false; >> + } >> + spin_unlock_irq(&blkcg->lock); >> + >> + if (!pd) >> + return; >> + >> + if (online && pol->pd_offline_fn) >> + pol->pd_offline_fn(pd); >> + >> + spin_lock_irq(&blkcg->lock); >> + WARN_ON_ONCE(blkg->pd[pol->plid] != pd); >> + WRITE_ONCE(blkg->pd[pol->plid], NULL); >> + spin_unlock_irq(&blkcg->lock); >> + >> + pol->pd_free_fn(pd); >> +} >> + >> /** >> * blkcg_activate_policy - activate a blkcg policy on a gendisk >> * @disk: gendisk of interest >> @@ -1535,9 +1566,9 @@ EXPORT_SYMBOL_GPL(io_cgrp_subsys); >> * bypass mode to populate its blkgs with policy_data for @pol. >> * >> * Activation happens with @disk bypassed, so nobody would be accessing blkgs >> - * from IO path. Update of each blkg is protected by both queue and blkcg >> - * locks so that holding either lock and testing blkcg_policy_enabled() is >> - * always enough for dereferencing policy data. >> + * from IO path. Update of each blkg is protected by q->blkcg_mutex and >> + * blkcg->lock so that holding either lock and testing blkcg_policy_enabled() >> + * is always enough for dereferencing policy data. >> * >> * The caller is responsible for synchronizing [de]activations and policy >> * [un]registerations. Returns 0 on success, -errno on failure. >> @@ -1563,8 +1594,9 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol) >> >> if (queue_is_mq(q)) >> memflags = blk_mq_freeze_queue(q); >> + >> retry: >> - spin_lock_irq(&q->queue_lock); >> + mutex_lock(&q->blkcg_mutex); >> >> /* blkg_list is pushed at the head, reverse walk to initialize parents first */ >> list_for_each_entry_reverse(blkg, &q->blkg_list, q_node) { >> @@ -1572,14 +1604,15 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol) >> >> if (blkg->pd[pol->plid]) >> continue; >> + if (hlist_unhashed(&blkg->blkcg_node)) >> + continue; >> >> - /* If prealloc matches, use it; otherwise try GFP_NOWAIT */ >> + /* If prealloc matches, use it; otherwise try GFP_NOWAIT. */ >> if (blkg == pinned_blkg) { >> pd = pd_prealloc; >> pd_prealloc = NULL; >> } else { >> - pd = pol->pd_alloc_fn(disk, blkg->blkcg, >> - GFP_NOWAIT); >> + pd = pol->pd_alloc_fn(disk, blkg->blkcg, GFP_NOWAIT); >> } >> >> if (!pd) { >> @@ -1592,7 +1625,7 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol) >> blkg_get(blkg); >> pinned_blkg = blkg; >> >> - spin_unlock_irq(&q->queue_lock); >> + mutex_unlock(&q->blkcg_mutex); >> >> if (pd_prealloc) >> pol->pd_free_fn(pd_prealloc); >> @@ -1600,11 +1633,10 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol) >> GFP_KERNEL); >> if (pd_prealloc) >> goto retry; >> - else >> - goto enomem; >> + goto enomem; >> } >> >> - spin_lock(&blkg->blkcg->lock); >> + spin_lock_irq(&blkg->blkcg->lock); >> >> pd->blkg = blkg; >> pd->plid = pol->plid; >> @@ -1617,14 +1649,14 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol) >> pol->pd_online_fn(pd); >> pd->online = true; >> >> - spin_unlock(&blkg->blkcg->lock); >> + spin_unlock_irq(&blkg->blkcg->lock); >> } >> >> __set_bit(pol->plid, q->blkcg_pols); >> ret = 0; >> >> - spin_unlock_irq(&q->queue_lock); >> out: >> + mutex_unlock(&q->blkcg_mutex); >> if (queue_is_mq(q)) >> blk_mq_unfreeze_queue(q, memflags); >> if (pinned_blkg) >> @@ -1635,23 +1667,9 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol) >> >> enomem: >> /* alloc failed, take down everything */ >> - spin_lock_irq(&q->queue_lock); >> - list_for_each_entry(blkg, &q->blkg_list, q_node) { >> - struct blkcg *blkcg = blkg->blkcg; >> - struct blkg_policy_data *pd; >> - >> - spin_lock(&blkcg->lock); >> - pd = blkg->pd[pol->plid]; >> - if (pd) { >> - if (pd->online && pol->pd_offline_fn) >> - pol->pd_offline_fn(pd); >> - pd->online = false; >> - pol->pd_free_fn(pd); >> - WRITE_ONCE(blkg->pd[pol->plid], NULL); >> - } >> - spin_unlock(&blkcg->lock); >> - } >> - spin_unlock_irq(&q->queue_lock); >> + mutex_lock(&q->blkcg_mutex); >> + list_for_each_entry(blkg, &q->blkg_list, q_node) >> + blkg_free_policy_data(blkg, pol); >> ret = -ENOMEM; >> goto out; >> } >> @@ -1679,24 +1697,12 @@ void blkcg_deactivate_policy(struct gendisk *disk, >> memflags = blk_mq_freeze_queue(q); >> >> mutex_lock(&q->blkcg_mutex); >> - spin_lock_irq(&q->queue_lock); >> >> __clear_bit(pol->plid, q->blkcg_pols); >> >> - list_for_each_entry(blkg, &q->blkg_list, q_node) { >> - struct blkcg *blkcg = blkg->blkcg; >> + list_for_each_entry(blkg, &q->blkg_list, q_node) >> + blkg_free_policy_data(blkg, pol); >> >> - spin_lock(&blkcg->lock); >> - if (blkg->pd[pol->plid]) { >> - if (blkg->pd[pol->plid]->online && pol->pd_offline_fn) >> - pol->pd_offline_fn(blkg->pd[pol->plid]); >> - pol->pd_free_fn(blkg->pd[pol->plid]); >> - blkg->pd[pol->plid] = NULL; >> - } >> - spin_unlock(&blkcg->lock); >> - } >> - >> - spin_unlock_irq(&q->queue_lock); >> mutex_unlock(&q->blkcg_mutex); >> >> if (queue_is_mq(q)) >> @@ -2087,11 +2093,11 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio, >> * Fast path failed, we're probably issuing IO in this cgroup the first >> * time, hold lock to create new blkg. >> */ >> - spin_lock_irq(&q->queue_lock); >> + mutex_lock(&q->blkcg_mutex); >> blkg = blkg_lookup_create(blkcg, bio->bi_bdev->bd_disk); >> if (blkg) >> blkg = blkg_lookup_tryget(blkg); >> - spin_unlock_irq(&q->queue_lock); >> + mutex_unlock(&q->blkcg_mutex); >> >> return blkg; >> } >> diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h >> index 615390f751aa..5aaf2d54d17e 100644 >> --- a/block/blk-cgroup.h >> +++ b/block/blk-cgroup.h >> @@ -66,7 +66,7 @@ struct blkcg_gq { >> /* reference count */ >> struct percpu_ref refcnt; >> >> - /* is this blkg online? protected by both blkcg and q locks */ >> + /* is this blkg online? protected by blkcg->lock and q->blkcg_mutex */ >> bool online; >> >> struct blkg_iostat_set __percpu *iostat_cpu; >> @@ -224,9 +224,9 @@ int blkg_conf_open_bdev(struct blkg_conf_ctx *ctx) >> __cond_acquires(0, &ctx->bdev->bd_queue->rq_qos_mutex); >> int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, >> struct blkg_conf_ctx *ctx) >> - __cond_acquires(0, &ctx->bdev->bd_disk->queue->queue_lock); >> + __cond_acquires(0, &ctx->bdev->bd_disk->queue->blkcg_mutex); >> void blkg_conf_unprep(struct blkg_conf_ctx *ctx) >> - __releases(ctx->bdev->bd_disk->queue->queue_lock); >> + __releases(ctx->bdev->bd_disk->queue->blkcg_mutex); >> void blkg_conf_close_bdev(struct blkg_conf_ctx *ctx) >> __releases(&ctx->bdev->bd_queue->rq_qos_mutex); >> >> @@ -255,7 +255,7 @@ static inline bool bio_issue_as_root_blkg(struct bio *bio) >> * >> * Lookup blkg for the @blkcg - @q pair. >> * >> - * Must be called in a RCU critical section. >> + * Must be called in a RCU critical section or with q->blkcg_mutex held. >> */ >> static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, >> struct request_queue *q) >> @@ -266,7 +266,7 @@ static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, >> return q->root_blkg; >> >> blkg = rcu_dereference_check(blkcg->blkg_hint, >> - lockdep_is_held(&q->queue_lock)); >> + lockdep_is_held(&q->blkcg_mutex)); >> if (blkg && blkg->q == q) >> return blkg; >> >> @@ -350,9 +350,9 @@ static inline void blkg_put(struct blkcg_gq *blkg) >> * @p_blkg: target blkg to walk descendants of >> * >> * Walk @c_blkg through the descendants of @p_blkg. Must be used with RCU >> - * read locked. If called under either blkcg or queue lock, the iteration >> - * is guaranteed to include all and only online blkgs. The caller may >> - * update @pos_css by calling css_rightmost_descendant() to skip subtree. >> + * read locked. If called under either blkcg->lock or q->blkcg_mutex, the >> + * iteration is guaranteed to include all and only online blkgs. The caller >> + * may update @pos_css by calling css_rightmost_descendant() to skip subtree. >> * @p_blkg is included in the iteration and the first node to be visited. >> */ >> #define blkg_for_each_descendant_pre(d_blkg, pos_css, p_blkg) \ >> diff --git a/block/blk-core.c b/block/blk-core.c >> index 8103643b39fc..0a1d2b09a5d9 100644 >> --- a/block/blk-core.c >> +++ b/block/blk-core.c >> @@ -965,6 +965,8 @@ static void bio_set_ioprio(struct bio *bio) >> */ >> void submit_bio(struct bio *bio) >> { >> + might_sleep(); >> + >> if (bio_op(bio) == REQ_OP_READ) { >> task_io_account_read(bio->bi_iter.bi_size); >> count_vm_events(PGPGIN, bio_sectors(bio)); >> diff --git a/block/blk-iocost.c b/block/blk-iocost.c >> index 8b2aeba2e1e3..ae50d143e4fc 100644 >> --- a/block/blk-iocost.c >> +++ b/block/blk-iocost.c >> @@ -3143,6 +3143,7 @@ static ssize_t ioc_weight_write(struct kernfs_open_file *of, char *buf, >> struct blkg_conf_ctx ctx; >> struct ioc_now now; >> struct ioc_gq *iocg; >> + unsigned long flags; >> u32 v; >> int ret; >> >> @@ -3195,11 +3196,11 @@ static ssize_t ioc_weight_write(struct kernfs_open_file *of, char *buf, >> goto unprep; >> } >> >> - spin_lock(&iocg->ioc->lock); >> + spin_lock_irqsave(&iocg->ioc->lock, flags); >> iocg->cfg_weight = v * WEIGHT_ONE; >> ioc_now(iocg->ioc, &now); >> weight_updated(iocg, &now); >> - spin_unlock(&iocg->ioc->lock); >> + spin_unlock_irqrestore(&iocg->ioc->lock, flags); >> >> ret = 0; >> >> diff --git a/block/blk-iolatency.c b/block/blk-iolatency.c >> index cef02b6c5fa9..30e23fee4f15 100644 >> --- a/block/blk-iolatency.c >> +++ b/block/blk-iolatency.c >> @@ -639,6 +639,7 @@ static void blkcg_iolatency_exit(struct rq_qos *rqos) >> timer_shutdown_sync(&blkiolat->timer); >> flush_work(&blkiolat->enable_work); >> blkcg_deactivate_policy(rqos->disk, &blkcg_policy_iolatency); >> + flush_work(&blkiolat->enable_work); >> kfree(blkiolat); >> } >> >> @@ -811,16 +812,18 @@ static void iolatency_clear_scaling(struct blkcg_gq *blkg) >> if (blkg->parent) { >> struct iolatency_grp *iolat = blkg_to_lat(blkg->parent); >> struct child_latency_info *lat_info; >> + unsigned long flags; >> + >> if (!iolat) >> return; >> >> lat_info = &iolat->child_lat; >> - spin_lock(&lat_info->lock); >> + spin_lock_irqsave(&lat_info->lock, flags); >> atomic_set(&lat_info->scale_cookie, DEFAULT_SCALE_COOKIE); >> lat_info->last_scale_event = 0; >> lat_info->scale_grp = NULL; >> lat_info->scale_lat = 0; >> - spin_unlock(&lat_info->lock); >> + spin_unlock_irqrestore(&lat_info->lock, flags); >> } >> } >> >> diff --git a/block/blk-throttle.c b/block/blk-throttle.c >> index 7bca2805404f..ef3edd5a4785 100644 >> --- a/block/blk-throttle.c >> +++ b/block/blk-throttle.c >> @@ -1777,10 +1777,10 @@ void blk_throtl_cancel_bios(struct gendisk *disk) >> if (!blk_throtl_activated(q)) >> return; >> >> - spin_lock_irq(&q->queue_lock); >> - spin_lock(&td->lock); >> + mutex_lock(&q->blkcg_mutex); >> + spin_lock_irq(&td->lock); >> /* >> - * queue_lock is held, rcu lock is not needed here technically. >> + * blkcg_mutex is held, rcu lock is not needed here technically. >> * However, rcu lock is still held to emphasize that following >> * path need RCU protection and to prevent warning from lockdep. >> */ >> @@ -1797,8 +1797,8 @@ void blk_throtl_cancel_bios(struct gendisk *disk) >> tg_cancel_writeback_bios(blkg_to_tg(blkg), cancel_bios); >> } >> rcu_read_unlock(); >> - spin_unlock(&td->lock); >> - spin_unlock_irq(&q->queue_lock); >> + spin_unlock_irq(&td->lock); >> + mutex_unlock(&q->blkcg_mutex); >> >> for (rw = READ; rw <= WRITE; rw++) { >> struct bio *bio; -- Thanks, Kuai