All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tao Cui <cui.tao@linux.dev>
To: Yu Kuai <yukuai@kernel.org>, axboe@kernel.dk, tj@kernel.org
Cc: cui.tao@linux.dev, hch@lst.de, dongsheng.yang@linux.dev,
	cengku@gmail.com, josef@toxicpanda.com, nilay@linux.ibm.com,
	ming.lei@redhat.com, yukuai@fygo.io, linux-block@vger.kernel.org,
	cgroups@vger.kernel.org
Subject: Re: [RFC PATCH v2 3/8] blk-cgroup: protect blkgs with blkcg_mutex
Date: Wed, 29 Jul 2026 15:29:00 +0800	[thread overview]
Message-ID: <d093d6be-1399-4cb9-9b3a-25518d60e801@linux.dev> (raw)
In-Reply-To: <20260724123037.3004560-4-yukuai@kernel.org>



在 2026/7/24 20:30, Yu Kuai 写道:
> From: Yu Kuai <yukuai@fygo.io>
> 
> 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.

Thanks,
Tao> Signed-off-by: Yu Kuai <yukuai@fygo.io>
> ---
>  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;


  reply	other threads:[~2026-07-29  7:29 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-24 12:30 [RFC PATCH v2 0/8] blk-cgroup: protect blkgs with blkcg_mutex Yu Kuai
2026-07-24 12:30 ` [RFC PATCH v2 1/8] block: associate blkg in submit_bio instead of bio_set_dev Yu Kuai
2026-07-27  8:16   ` yu kuai
2026-07-28  4:23     ` Christoph Hellwig
2026-07-29  8:28       ` yu kuai
2026-07-29  8:35         ` Christoph Hellwig
2026-07-29  8:05     ` Nilay Shroff
2026-07-29  9:49       ` yu kuai
2026-07-29  7:23   ` Tao Cui
2026-07-29  7:49     ` yu kuai
2026-07-24 12:30 ` [RFC PATCH v2 2/8] blk-throttle: protect throttle state with td lock Yu Kuai
2026-07-24 12:30 ` [RFC PATCH v2 3/8] blk-cgroup: protect blkgs with blkcg_mutex Yu Kuai
2026-07-29  7:29   ` Tao Cui [this message]
2026-07-29  7:54     ` yu kuai
2026-07-24 12:30 ` [RFC PATCH v2 4/8] blk-cgroup: remove blkg radix tree preloading Yu Kuai
2026-07-24 12:30 ` [RFC PATCH v2 5/8] blk-cgroup: allocate blkgs in blkg_create Yu Kuai
2026-07-24 12:30 ` [RFC PATCH v2 6/8] blk-cgroup: share blkg creation between lookup and config prep Yu Kuai
2026-07-24 12:30 ` [RFC PATCH v2 7/8] bfq: avoid blkg lookup from locked cgroup update Yu Kuai
2026-07-29  7:33   ` Tao Cui
2026-07-29  7:56     ` yu kuai
2026-07-24 12:30 ` [RFC PATCH v2 8/8] block: fail nowait bio submit if blkg allocation can't sleep Yu Kuai
2026-07-26  7:08 ` [syzbot ci] Re: blk-cgroup: protect blkgs with blkcg_mutex syzbot ci

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d093d6be-1399-4cb9-9b3a-25518d60e801@linux.dev \
    --to=cui.tao@linux.dev \
    --cc=axboe@kernel.dk \
    --cc=cengku@gmail.com \
    --cc=cgroups@vger.kernel.org \
    --cc=dongsheng.yang@linux.dev \
    --cc=hch@lst.de \
    --cc=josef@toxicpanda.com \
    --cc=linux-block@vger.kernel.org \
    --cc=ming.lei@redhat.com \
    --cc=nilay@linux.ibm.com \
    --cc=tj@kernel.org \
    --cc=yukuai@fygo.io \
    --cc=yukuai@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.