AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/25 18:25 flow patch-triage
2m Args:
null

Results:
{
  "EnableConfigs": [
    "CGROUPS",
    "BLK_CGROUP",
    "BLK_CGROUP_IOLATENCY",
    "BLK_CGROUP_IOCOST",
    "BLK_DEV_THROTTLING",
    "IOSCHED_BFQ",
    "BFQ_GROUP_IOSCHED"
  ],
  "FocusSymbols": [
    "bio_associate_blkg",
    "bio_associate_blkg_from_css",
    "bio_disassociate_blkg",
    "blkcg_activate_policy",
    "blkcg_deactivate_policy",
    "blkg_conf_prep",
    "blkg_conf_unprep",
    "blkcg_init_disk",
    "blk_throtl_cancel_bios",
    "__blk_throtl_bio",
    "blk_throtl_exit",
    "bfq_bio_bfqg",
    "bfq_end_wr_async"
  ],
  "Reasoning": "The patch modifies the block cgroup (blkcg) locking mechanism, changing it from using the request queue's `queue_lock` to a dedicated `blkcg_mutex` and the `blkcg-\u003elock`. It also introduces a return value to `bio_associate_blkg` to handle `REQ_NOWAIT` failures gracefully, adds `bio_disassociate_blkg`, and updates various blkcg policies (throttle, iocost, iolatency, bfq) to use the new locking scheme. These are core functional changes to the block layer's cgroup integration and should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/07/25 18:25 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit aea60e0c3e41e5f874bfdb7dccb32ea2cd8b60ef\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sat Jul 25 18:25:00 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c\nindex e82ff03bda02e1..8a3ff95103867b 100644\n--- a/block/bfq-cgroup.c\n+++ b/block/bfq-cgroup.c\n@@ -426,7 +426,7 @@ static void bfqg_stats_xfer_dead(struct bfq_group *bfqg)\n \n \tparent = bfqg_parent(bfqg);\n \n-\tlockdep_assert_held(\u0026bfqg_to_blkg(bfqg)-\u003eq-\u003equeue_lock);\n+\tlockdep_assert_held(\u0026bfqg_to_blkg(bfqg)-\u003eq-\u003eblkcg_mutex);\n \n \tif (unlikely(!parent))\n \t\treturn;\n@@ -604,6 +604,16 @@ static void bfq_link_bfqg(struct bfq_data *bfqd, struct bfq_group *bfqg)\n \t}\n }\n \n+static void bfq_bio_update_blkg(struct bio *bio, struct blkcg_gq *blkg)\n+{\n+\tif (bio-\u003ebi_blkg == blkg)\n+\t\treturn;\n+\n+\tblkg_get(blkg);\n+\tblkg_put(bio-\u003ebi_blkg);\n+\tbio-\u003ebi_blkg = blkg;\n+}\n+\n struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)\n {\n \tstruct blkcg_gq *blkg = bio-\u003ebi_blkg;\n@@ -616,13 +626,13 @@ struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)\n \t\t}\n \t\tbfqg = blkg_to_bfqg(blkg);\n \t\tif (bfqg-\u003epd.online) {\n-\t\t\tbio_associate_blkg_from_css(bio, \u0026blkg-\u003eblkcg-\u003ecss);\n+\t\t\tbfq_bio_update_blkg(bio, blkg);\n \t\t\treturn bfqg;\n \t\t}\n \t\tblkg = blkg-\u003eparent;\n \t}\n-\tbio_associate_blkg_from_css(bio,\n-\t\t\t\t\u0026bfqg_to_blkg(bfqd-\u003eroot_group)-\u003eblkcg-\u003ecss);\n+\tblkg = bfqg_to_blkg(bfqd-\u003eroot_group);\n+\tbfq_bio_update_blkg(bio, blkg);\n \treturn bfqd-\u003eroot_group;\n }\n \n@@ -874,7 +884,7 @@ static void bfq_reparent_active_queues(struct bfq_data *bfqd,\n  *\t\t    and reparent its children entities.\n  * @pd: descriptor of the policy going offline.\n  *\n- * blkio already grabs the queue_lock for us, so no need to use\n+ * blkio already grabs the blkcg_mutex for us, so no need to use\n  * RCU-based magic\n  */\n static void bfq_pd_offline(struct blkg_policy_data *pd)\n@@ -947,8 +957,7 @@ void bfq_end_wr_async(struct bfq_data *bfqd)\n \tstruct blkcg_gq *blkg;\n \n \tmutex_lock(\u0026q-\u003eblkcg_mutex);\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n-\tspin_lock(\u0026bfqd-\u003elock);\n+\tspin_lock_irq(\u0026bfqd-\u003elock);\n \n \tlist_for_each_entry(blkg, \u0026q-\u003eblkg_list, q_node) {\n \t\tstruct bfq_group *bfqg = blkg_to_bfqg(blkg);\n@@ -957,8 +966,7 @@ void bfq_end_wr_async(struct bfq_data *bfqd)\n \t}\n \tbfq_end_wr_async_queues(bfqd, bfqd-\u003eroot_group);\n \n-\tspin_unlock(\u0026bfqd-\u003elock);\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\tspin_unlock_irq(\u0026bfqd-\u003elock);\n \tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n }\n \ndiff --git a/block/bio.c b/block/bio.c\nindex 6a2f6fc3413e89..ea030c5e39a459 100644\n--- a/block/bio.c\n+++ b/block/bio.c\n@@ -179,12 +179,7 @@ static inline gfp_t try_alloc_gfp(gfp_t gfp)\n \n void bio_uninit(struct bio *bio)\n {\n-#ifdef CONFIG_BLK_CGROUP\n-\tif (bio-\u003ebi_blkg) {\n-\t\tblkg_put(bio-\u003ebi_blkg);\n-\t\tbio-\u003ebi_blkg = NULL;\n-\t}\n-#endif\n+\tbio_disassociate_blkg(bio);\n \tif (bio_integrity(bio))\n \t\tbio_integrity_free(bio);\n \n@@ -235,8 +230,6 @@ void bio_init(struct bio *bio, struct block_device *bdev, struct bio_vec *table,\n #ifdef CONFIG_BLK_CGROUP\n \tbio-\u003ebi_blkg = NULL;\n \tbio-\u003eissue_time_ns = 0;\n-\tif (bdev)\n-\t\tbio_associate_blkg(bio);\n #ifdef CONFIG_BLK_CGROUP_IOCOST\n \tbio-\u003ebi_iocost_cost = 0;\n #endif\n@@ -280,8 +273,6 @@ void bio_reset(struct bio *bio, struct block_device *bdev, blk_opf_t opf)\n \tatomic_set(\u0026bio-\u003e__bi_remaining, 1);\n \tbio-\u003ebi_io_vec = bv;\n \tbio-\u003ebi_bdev = bdev;\n-\tif (bio-\u003ebi_bdev)\n-\t\tbio_associate_blkg(bio);\n \tbio-\u003ebi_opf = opf;\n }\n EXPORT_SYMBOL(bio_reset);\n@@ -1803,17 +1794,12 @@ void bio_endio(struct bio *bio)\n \t\tgoto again;\n \t}\n \n-#ifdef CONFIG_BLK_CGROUP\n \t/*\n \t * Release cgroup info.  We shouldn't have to do this here, but quite\n \t * a few callers of bio_init fail to call bio_uninit, so we cover up\n \t * for that here at least for now.\n \t */\n-\tif (bio-\u003ebi_blkg) {\n-\t\tblkg_put(bio-\u003ebi_blkg);\n-\t\tbio-\u003ebi_blkg = NULL;\n-\t}\n-#endif\n+\tbio_disassociate_blkg(bio);\n \n \tif (bio-\u003ebi_end_io)\n \t\tbio-\u003ebi_end_io(bio);\ndiff --git a/block/blk-cgroup.c b/block/blk-cgroup.c\nindex d9676126c5b5da..3c8efba2e69aec 100644\n--- a/block/blk-cgroup.c\n+++ b/block/blk-cgroup.c\n@@ -30,6 +30,7 @@\n #include \u003clinux/resume_user_mode.h\u003e\n #include \u003clinux/psi.h\u003e\n #include \u003clinux/part_stat.h\u003e\n+#include \u003clinux/preempt.h\u003e\n #include \"blk.h\"\n #include \"blk-cgroup.h\"\n #include \"blk-ioprio.h\"\n@@ -131,9 +132,7 @@ static void blkg_free_workfn(struct work_struct *work)\n \t\t\tblkcg_policy[i]-\u003epd_free_fn(blkg-\u003epd[i]);\n \tif (blkg-\u003eparent)\n \t\tblkg_put(blkg-\u003eparent);\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n \tlist_del_init(\u0026blkg-\u003eq_node);\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n \tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \n \t/*\n@@ -372,17 +371,13 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct gendisk *disk,\n \treturn NULL;\n }\n \n-/*\n- * If @new_blkg is %NULL, this function tries to allocate a new one as\n- * necessary using %GFP_NOWAIT.  @new_blkg is always consumed on return.\n- */\n static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,\n-\t\t\t\t    struct blkcg_gq *new_blkg)\n+\t\t\t\t    gfp_t gfp_mask)\n {\n-\tstruct blkcg_gq *blkg;\n+\tstruct blkcg_gq *blkg = NULL;\n \tint i, ret;\n \n-\tlockdep_assert_held(\u0026disk-\u003equeue-\u003equeue_lock);\n+\tlockdep_assert_held(\u0026disk-\u003equeue-\u003eblkcg_mutex);\n \n \t/* request_queue is dying, do not create/recreate a blkg */\n \tif (blk_queue_dying(disk-\u003equeue)) {\n@@ -390,24 +385,23 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,\n \t\tgoto err_free_blkg;\n \t}\n \n-\t/* allocate */\n-\tif (!new_blkg) {\n-\t\tnew_blkg = blkg_alloc(blkcg, disk, GFP_NOWAIT);\n-\t\tif (unlikely(!new_blkg)) {\n-\t\t\tret = -ENOMEM;\n-\t\t\tgoto err_free_blkg;\n-\t\t}\n+\tblkg = blkg_alloc(blkcg, disk, gfp_mask);\n+\tif (unlikely(!blkg)) {\n+\t\tret = -ENOMEM;\n+\t\tgoto err_free_blkg;\n \t}\n-\tblkg = new_blkg;\n \n \t/* link parent */\n \tif (blkcg_parent(blkcg)) {\n+\t\trcu_read_lock();\n \t\tblkg-\u003eparent = blkg_lookup(blkcg_parent(blkcg), disk-\u003equeue);\n \t\tif (WARN_ON_ONCE(!blkg-\u003eparent)) {\n+\t\t\trcu_read_unlock();\n \t\t\tret = -ENODEV;\n \t\t\tgoto err_free_blkg;\n \t\t}\n \t\tblkg_get(blkg-\u003eparent);\n+\t\trcu_read_unlock();\n \t}\n \n \t/* invoke per-policy init */\n@@ -418,8 +412,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,\n \t\t\tpol-\u003epd_init_fn(blkg-\u003epd[i]);\n \t}\n \n-\t/* insert */\n-\tspin_lock(\u0026blkcg-\u003elock);\n+\tspin_lock_irq(\u0026blkcg-\u003elock);\n \tret = radix_tree_insert(\u0026blkcg-\u003eblkg_tree, disk-\u003equeue-\u003eid, blkg);\n \tif (likely(!ret)) {\n \t\thlist_add_head_rcu(\u0026blkg-\u003eblkcg_node, \u0026blkcg-\u003eblkg_list);\n@@ -436,7 +429,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,\n \t\t}\n \t\tblkg-\u003eonline = true;\n \t}\n-\tspin_unlock(\u0026blkcg-\u003elock);\n+\tspin_unlock_irq(\u0026blkcg-\u003elock);\n \n \tif (!ret)\n \t\treturn blkg;\n@@ -446,8 +439,8 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,\n \treturn ERR_PTR(ret);\n \n err_free_blkg:\n-\tif (new_blkg)\n-\t\tblkg_free(new_blkg);\n+\tif (blkg)\n+\t\tblkg_free(blkg);\n \treturn ERR_PTR(ret);\n }\n \n@@ -455,17 +448,19 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,\n  * blkg_lookup_create - lookup blkg, try to create one if not there\n  * @blkcg: blkcg of interest\n  * @disk: gendisk of interest\n+ * @gfp_mask: allocation mask to use\n+ * @blkgp: out parameter for the target blkg, or closest blkg on failure\n  *\n  * Lookup blkg for the @blkcg - @disk pair.  If it doesn't exist, try to\n  * create one.  blkg creation is performed recursively from blkcg_root such\n  * that all non-root blkg's have access to the parent blkg.  This function\n- * should be called under RCU read lock and takes @disk-\u003equeue-\u003equeue_lock.\n+ * must be called with @disk-\u003equeue-\u003eblkcg_mutex held.\n  *\n- * Returns the blkg or the closest blkg if blkg_create() fails as it walks\n- * down from root.\n+ * On success, *@blkgp points to the target blkg and 0 is returned.  On\n+ * failure, *@blkgp points to the closest blkg and the errno is returned.\n  */\n-static struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,\n-\t\tstruct gendisk *disk)\n+static int blkg_lookup_create(struct blkcg *blkcg, struct gendisk *disk,\n+\t\t\t      gfp_t gfp_mask, struct blkcg_gq **blkgp)\n {\n \tstruct request_queue *q = disk-\u003equeue;\n \tstruct blkcg_gq *blkg;\n@@ -477,7 +472,8 @@ static struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,\n \t\t    blkg != rcu_dereference(blkcg-\u003eblkg_hint))\n \t\t\trcu_assign_pointer(blkcg-\u003eblkg_hint, blkg);\n \t\trcu_read_unlock();\n-\t\treturn blkg;\n+\t\t*blkgp = blkg;\n+\t\treturn 0;\n \t}\n \trcu_read_unlock();\n \n@@ -491,6 +487,7 @@ static struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,\n \t\tstruct blkcg *parent = blkcg_parent(blkcg);\n \t\tstruct blkcg_gq *ret_blkg = q-\u003eroot_blkg;\n \n+\t\trcu_read_lock();\n \t\twhile (parent) {\n \t\t\tblkg = blkg_lookup(parent, q);\n \t\t\tif (blkg) {\n@@ -501,17 +498,18 @@ static struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,\n \t\t\tpos = parent;\n \t\t\tparent = blkcg_parent(parent);\n \t\t}\n+\t\trcu_read_unlock();\n \n-\t\tblkg = blkg_create(pos, disk, NULL);\n+\t\tblkg = blkg_create(pos, disk, gfp_mask);\n \t\tif (IS_ERR(blkg)) {\n-\t\t\tblkg = ret_blkg;\n-\t\t\tbreak;\n+\t\t\t*blkgp = ret_blkg;\n+\t\t\treturn PTR_ERR(blkg);\n+\t\t}\n+\t\tif (pos == blkcg) {\n+\t\t\t*blkgp = blkg;\n+\t\t\treturn 0;\n \t\t}\n-\t\tif (pos == blkcg)\n-\t\t\tbreak;\n \t}\n-\n-\treturn blkg;\n }\n \n static void blkg_destroy(struct blkcg_gq *blkg)\n@@ -519,7 +517,7 @@ static void blkg_destroy(struct blkcg_gq *blkg)\n \tstruct blkcg *blkcg = blkg-\u003eblkcg;\n \tint i;\n \n-\tlockdep_assert_held(\u0026blkg-\u003eq-\u003equeue_lock);\n+\tlockdep_assert_held(\u0026blkg-\u003eq-\u003eblkcg_mutex);\n \tlockdep_assert_held(\u0026blkcg-\u003elock);\n \n \t/*\n@@ -547,8 +545,8 @@ static void blkg_destroy(struct blkcg_gq *blkg)\n \thlist_del_init_rcu(\u0026blkg-\u003eblkcg_node);\n \n \t/*\n-\t * Both setting lookup hint to and clearing it from @blkg are done\n-\t * under queue_lock.  If it's not pointing to @blkg now, it never\n+\t * Both setting lookup hint to and clearing it from @blkg are done under\n+\t * blkcg_mutex.  If it's not pointing to @blkg now, it never\n \t * will.  Hint assignment itself can race safely.\n \t */\n \tif (rcu_access_pointer(blkcg-\u003eblkg_hint) == blkg)\n@@ -569,24 +567,21 @@ static void blkg_destroy_all(struct gendisk *disk)\n \tint i;\n \n restart:\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n+\tmutex_lock(\u0026q-\u003eblkcg_mutex);\n \tlist_for_each_entry(blkg, \u0026q-\u003eblkg_list, q_node) {\n \t\tstruct blkcg *blkcg = blkg-\u003eblkcg;\n \n \t\tif (hlist_unhashed(\u0026blkg-\u003eblkcg_node))\n \t\t\tcontinue;\n \n-\t\tspin_lock(\u0026blkcg-\u003elock);\n+\t\tspin_lock_irq(\u0026blkcg-\u003elock);\n \t\tblkg_destroy(blkg);\n-\t\tspin_unlock(\u0026blkcg-\u003elock);\n+\t\tspin_unlock_irq(\u0026blkcg-\u003elock);\n \n-\t\t/*\n-\t\t * in order to avoid holding the spin lock for too long, release\n-\t\t * it when a batch of blkgs are destroyed.\n-\t\t */\n+\t\t/* Avoid holding blkcg_mutex for too long. */\n \t\tif (!(--count)) {\n \t\t\tcount = BLKG_DESTROY_BATCH_SIZE;\n-\t\t\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\t\t\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \t\t\tcond_resched();\n \t\t\tgoto restart;\n \t\t}\n@@ -605,7 +600,7 @@ static void blkg_destroy_all(struct gendisk *disk)\n \t}\n \n \tq-\u003eroot_blkg = NULL;\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \n \twake_up_var(\u0026q-\u003eroot_blkg);\n }\n@@ -822,8 +817,8 @@ EXPORT_SYMBOL_GPL(blkg_conf_open_bdev);\n  * @ctx-\u003eblkg to the blkg being configured.\n  *\n  * blkg_conf_open_bdev() must be called on @ctx beforehand. On success, this\n- * function returns with queue lock held and must be followed by\n- * blkg_conf_close_bdev().\n+ * function returns with blkcg_mutex held and must be followed by\n+ * blkg_conf_unprep().\n  */\n int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,\n \t\t   struct blkg_conf_ctx *ctx)\n@@ -841,81 +836,20 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,\n \n \t/* Prevent concurrent with blkcg_deactivate_policy() */\n \tmutex_lock(\u0026q-\u003eblkcg_mutex);\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n \n \tif (!blkcg_policy_enabled(q, pol)) {\n \t\tret = -EOPNOTSUPP;\n \t\tgoto fail_unlock;\n \t}\n \n-\tblkg = blkg_lookup(blkcg, q);\n-\tif (blkg)\n-\t\tgoto success;\n-\n-\t/*\n-\t * Create blkgs walking down from blkcg_root to @blkcg, so that all\n-\t * non-root blkgs have access to their parents.\n-\t */\n-\twhile (true) {\n-\t\tstruct blkcg *pos = blkcg;\n-\t\tstruct blkcg *parent;\n-\t\tstruct blkcg_gq *new_blkg;\n-\n-\t\tparent = blkcg_parent(blkcg);\n-\t\twhile (parent \u0026\u0026 !blkg_lookup(parent, q)) {\n-\t\t\tpos = parent;\n-\t\t\tparent = blkcg_parent(parent);\n-\t\t}\n-\n-\t\t/* Drop locks to do new blkg allocation with GFP_KERNEL. */\n-\t\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n-\n-\t\tnew_blkg = blkg_alloc(pos, disk, GFP_NOIO);\n-\t\tif (unlikely(!new_blkg)) {\n-\t\t\tret = -ENOMEM;\n-\t\t\tgoto fail_exit;\n-\t\t}\n-\n-\t\tif (radix_tree_preload(GFP_KERNEL)) {\n-\t\t\tblkg_free(new_blkg);\n-\t\t\tret = -ENOMEM;\n-\t\t\tgoto fail_exit;\n-\t\t}\n-\n-\t\tspin_lock_irq(\u0026q-\u003equeue_lock);\n-\n-\t\tif (!blkcg_policy_enabled(q, pol)) {\n-\t\t\tblkg_free(new_blkg);\n-\t\t\tret = -EOPNOTSUPP;\n-\t\t\tgoto fail_preloaded;\n-\t\t}\n-\n-\t\tblkg = blkg_lookup(pos, q);\n-\t\tif (blkg) {\n-\t\t\tblkg_free(new_blkg);\n-\t\t} else {\n-\t\t\tblkg = blkg_create(pos, disk, new_blkg);\n-\t\t\tif (IS_ERR(blkg)) {\n-\t\t\t\tret = PTR_ERR(blkg);\n-\t\t\t\tgoto fail_preloaded;\n-\t\t\t}\n-\t\t}\n-\n-\t\tradix_tree_preload_end();\n+\tret = blkg_lookup_create(blkcg, disk, GFP_NOIO, \u0026blkg);\n+\tif (ret)\n+\t\tgoto fail_unlock;\n \n-\t\tif (pos == blkcg)\n-\t\t\tgoto success;\n-\t}\n-success:\n-\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \tctx-\u003eblkg = blkg;\n \treturn 0;\n \n-fail_preloaded:\n-\tradix_tree_preload_end();\n fail_unlock:\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n-fail_exit:\n \tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \t/*\n \t * If queue was bypassing, we should retry.  Do so after a\n@@ -938,7 +872,7 @@ EXPORT_SYMBOL_GPL(blkg_conf_prep);\n void blkg_conf_unprep(struct blkg_conf_ctx *ctx)\n {\n \tWARN_ON_ONCE(!ctx-\u003eblkg);\n-\tspin_unlock_irq(\u0026ctx-\u003ebdev-\u003ebd_disk-\u003equeue-\u003equeue_lock);\n+\tmutex_unlock(\u0026ctx-\u003ebdev-\u003ebd_disk-\u003equeue-\u003eblkcg_mutex);\n \tctx-\u003eblkg = NULL;\n }\n EXPORT_SYMBOL_GPL(blkg_conf_unprep);\n@@ -1258,8 +1192,9 @@ static struct blkcg_gq *blkcg_get_first_blkg(struct blkcg *blkcg)\n  * blkcg_destroy_blkgs - responsible for shooting down blkgs\n  * @blkcg: blkcg of interest\n  *\n- * blkgs should be removed while holding both q and blkcg locks.  As blkcg lock\n- * is nested inside q lock, this function performs reverse double lock dancing.\n+ * blkgs should be removed while holding both q-\u003eblkcg_mutex and blkcg-\u003elock.\n+ * As blkcg-\u003elock is nested inside q-\u003eblkcg_mutex, this function performs\n+ * reverse double lock dancing.\n  * Destroying the blkgs releases the reference held on the blkcg's css allowing\n  * blkcg_css_free to eventually be called.\n  *\n@@ -1274,13 +1209,13 @@ static void blkcg_destroy_blkgs(struct blkcg *blkcg)\n \twhile ((blkg = blkcg_get_first_blkg(blkcg))) {\n \t\tstruct request_queue *q = blkg-\u003eq;\n \n-\t\tspin_lock_irq(\u0026q-\u003equeue_lock);\n-\t\tspin_lock(\u0026blkcg-\u003elock);\n+\t\tmutex_lock(\u0026q-\u003eblkcg_mutex);\n+\t\tspin_lock_irq(\u0026blkcg-\u003elock);\n \n \t\tblkg_destroy(blkg);\n \n-\t\tspin_unlock(\u0026blkcg-\u003elock);\n-\t\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\t\tspin_unlock_irq(\u0026blkcg-\u003elock);\n+\t\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \n \t\tblkg_put(blkg);\n \t\tcond_resched();\n@@ -1450,8 +1385,7 @@ void blkg_init_queue(struct request_queue *q)\n int blkcg_init_disk(struct gendisk *disk)\n {\n \tstruct request_queue *q = disk-\u003equeue;\n-\tstruct blkcg_gq *new_blkg, *blkg;\n-\tbool preloaded;\n+\tstruct blkcg_gq *blkg;\n \n \t/*\n \t * If the queue is shared across disk rebind (e.g., SCSI), the\n@@ -1465,30 +1399,19 @@ int blkcg_init_disk(struct gendisk *disk)\n \t */\n \twait_var_event(\u0026q-\u003eroot_blkg, !READ_ONCE(q-\u003eroot_blkg));\n \n-\tnew_blkg = blkg_alloc(\u0026blkcg_root, disk, GFP_KERNEL);\n-\tif (!new_blkg)\n-\t\treturn -ENOMEM;\n-\n-\tpreloaded = !radix_tree_preload(GFP_KERNEL);\n-\n \t/* Make sure the root blkg exists. */\n-\t/* spin_lock_irq can serve as RCU read-side critical section. */\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n-\tblkg = blkg_create(\u0026blkcg_root, disk, new_blkg);\n+\tmutex_lock(\u0026q-\u003eblkcg_mutex);\n+\tblkg = blkg_create(\u0026blkcg_root, disk, GFP_KERNEL);\n \tif (IS_ERR(blkg))\n \t\tgoto err_unlock;\n \tq-\u003eroot_blkg = blkg;\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n \n-\tif (preloaded)\n-\t\tradix_tree_preload_end();\n+\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \n \treturn 0;\n \n err_unlock:\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n-\tif (preloaded)\n-\t\tradix_tree_preload_end();\n+\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \treturn PTR_ERR(blkg);\n }\n \n@@ -1526,6 +1449,42 @@ struct cgroup_subsys io_cgrp_subsys = {\n };\n EXPORT_SYMBOL_GPL(io_cgrp_subsys);\n \n+static void blkg_free_policy_data(struct blkcg_gq *blkg,\n+\t\t\t\t  const struct blkcg_policy *pol)\n+{\n+\tstruct blkcg *blkcg = blkg-\u003eblkcg;\n+\tstruct blkg_policy_data *pd;\n+\tbool online = false;\n+\n+\tlockdep_assert_held(\u0026blkg-\u003eq-\u003eblkcg_mutex);\n+\n+\t/*\n+\t * -\u003epd_offline_fn() may need blkg-\u003epd[] to stay installed, while\n+\t * -\u003epd_free_fn() can sleep.  Mark offline under blkcg-\u003elock, run\n+\t * the offline callback, detach under blkcg-\u003elock, then free.\n+\t */\n+\tspin_lock_irq(\u0026blkcg-\u003elock);\n+\tpd = blkg-\u003epd[pol-\u003eplid];\n+\tif (pd) {\n+\t\tonline = pd-\u003eonline;\n+\t\tpd-\u003eonline = false;\n+\t}\n+\tspin_unlock_irq(\u0026blkcg-\u003elock);\n+\n+\tif (!pd)\n+\t\treturn;\n+\n+\tif (online \u0026\u0026 pol-\u003epd_offline_fn)\n+\t\tpol-\u003epd_offline_fn(pd);\n+\n+\tspin_lock_irq(\u0026blkcg-\u003elock);\n+\tWARN_ON_ONCE(blkg-\u003epd[pol-\u003eplid] != pd);\n+\tWRITE_ONCE(blkg-\u003epd[pol-\u003eplid], NULL);\n+\tspin_unlock_irq(\u0026blkcg-\u003elock);\n+\n+\tpol-\u003epd_free_fn(pd);\n+}\n+\n /**\n  * blkcg_activate_policy - activate a blkcg policy on a gendisk\n  * @disk: gendisk of interest\n@@ -1535,9 +1494,9 @@ EXPORT_SYMBOL_GPL(io_cgrp_subsys);\n  * bypass mode to populate its blkgs with policy_data for @pol.\n  *\n  * Activation happens with @disk bypassed, so nobody would be accessing blkgs\n- * from IO path.  Update of each blkg is protected by both queue and blkcg\n- * locks so that holding either lock and testing blkcg_policy_enabled() is\n- * always enough for dereferencing policy data.\n+ * from IO path.  Update of each blkg is protected by q-\u003eblkcg_mutex and\n+ * blkcg-\u003elock so that holding either lock and testing blkcg_policy_enabled()\n+ * is always enough for dereferencing policy data.\n  *\n  * The caller is responsible for synchronizing [de]activations and policy\n  * [un]registerations.  Returns 0 on success, -errno on failure.\n@@ -1545,8 +1504,7 @@ EXPORT_SYMBOL_GPL(io_cgrp_subsys);\n int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)\n {\n \tstruct request_queue *q = disk-\u003equeue;\n-\tstruct blkg_policy_data *pd_prealloc = NULL;\n-\tstruct blkcg_gq *blkg, *pinned_blkg = NULL;\n+\tstruct blkcg_gq *blkg;\n \tunsigned int memflags;\n \tint ret;\n \n@@ -1563,8 +1521,8 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)\n \n \tif (queue_is_mq(q))\n \t\tmemflags = blk_mq_freeze_queue(q);\n-retry:\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n+\n+\tmutex_lock(\u0026q-\u003eblkcg_mutex);\n \n \t/* blkg_list is pushed at the head, reverse walk to initialize parents first */\n \tlist_for_each_entry_reverse(blkg, \u0026q-\u003eblkg_list, q_node) {\n@@ -1572,39 +1530,14 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)\n \n \t\tif (blkg-\u003epd[pol-\u003eplid])\n \t\t\tcontinue;\n+\t\tif (hlist_unhashed(\u0026blkg-\u003eblkcg_node))\n+\t\t\tcontinue;\n \n-\t\t/* If prealloc matches, use it; otherwise try GFP_NOWAIT */\n-\t\tif (blkg == pinned_blkg) {\n-\t\t\tpd = pd_prealloc;\n-\t\t\tpd_prealloc = NULL;\n-\t\t} else {\n-\t\t\tpd = pol-\u003epd_alloc_fn(disk, blkg-\u003eblkcg,\n-\t\t\t\t\t      GFP_NOWAIT);\n-\t\t}\n-\n-\t\tif (!pd) {\n-\t\t\t/*\n-\t\t\t * GFP_NOWAIT failed.  Free the existing one and\n-\t\t\t * prealloc for @blkg w/ GFP_KERNEL.\n-\t\t\t */\n-\t\t\tif (pinned_blkg)\n-\t\t\t\tblkg_put(pinned_blkg);\n-\t\t\tblkg_get(blkg);\n-\t\t\tpinned_blkg = blkg;\n-\n-\t\t\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n-\n-\t\t\tif (pd_prealloc)\n-\t\t\t\tpol-\u003epd_free_fn(pd_prealloc);\n-\t\t\tpd_prealloc = pol-\u003epd_alloc_fn(disk, blkg-\u003eblkcg,\n-\t\t\t\t\t\t       GFP_KERNEL);\n-\t\t\tif (pd_prealloc)\n-\t\t\t\tgoto retry;\n-\t\t\telse\n-\t\t\t\tgoto enomem;\n-\t\t}\n+\t\tpd = pol-\u003epd_alloc_fn(disk, blkg-\u003eblkcg, GFP_NOIO);\n+\t\tif (!pd)\n+\t\t\tgoto enomem;\n \n-\t\tspin_lock(\u0026blkg-\u003eblkcg-\u003elock);\n+\t\tspin_lock_irq(\u0026blkg-\u003eblkcg-\u003elock);\n \n \t\tpd-\u003eblkg = blkg;\n \t\tpd-\u003eplid = pol-\u003eplid;\n@@ -1617,41 +1550,22 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)\n \t\t\tpol-\u003epd_online_fn(pd);\n \t\tpd-\u003eonline = true;\n \n-\t\tspin_unlock(\u0026blkg-\u003eblkcg-\u003elock);\n+\t\tspin_unlock_irq(\u0026blkg-\u003eblkcg-\u003elock);\n \t}\n \n \t__set_bit(pol-\u003eplid, q-\u003eblkcg_pols);\n \tret = 0;\n \n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n out:\n+\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \tif (queue_is_mq(q))\n \t\tblk_mq_unfreeze_queue(q, memflags);\n-\tif (pinned_blkg)\n-\t\tblkg_put(pinned_blkg);\n-\tif (pd_prealloc)\n-\t\tpol-\u003epd_free_fn(pd_prealloc);\n \treturn ret;\n \n enomem:\n \t/* alloc failed, take down everything */\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n-\tlist_for_each_entry(blkg, \u0026q-\u003eblkg_list, q_node) {\n-\t\tstruct blkcg *blkcg = blkg-\u003eblkcg;\n-\t\tstruct blkg_policy_data *pd;\n-\n-\t\tspin_lock(\u0026blkcg-\u003elock);\n-\t\tpd = blkg-\u003epd[pol-\u003eplid];\n-\t\tif (pd) {\n-\t\t\tif (pd-\u003eonline \u0026\u0026 pol-\u003epd_offline_fn)\n-\t\t\t\tpol-\u003epd_offline_fn(pd);\n-\t\t\tpd-\u003eonline = false;\n-\t\t\tpol-\u003epd_free_fn(pd);\n-\t\t\tWRITE_ONCE(blkg-\u003epd[pol-\u003eplid], NULL);\n-\t\t}\n-\t\tspin_unlock(\u0026blkcg-\u003elock);\n-\t}\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\tlist_for_each_entry(blkg, \u0026q-\u003eblkg_list, q_node)\n+\t\tblkg_free_policy_data(blkg, pol);\n \tret = -ENOMEM;\n \tgoto out;\n }\n@@ -1679,24 +1593,12 @@ void blkcg_deactivate_policy(struct gendisk *disk,\n \t\tmemflags = blk_mq_freeze_queue(q);\n \n \tmutex_lock(\u0026q-\u003eblkcg_mutex);\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n \n \t__clear_bit(pol-\u003eplid, q-\u003eblkcg_pols);\n \n-\tlist_for_each_entry(blkg, \u0026q-\u003eblkg_list, q_node) {\n-\t\tstruct blkcg *blkcg = blkg-\u003eblkcg;\n+\tlist_for_each_entry(blkg, \u0026q-\u003eblkg_list, q_node)\n+\t\tblkg_free_policy_data(blkg, pol);\n \n-\t\tspin_lock(\u0026blkcg-\u003elock);\n-\t\tif (blkg-\u003epd[pol-\u003eplid]) {\n-\t\t\tif (blkg-\u003epd[pol-\u003eplid]-\u003eonline \u0026\u0026 pol-\u003epd_offline_fn)\n-\t\t\t\tpol-\u003epd_offline_fn(blkg-\u003epd[pol-\u003eplid]);\n-\t\t\tpol-\u003epd_free_fn(blkg-\u003epd[pol-\u003eplid]);\n-\t\t\tblkg-\u003epd[pol-\u003eplid] = NULL;\n-\t\t}\n-\t\tspin_unlock(\u0026blkcg-\u003elock);\n-\t}\n-\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n \tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \n \tif (queue_is_mq(q))\n@@ -2083,15 +1985,39 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio,\n \tif (blkg)\n \t\treturn blkg;\n \n+\tif (bio-\u003ebi_opf \u0026 REQ_NOWAIT) {\n+\t\tint ret;\n+\n+\t\t/*\n+\t\t * Nowait callers must not sleep on the mutex nor allocate with\n+\t\t * sleeping GFPs.  Trylock the mutex and create the missing blkg\n+\t\t * atomically; if the mutex is contended, the caller is atomic,\n+\t\t * or blkg allocation fails, return NULL so the caller can fail\n+\t\t * the bio and let the submitter retry once the blkg exists.\n+\t\t */\n+\t\tif (!preemptible() || !mutex_trylock(\u0026q-\u003eblkcg_mutex))\n+\t\t\treturn NULL;\n+\n+\t\tret = blkg_lookup_create(blkcg, bio-\u003ebi_bdev-\u003ebd_disk,\n+\t\t\t\t\t GFP_ATOMIC, \u0026blkg);\n+\t\tif (ret)\n+\t\t\tblkg = NULL;\n+\t\telse if (blkg)\n+\t\t\tblkg = blkg_lookup_tryget(blkg);\n+\t\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n+\n+\t\treturn blkg;\n+\t}\n+\n \t/*\n \t * Fast path failed, we're probably issuing IO in this cgroup the first\n \t * time, hold lock to create new blkg.\n \t */\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n-\tblkg = blkg_lookup_create(blkcg, bio-\u003ebi_bdev-\u003ebd_disk);\n+\tmutex_lock(\u0026q-\u003eblkcg_mutex);\n+\tblkg_lookup_create(blkcg, bio-\u003ebi_bdev-\u003ebd_disk, GFP_NOIO, \u0026blkg);\n \tif (blkg)\n \t\tblkg = blkg_lookup_tryget(blkg);\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \n \treturn blkg;\n }\n@@ -2109,19 +2035,33 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio,\n  *\n  * A reference will be taken on the blkg and will be released when @bio is\n  * freed.\n+ *\n+ * If @bio is REQ_NOWAIT and associating requires creating a new blkg, this\n+ * function does not sleep; when the blkg cannot be created atomically it\n+ * returns %false with @bio left unassociated so the caller can fail the I/O.\n+ *\n+ * Return: %true if @bio is associated with a blkg, %false on nowait failure.\n  */\n-void bio_associate_blkg_from_css(struct bio *bio,\n+bool bio_associate_blkg_from_css(struct bio *bio,\n \t\t\t\t struct cgroup_subsys_state *css)\n {\n-\tif (bio-\u003ebi_blkg)\n+\tstruct blkcg_gq *blkg;\n+\n+\tif (bio-\u003ebi_blkg) {\n \t\tblkg_put(bio-\u003ebi_blkg);\n+\t\tbio-\u003ebi_blkg = NULL;\n+\t}\n \n \tif (css \u0026\u0026 css-\u003eparent) {\n-\t\tbio-\u003ebi_blkg = blkg_tryget_closest(bio, css);\n+\t\tblkg = blkg_tryget_closest(bio, css);\n+\t\tif (!blkg)\n+\t\t\treturn false;\n+\t\tbio-\u003ebi_blkg = blkg;\n \t} else {\n \t\tblkg_get(bdev_get_queue(bio-\u003ebi_bdev)-\u003eroot_blkg);\n \t\tbio-\u003ebi_blkg = bdev_get_queue(bio-\u003ebi_bdev)-\u003eroot_blkg;\n \t}\n+\treturn true;\n }\n EXPORT_SYMBOL_GPL(bio_associate_blkg_from_css);\n \n@@ -2129,32 +2069,38 @@ EXPORT_SYMBOL_GPL(bio_associate_blkg_from_css);\n  * bio_associate_blkg - associate a bio with a blkg\n  * @bio: target bio\n  *\n- * Associate @bio with the blkg found from the bio's css and request_queue.\n- * If one is not found, bio_lookup_blkg() creates the blkg.  If a blkg is\n- * already associated, the css is reused and association redone as the\n- * request_queue may have changed.\n+ * Associate @bio with the blkg found from the bio's css and request_queue,\n+ * creating it if necessary.  If a blkg is already associated, the css is\n+ * reused and association redone as the request_queue may have changed.\n+ *\n+ * If @bio is REQ_NOWAIT, association does not sleep; if the blkg cannot be\n+ * created without sleeping, %false is returned with @bio left unassociated.\n+ *\n+ * Return: %true on success, %false on nowait failure.\n  */\n-void bio_associate_blkg(struct bio *bio)\n+bool bio_associate_blkg(struct bio *bio)\n {\n \tstruct cgroup_subsys_state *css;\n+\tbool ret;\n \n \tif (blk_op_is_passthrough(bio-\u003ebi_opf))\n-\t\treturn;\n+\t\treturn true;\n \n \tif (bio-\u003ebi_blkg) {\n \t\tcss = bio_blkcg_css(bio);\n-\t\tbio_associate_blkg_from_css(bio, css);\n-\t} else {\n-\t\trcu_read_lock();\n-\t\tcss = blkcg_css();\n-\t\tif (!css_tryget_online(css))\n-\t\t\tcss = NULL;\n-\t\trcu_read_unlock();\n-\n-\t\tbio_associate_blkg_from_css(bio, css);\n-\t\tif (css)\n-\t\t\tcss_put(css);\n+\t\treturn bio_associate_blkg_from_css(bio, css);\n \t}\n+\n+\trcu_read_lock();\n+\tcss = blkcg_css();\n+\tif (!css_tryget_online(css))\n+\t\tcss = NULL;\n+\trcu_read_unlock();\n+\n+\tret = bio_associate_blkg_from_css(bio, css);\n+\tif (css)\n+\t\tcss_put(css);\n+\treturn ret;\n }\n EXPORT_SYMBOL_GPL(bio_associate_blkg);\n \n@@ -2170,6 +2116,24 @@ void bio_clone_blkg_association(struct bio *dst, struct bio *src)\n }\n EXPORT_SYMBOL_GPL(bio_clone_blkg_association);\n \n+/**\n+ * bio_disassociate_blkg - disassociate a bio from its blkg\n+ * @bio: target bio\n+ *\n+ * Drop the blkg reference held by @bio and clear the association.  This is\n+ * used when a bio's target device changes (e.g. via bio_set_dev()): the old\n+ * blkg is tied to the previous request_queue, so it is dropped here and the\n+ * bio is reassociated to the new queue from the submit path.\n+ */\n+void bio_disassociate_blkg(struct bio *bio)\n+{\n+\tif (bio-\u003ebi_blkg) {\n+\t\tblkg_put(bio-\u003ebi_blkg);\n+\t\tbio-\u003ebi_blkg = NULL;\n+\t}\n+}\n+EXPORT_SYMBOL_GPL(bio_disassociate_blkg);\n+\n static int blk_cgroup_io_type(struct bio *bio)\n {\n \tif (op_is_discard(bio-\u003ebi_opf))\ndiff --git a/block/blk-cgroup.h b/block/blk-cgroup.h\nindex 615390f751aa25..5aaf2d54d17ede 100644\n--- a/block/blk-cgroup.h\n+++ b/block/blk-cgroup.h\n@@ -66,7 +66,7 @@ struct blkcg_gq {\n \t/* reference count */\n \tstruct percpu_ref\t\trefcnt;\n \n-\t/* is this blkg online? protected by both blkcg and q locks */\n+\t/* is this blkg online? protected by blkcg-\u003elock and q-\u003eblkcg_mutex */\n \tbool\t\t\t\tonline;\n \n \tstruct blkg_iostat_set __percpu\t*iostat_cpu;\n@@ -224,9 +224,9 @@ int blkg_conf_open_bdev(struct blkg_conf_ctx *ctx)\n \t__cond_acquires(0, \u0026ctx-\u003ebdev-\u003ebd_queue-\u003erq_qos_mutex);\n int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,\n \t\t   struct blkg_conf_ctx *ctx)\n-\t__cond_acquires(0, \u0026ctx-\u003ebdev-\u003ebd_disk-\u003equeue-\u003equeue_lock);\n+\t__cond_acquires(0, \u0026ctx-\u003ebdev-\u003ebd_disk-\u003equeue-\u003eblkcg_mutex);\n void blkg_conf_unprep(struct blkg_conf_ctx *ctx)\n-\t__releases(ctx-\u003ebdev-\u003ebd_disk-\u003equeue-\u003equeue_lock);\n+\t__releases(ctx-\u003ebdev-\u003ebd_disk-\u003equeue-\u003eblkcg_mutex);\n void blkg_conf_close_bdev(struct blkg_conf_ctx *ctx)\n \t__releases(\u0026ctx-\u003ebdev-\u003ebd_queue-\u003erq_qos_mutex);\n \n@@ -255,7 +255,7 @@ static inline bool bio_issue_as_root_blkg(struct bio *bio)\n  *\n  * Lookup blkg for the @blkcg - @q pair.\n  *\n- * Must be called in a RCU critical section.\n+ * Must be called in a RCU critical section or with q-\u003eblkcg_mutex held.\n  */\n static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg,\n \t\t\t\t\t   struct request_queue *q)\n@@ -266,7 +266,7 @@ static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg,\n \t\treturn q-\u003eroot_blkg;\n \n \tblkg = rcu_dereference_check(blkcg-\u003eblkg_hint,\n-\t\t\tlockdep_is_held(\u0026q-\u003equeue_lock));\n+\t\t\tlockdep_is_held(\u0026q-\u003eblkcg_mutex));\n \tif (blkg \u0026\u0026 blkg-\u003eq == q)\n \t\treturn blkg;\n \n@@ -350,9 +350,9 @@ static inline void blkg_put(struct blkcg_gq *blkg)\n  * @p_blkg: target blkg to walk descendants of\n  *\n  * Walk @c_blkg through the descendants of @p_blkg.  Must be used with RCU\n- * read locked.  If called under either blkcg or queue lock, the iteration\n- * is guaranteed to include all and only online blkgs.  The caller may\n- * update @pos_css by calling css_rightmost_descendant() to skip subtree.\n+ * read locked.  If called under either blkcg-\u003elock or q-\u003eblkcg_mutex, the\n+ * iteration is guaranteed to include all and only online blkgs.  The caller\n+ * may update @pos_css by calling css_rightmost_descendant() to skip subtree.\n  * @p_blkg is included in the iteration and the first node to be visited.\n  */\n #define blkg_for_each_descendant_pre(d_blkg, pos_css, p_blkg)\t\t\\\ndiff --git a/block/blk-core.c b/block/blk-core.c\nindex 365641266c9e82..9b96dd802c92d9 100644\n--- a/block/blk-core.c\n+++ b/block/blk-core.c\n@@ -822,6 +822,28 @@ void submit_bio_noacct(struct bio *bio)\n \n \tmight_sleep();\n \n+\t/*\n+\t * Associate the bio with a blkg for its target queue here, where it is\n+\t * safe to sleep, instead of in bio_set_dev()/bio_init() which may run\n+\t * under a spinlock.  bio_set_dev() no longer associates, so a freshly\n+\t * allocated or remapped bio has no blkg until it reaches the submit path.\n+\t * Reassociate only when the bio was remapped to a different queue since\n+\t * it was last associated; blk_throtl_bio() and the rq_qos throttlers\n+\t * rely on bio-\u003ebi_blkg matching the queue of bio-\u003ebi_bdev.\n+\t */\n+#ifdef CONFIG_BLK_CGROUP\n+\tif (!bio-\u003ebi_blkg || bio-\u003ebi_blkg-\u003eq != q) {\n+\t\t/*\n+\t\t * For REQ_NOWAIT, do not sleep to create a new blkg: fail the\n+\t\t * bio so the submitter retries once the blkg exists.\n+\t\t */\n+\t\tif (!bio_associate_blkg(bio)) {\n+\t\t\tbio_endio_status(bio, BLK_STS_AGAIN);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+#endif\n+\n \t/*\n \t * For a REQ_NOWAIT based request, return -EOPNOTSUPP\n \t * if queue does not support NOWAIT.\n@@ -951,6 +973,8 @@ static void bio_set_ioprio(struct bio *bio)\n  */\n void submit_bio(struct bio *bio)\n {\n+\tmight_sleep();\n+\n \tif (bio_op(bio) == REQ_OP_READ) {\n \t\ttask_io_account_read(bio-\u003ebi_iter.bi_size);\n \t\tcount_vm_events(PGPGIN, bio_sectors(bio));\n@@ -958,6 +982,18 @@ void submit_bio(struct bio *bio)\n \t\tcount_vm_events(PGPGOUT, bio_sectors(bio));\n \t}\n \n+\t/*\n+\t * bio_set_ioprio() -\u003e blkcg_set_ioprio() reads the policy from\n+\t * bio-\u003ebi_blkg, so associate the blkg (for new I/O, the first time) before\n+\t * it runs.  This is the sleepable entry point for new I/O; remapped bios\n+\t * that reach submit_bio_noacct() directly are reassociated there.\n+\t * For REQ_NOWAIT I/O, fail the bio instead of sleeping if a new blkg\n+\t * cannot be allocated.\n+\t */\n+\tif (!bio_associate_blkg(bio)) {\n+\t\tbio_endio_status(bio, BLK_STS_AGAIN);\n+\t\treturn;\n+\t}\n \tbio_set_ioprio(bio);\n \tsubmit_bio_noacct(bio);\n }\ndiff --git a/block/blk-iocost.c b/block/blk-iocost.c\nindex 8b2aeba2e1e39c..ae50d143e4fcca 100644\n--- a/block/blk-iocost.c\n+++ b/block/blk-iocost.c\n@@ -3143,6 +3143,7 @@ static ssize_t ioc_weight_write(struct kernfs_open_file *of, char *buf,\n \tstruct blkg_conf_ctx ctx;\n \tstruct ioc_now now;\n \tstruct ioc_gq *iocg;\n+\tunsigned long flags;\n \tu32 v;\n \tint ret;\n \n@@ -3195,11 +3196,11 @@ static ssize_t ioc_weight_write(struct kernfs_open_file *of, char *buf,\n \t\t\tgoto unprep;\n \t}\n \n-\tspin_lock(\u0026iocg-\u003eioc-\u003elock);\n+\tspin_lock_irqsave(\u0026iocg-\u003eioc-\u003elock, flags);\n \tiocg-\u003ecfg_weight = v * WEIGHT_ONE;\n \tioc_now(iocg-\u003eioc, \u0026now);\n \tweight_updated(iocg, \u0026now);\n-\tspin_unlock(\u0026iocg-\u003eioc-\u003elock);\n+\tspin_unlock_irqrestore(\u0026iocg-\u003eioc-\u003elock, flags);\n \n \tret = 0;\n \ndiff --git a/block/blk-iolatency.c b/block/blk-iolatency.c\nindex cef02b6c5fa91d..30e23fee4f1574 100644\n--- a/block/blk-iolatency.c\n+++ b/block/blk-iolatency.c\n@@ -639,6 +639,7 @@ static void blkcg_iolatency_exit(struct rq_qos *rqos)\n \ttimer_shutdown_sync(\u0026blkiolat-\u003etimer);\n \tflush_work(\u0026blkiolat-\u003eenable_work);\n \tblkcg_deactivate_policy(rqos-\u003edisk, \u0026blkcg_policy_iolatency);\n+\tflush_work(\u0026blkiolat-\u003eenable_work);\n \tkfree(blkiolat);\n }\n \n@@ -811,16 +812,18 @@ static void iolatency_clear_scaling(struct blkcg_gq *blkg)\n \tif (blkg-\u003eparent) {\n \t\tstruct iolatency_grp *iolat = blkg_to_lat(blkg-\u003eparent);\n \t\tstruct child_latency_info *lat_info;\n+\t\tunsigned long flags;\n+\n \t\tif (!iolat)\n \t\t\treturn;\n \n \t\tlat_info = \u0026iolat-\u003echild_lat;\n-\t\tspin_lock(\u0026lat_info-\u003elock);\n+\t\tspin_lock_irqsave(\u0026lat_info-\u003elock, flags);\n \t\tatomic_set(\u0026lat_info-\u003escale_cookie, DEFAULT_SCALE_COOKIE);\n \t\tlat_info-\u003elast_scale_event = 0;\n \t\tlat_info-\u003escale_grp = NULL;\n \t\tlat_info-\u003escale_lat = 0;\n-\t\tspin_unlock(\u0026lat_info-\u003elock);\n+\t\tspin_unlock_irqrestore(\u0026lat_info-\u003elock, flags);\n \t}\n }\n \ndiff --git a/block/blk-throttle.c b/block/blk-throttle.c\nindex ffc3b70065d4b1..ef3edd5a4785be 100644\n--- a/block/blk-throttle.c\n+++ b/block/blk-throttle.c\n@@ -30,6 +30,9 @@ static struct workqueue_struct *kthrotld_workqueue;\n \n struct throtl_data\n {\n+\t/* protects throttle service queues and group runtime state */\n+\tspinlock_t lock;\n+\n \t/* service tree for active throtl groups */\n \tstruct throtl_service_queue service_queue;\n \n@@ -346,11 +349,16 @@ static void tg_update_has_rules(struct throtl_grp *tg)\n static void throtl_pd_online(struct blkg_policy_data *pd)\n {\n \tstruct throtl_grp *tg = pd_to_tg(pd);\n+\tstruct throtl_data *td = tg-\u003etd;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026td-\u003elock, flags);\n \t/*\n \t * We don't want new groups to escape the limits of its ancestors.\n \t * Update has_rules[] after a new group is brought online.\n \t */\n \ttg_update_has_rules(tg);\n+\tspin_unlock_irqrestore(\u0026td-\u003elock, flags);\n }\n \n static void tg_release(struct rcu_head *rcu)\n@@ -368,7 +376,7 @@ static void throtl_pd_free(struct blkg_policy_data *pd)\n {\n \tstruct throtl_grp *tg = pd_to_tg(pd);\n \n-\ttimer_delete_sync(\u0026tg-\u003eservice_queue.pending_timer);\n+\ttimer_shutdown_sync(\u0026tg-\u003eservice_queue.pending_timer);\n \tcall_rcu(\u0026pd-\u003ercu_head, tg_release);\n }\n \n@@ -1142,9 +1150,9 @@ static void throtl_pending_timer_fn(struct timer_list *t)\n \telse\n \t\tq = td-\u003equeue;\n \n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n+\tspin_lock_irq(\u0026td-\u003elock);\n \n-\tif (!q-\u003eroot_blkg)\n+\tif (!READ_ONCE(q-\u003eroot_blkg))\n \t\tgoto out_unlock;\n \n again:\n@@ -1168,9 +1176,9 @@ static void throtl_pending_timer_fn(struct timer_list *t)\n \t\t\tbreak;\n \n \t\t/* this dispatch windows is still open, relax and repeat */\n-\t\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\t\tspin_unlock_irq(\u0026td-\u003elock);\n \t\tcpu_relax();\n-\t\tspin_lock_irq(\u0026q-\u003equeue_lock);\n+\t\tspin_lock_irq(\u0026td-\u003elock);\n \t}\n \n \tif (!dispatched)\n@@ -1193,7 +1201,7 @@ static void throtl_pending_timer_fn(struct timer_list *t)\n \t\tqueue_work(kthrotld_workqueue, \u0026td-\u003edispatch_work);\n \t}\n out_unlock:\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\tspin_unlock_irq(\u0026td-\u003elock);\n }\n \n /**\n@@ -1209,7 +1217,6 @@ static void blk_throtl_dispatch_work_fn(struct work_struct *work)\n \tstruct throtl_data *td = container_of(work, struct throtl_data,\n \t\t\t\t\t      dispatch_work);\n \tstruct throtl_service_queue *td_sq = \u0026td-\u003eservice_queue;\n-\tstruct request_queue *q = td-\u003equeue;\n \tstruct bio_list bio_list_on_stack;\n \tstruct bio *bio;\n \tstruct blk_plug plug;\n@@ -1217,11 +1224,11 @@ static void blk_throtl_dispatch_work_fn(struct work_struct *work)\n \n \tbio_list_init(\u0026bio_list_on_stack);\n \n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n+\tspin_lock_irq(\u0026td-\u003elock);\n \tfor (rw = READ; rw \u003c= WRITE; rw++)\n \t\twhile ((bio = throtl_pop_queued(td_sq, NULL, rw)))\n \t\t\tbio_list_add(\u0026bio_list_on_stack, bio);\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\tspin_unlock_irq(\u0026td-\u003elock);\n \n \tif (!bio_list_empty(\u0026bio_list_on_stack)) {\n \t\tblk_start_plug(\u0026plug);\n@@ -1299,7 +1306,7 @@ static void tg_conf_updated(struct throtl_grp *tg, bool global)\n \trcu_read_unlock();\n \n \t/*\n-\t * We're already holding queue_lock and know @tg is valid.  Let's\n+\t * We're already holding td-\u003elock and know @tg is valid.  Let's\n \t * apply the new config directly.\n \t *\n \t * Restart the slices for both READ and WRITES. It might happen\n@@ -1327,6 +1334,7 @@ static int blk_throtl_init(struct gendisk *disk)\n \t\treturn -ENOMEM;\n \n \tINIT_WORK(\u0026td-\u003edispatch_work, blk_throtl_dispatch_work_fn);\n+\tspin_lock_init(\u0026td-\u003elock);\n \tthrotl_service_queue_init(\u0026td-\u003eservice_queue);\n \n \tmemflags = blk_mq_freeze_queue(disk-\u003equeue);\n@@ -1381,6 +1389,7 @@ static ssize_t tg_set_conf(struct kernfs_open_file *of,\n \t\tv = U64_MAX;\n \n \ttg = blkg_to_tg(ctx.blkg);\n+\tspin_lock_irq(\u0026tg-\u003etd-\u003elock);\n \ttg_update_carryover(tg);\n \n \tif (is_u64)\n@@ -1389,6 +1398,7 @@ static ssize_t tg_set_conf(struct kernfs_open_file *of,\n \t\t*(unsigned int *)((void *)tg + of_cft(of)-\u003eprivate) = v;\n \n \ttg_conf_updated(tg, false);\n+\tspin_unlock_irq(\u0026tg-\u003etd-\u003elock);\n \tret = 0;\n \n unprep:\n@@ -1563,6 +1573,7 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,\n \t\tgoto close_bdev;\n \n \ttg = blkg_to_tg(ctx.blkg);\n+\tspin_lock_irq(\u0026tg-\u003etd-\u003elock);\n \ttg_update_carryover(tg);\n \n \tv[0] = tg-\u003ebps[READ];\n@@ -1586,11 +1597,11 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,\n \t\tp = tok;\n \t\tstrsep(\u0026p, \"=\");\n \t\tif (!p || (sscanf(p, \"%llu\", \u0026val) != 1 \u0026\u0026 strcmp(p, \"max\")))\n-\t\t\tgoto unprep;\n+\t\t\tgoto unlock;\n \n \t\tret = -ERANGE;\n \t\tif (!val)\n-\t\t\tgoto unprep;\n+\t\t\tgoto unlock;\n \n \t\tret = -EINVAL;\n \t\tif (!strcmp(tok, \"rbps\"))\n@@ -1602,7 +1613,7 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,\n \t\telse if (!strcmp(tok, \"wiops\"))\n \t\t\tv[3] = min_t(u64, val, UINT_MAX);\n \t\telse\n-\t\t\tgoto unprep;\n+\t\t\tgoto unlock;\n \t}\n \n \ttg-\u003ebps[READ] = v[0];\n@@ -1611,7 +1622,11 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,\n \ttg-\u003eiops[WRITE] = v[3];\n \n \ttg_conf_updated(tg, false);\n+\tspin_unlock_irq(\u0026tg-\u003etd-\u003elock);\n \tret = 0;\n+\tgoto unprep;\n+unlock:\n+\tspin_unlock_irq(\u0026tg-\u003etd-\u003elock);\n unprep:\n \tblkg_conf_unprep(\u0026ctx);\n close_bdev:\n@@ -1636,6 +1651,28 @@ static void throtl_shutdown_wq(struct request_queue *q)\n \tcancel_work_sync(\u0026td-\u003edispatch_work);\n }\n \n+static void throtl_shutdown_timers(struct request_queue *q)\n+{\n+\tstruct throtl_data *td = q-\u003etd;\n+\tstruct blkcg_gq *blkg;\n+\n+\t/*\n+\t * blkg_destroy_all() has already offlined the policy, but blkg policy\n+\t * data is freed asynchronously.  Shut down per-group timers before\n+\t * freeing td, as their callbacks still dereference tg-\u003etd.\n+\t */\n+\tmutex_lock(\u0026q-\u003eblkcg_mutex);\n+\tlist_for_each_entry(blkg, \u0026q-\u003eblkg_list, q_node) {\n+\t\tstruct throtl_grp *tg = blkg_to_tg(blkg);\n+\n+\t\tif (tg)\n+\t\t\ttimer_shutdown_sync(\u0026tg-\u003eservice_queue.pending_timer);\n+\t}\n+\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n+\n+\ttimer_shutdown_sync(\u0026td-\u003eservice_queue.pending_timer);\n+}\n+\n static void tg_flush_bios(struct throtl_grp *tg)\n {\n \tstruct throtl_service_queue *sq = \u0026tg-\u003eservice_queue;\n@@ -1669,7 +1706,13 @@ static void tg_flush_bios(struct throtl_grp *tg)\n \n static void throtl_pd_offline(struct blkg_policy_data *pd)\n {\n-\ttg_flush_bios(pd_to_tg(pd));\n+\tstruct throtl_grp *tg = pd_to_tg(pd);\n+\tstruct throtl_data *td = tg-\u003etd;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026td-\u003elock, flags);\n+\ttg_flush_bios(tg);\n+\tspin_unlock_irqrestore(\u0026td-\u003elock, flags);\n }\n \n struct blkcg_policy blkcg_policy_throtl = {\n@@ -1725,6 +1768,7 @@ static void tg_cancel_writeback_bios(struct throtl_grp *tg,\n void blk_throtl_cancel_bios(struct gendisk *disk)\n {\n \tstruct request_queue *q = disk-\u003equeue;\n+\tstruct throtl_data *td = q-\u003etd;\n \tstruct cgroup_subsys_state *pos_css;\n \tstruct blkcg_gq *blkg;\n \tstruct bio_list cancel_bios[2] = { };\n@@ -1733,9 +1777,10 @@ void blk_throtl_cancel_bios(struct gendisk *disk)\n \tif (!blk_throtl_activated(q))\n \t\treturn;\n \n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n+\tmutex_lock(\u0026q-\u003eblkcg_mutex);\n+\tspin_lock_irq(\u0026td-\u003elock);\n \t/*\n-\t * queue_lock is held, rcu lock is not needed here technically.\n+\t * blkcg_mutex is held, rcu lock is not needed here technically.\n \t * However, rcu lock is still held to emphasize that following\n \t * path need RCU protection and to prevent warning from lockdep.\n \t */\n@@ -1752,7 +1797,8 @@ void blk_throtl_cancel_bios(struct gendisk *disk)\n \t\ttg_cancel_writeback_bios(blkg_to_tg(blkg), cancel_bios);\n \t}\n \trcu_read_unlock();\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\tspin_unlock_irq(\u0026td-\u003elock);\n+\tmutex_unlock(\u0026q-\u003eblkcg_mutex);\n \n \tfor (rw = READ; rw \u003c= WRITE; rw++) {\n \t\tstruct bio *bio;\n@@ -1791,7 +1837,6 @@ static bool tg_within_limit(struct throtl_grp *tg, struct bio *bio, bool rw)\n \n bool __blk_throtl_bio(struct bio *bio)\n {\n-\tstruct request_queue *q = bdev_get_queue(bio-\u003ebi_bdev);\n \tstruct blkcg_gq *blkg = bio-\u003ebi_blkg;\n \tstruct throtl_qnode *qn = NULL;\n \tstruct throtl_grp *tg = blkg_to_tg(blkg);\n@@ -1801,7 +1846,7 @@ bool __blk_throtl_bio(struct bio *bio)\n \tstruct throtl_data *td = tg-\u003etd;\n \n \trcu_read_lock();\n-\tspin_lock_irq(\u0026q-\u003equeue_lock);\n+\tspin_lock_irq(\u0026td-\u003elock);\n \tsq = \u0026tg-\u003eservice_queue;\n \n \twhile (true) {\n@@ -1877,7 +1922,7 @@ bool __blk_throtl_bio(struct bio *bio)\n \t}\n \n out_unlock:\n-\tspin_unlock_irq(\u0026q-\u003equeue_lock);\n+\tspin_unlock_irq(\u0026td-\u003elock);\n \n \trcu_read_unlock();\n \treturn throttled;\n@@ -1886,17 +1931,19 @@ bool __blk_throtl_bio(struct bio *bio)\n void blk_throtl_exit(struct gendisk *disk)\n {\n \tstruct request_queue *q = disk-\u003equeue;\n+\tstruct throtl_data *td = q-\u003etd;\n \n \t/*\n \t * blkg_destroy_all() already deactivate throtl policy, just check and\n \t * free throtl data.\n \t */\n-\tif (!q-\u003etd)\n+\tif (!td)\n \t\treturn;\n \n-\ttimer_delete_sync(\u0026q-\u003etd-\u003eservice_queue.pending_timer);\n+\tthrotl_shutdown_timers(q);\n \tthrotl_shutdown_wq(q);\n-\tkfree(q-\u003etd);\n+\tq-\u003etd = NULL;\n+\tkfree(td);\n }\n \n static int __init throtl_init(void)\ndiff --git a/include/linux/bio.h b/include/linux/bio.h\nindex 8f33f717b14f5b..570b62e676dc1a 100644\n--- a/include/linux/bio.h\n+++ b/include/linux/bio.h\n@@ -503,18 +503,19 @@ static inline void bio_release_pages(struct bio *bio, bool mark_dirty)\n \tdisk_devt((bio)-\u003ebi_bdev-\u003ebd_disk)\n \n #ifdef CONFIG_BLK_CGROUP\n-void bio_associate_blkg(struct bio *bio);\n-void bio_associate_blkg_from_css(struct bio *bio,\n+bool bio_associate_blkg(struct bio *bio);\n+bool bio_associate_blkg_from_css(struct bio *bio,\n \t\t\t\t struct cgroup_subsys_state *css);\n void bio_clone_blkg_association(struct bio *dst, struct bio *src);\n+void bio_disassociate_blkg(struct bio *bio);\n void blkcg_punt_bio_submit(struct bio *bio);\n #else\t/* CONFIG_BLK_CGROUP */\n-static inline void bio_associate_blkg(struct bio *bio) { }\n-static inline void bio_associate_blkg_from_css(struct bio *bio,\n-\t\t\t\t\t       struct cgroup_subsys_state *css)\n-{ }\n+static inline bool bio_associate_blkg(struct bio *bio) { return true; }\n+static inline bool bio_associate_blkg_from_css(struct bio *bio,\n+\t\t\t\t\t       struct cgroup_subsys_state *css) { return true; }\n static inline void bio_clone_blkg_association(struct bio *dst,\n \t\t\t\t\t      struct bio *src) { }\n+static inline void bio_disassociate_blkg(struct bio *bio) { }\n static inline void blkcg_punt_bio_submit(struct bio *bio)\n {\n \tsubmit_bio(bio);\n@@ -524,10 +525,11 @@ static inline void blkcg_punt_bio_submit(struct bio *bio)\n static inline void bio_set_dev(struct bio *bio, struct block_device *bdev)\n {\n \tbio_clear_flag(bio, BIO_REMAPPED);\n-\tif (bio-\u003ebi_bdev != bdev)\n+\tif (bio-\u003ebi_bdev != bdev) {\n \t\tbio_clear_flag(bio, BIO_BPS_THROTTLED);\n+\t\tbio_disassociate_blkg(bio);\n+\t}\n \tbio-\u003ebi_bdev = bdev;\n-\tbio_associate_blkg(bio);\n }\n \n /*\n"
}

2/1 2026/07/25 18:25 agent patch-evaluator
2m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "CGROUPS",
    "BLK_CGROUP",
    "BLK_CGROUP_IOLATENCY",
    "BLK_CGROUP_IOCOST",
    "BLK_DEV_THROTTLING",
    "IOSCHED_BFQ",
    "BFQ_GROUP_IOSCHED"
  ],
  "FocusSymbols": [
    "bio_associate_blkg",
    "bio_associate_blkg_from_css",
    "bio_disassociate_blkg",
    "blkcg_activate_policy",
    "blkcg_deactivate_policy",
    "blkg_conf_prep",
    "blkg_conf_unprep",
    "blkcg_init_disk",
    "blk_throtl_cancel_bios",
    "__blk_throtl_bio",
    "blk_throtl_exit",
    "bfq_bio_bfqg",
    "bfq_end_wr_async"
  ],
  "Reasoning": "The patch modifies the block cgroup (blkcg) locking mechanism, changing it from using the request queue's `queue_lock` to a dedicated `blkcg_mutex` and the `blkcg-\u003elock`. It also introduces a return value to `bio_associate_blkg` to handle `REQ_NOWAIT` failures gracefully, adds `bio_disassociate_blkg`, and updates various blkcg policies (throttle, iocost, iolatency, bfq) to use the new locking scheme. These are core functional changes to the block layer's cgroup integration and should be fuzzed.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit aea60e0c3e41e5f874bfdb7dccb32ea2cd8b60ef
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Jul 25 18:25:00 2026 +0000

    syz-cluster: applied patch under review

diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c
index e82ff03bda02e1..8a3ff95103867b 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;
@@ -604,6 +604,16 @@ static void bfq_link_bfqg(struct bfq_data *bfqd, struct bfq_group *bfqg)
 	}
 }
 
+static void bfq_bio_update_blkg(struct bio *bio, struct blkcg_gq *blkg)
+{
+	if (bio->bi_blkg == blkg)
+		return;
+
+	blkg_get(blkg);
+	blkg_put(bio->bi_blkg);
+	bio->bi_blkg = blkg;
+}
+
 struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)
 {
 	struct blkcg_gq *blkg = bio->bi_blkg;
@@ -616,13 +626,13 @@ struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)
 		}
 		bfqg = blkg_to_bfqg(blkg);
 		if (bfqg->pd.online) {
-			bio_associate_blkg_from_css(bio, &blkg->blkcg->css);
+			bfq_bio_update_blkg(bio, blkg);
 			return bfqg;
 		}
 		blkg = blkg->parent;
 	}
-	bio_associate_blkg_from_css(bio,
-				&bfqg_to_blkg(bfqd->root_group)->blkcg->css);
+	blkg = bfqg_to_blkg(bfqd->root_group);
+	bfq_bio_update_blkg(bio, blkg);
 	return bfqd->root_group;
 }
 
@@ -874,7 +884,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 +957,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 +966,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/bio.c b/block/bio.c
index 6a2f6fc3413e89..ea030c5e39a459 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -179,12 +179,7 @@ static inline gfp_t try_alloc_gfp(gfp_t gfp)
 
 void bio_uninit(struct bio *bio)
 {
-#ifdef CONFIG_BLK_CGROUP
-	if (bio->bi_blkg) {
-		blkg_put(bio->bi_blkg);
-		bio->bi_blkg = NULL;
-	}
-#endif
+	bio_disassociate_blkg(bio);
 	if (bio_integrity(bio))
 		bio_integrity_free(bio);
 
@@ -235,8 +230,6 @@ void bio_init(struct bio *bio, struct block_device *bdev, struct bio_vec *table,
 #ifdef CONFIG_BLK_CGROUP
 	bio->bi_blkg = NULL;
 	bio->issue_time_ns = 0;
-	if (bdev)
-		bio_associate_blkg(bio);
 #ifdef CONFIG_BLK_CGROUP_IOCOST
 	bio->bi_iocost_cost = 0;
 #endif
@@ -280,8 +273,6 @@ void bio_reset(struct bio *bio, struct block_device *bdev, blk_opf_t opf)
 	atomic_set(&bio->__bi_remaining, 1);
 	bio->bi_io_vec = bv;
 	bio->bi_bdev = bdev;
-	if (bio->bi_bdev)
-		bio_associate_blkg(bio);
 	bio->bi_opf = opf;
 }
 EXPORT_SYMBOL(bio_reset);
@@ -1803,17 +1794,12 @@ void bio_endio(struct bio *bio)
 		goto again;
 	}
 
-#ifdef CONFIG_BLK_CGROUP
 	/*
 	 * Release cgroup info.  We shouldn't have to do this here, but quite
 	 * a few callers of bio_init fail to call bio_uninit, so we cover up
 	 * for that here at least for now.
 	 */
-	if (bio->bi_blkg) {
-		blkg_put(bio->bi_blkg);
-		bio->bi_blkg = NULL;
-	}
-#endif
+	bio_disassociate_blkg(bio);
 
 	if (bio->bi_end_io)
 		bio->bi_end_io(bio);
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index d9676126c5b5da..3c8efba2e69aec 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -30,6 +30,7 @@
 #include <linux/resume_user_mode.h>
 #include <linux/psi.h>
 #include <linux/part_stat.h>
+#include <linux/preempt.h>
 #include "blk.h"
 #include "blk-cgroup.h"
 #include "blk-ioprio.h"
@@ -131,9 +132,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);
 
 	/*
@@ -372,17 +371,13 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct gendisk *disk,
 	return NULL;
 }
 
-/*
- * If @new_blkg is %NULL, this function tries to allocate a new one as
- * necessary using %GFP_NOWAIT.  @new_blkg is always consumed on return.
- */
 static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,
-				    struct blkcg_gq *new_blkg)
+				    gfp_t gfp_mask)
 {
-	struct blkcg_gq *blkg;
+	struct blkcg_gq *blkg = NULL;
 	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)) {
@@ -390,24 +385,23 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,
 		goto err_free_blkg;
 	}
 
-	/* allocate */
-	if (!new_blkg) {
-		new_blkg = blkg_alloc(blkcg, disk, GFP_NOWAIT);
-		if (unlikely(!new_blkg)) {
-			ret = -ENOMEM;
-			goto err_free_blkg;
-		}
+	blkg = blkg_alloc(blkcg, disk, gfp_mask);
+	if (unlikely(!blkg)) {
+		ret = -ENOMEM;
+		goto err_free_blkg;
 	}
-	blkg = new_blkg;
 
 	/* 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 */
@@ -418,8 +412,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,
 			pol->pd_init_fn(blkg->pd[i]);
 	}
 
-	/* 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 +429,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;
@@ -446,8 +439,8 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,
 	return ERR_PTR(ret);
 
 err_free_blkg:
-	if (new_blkg)
-		blkg_free(new_blkg);
+	if (blkg)
+		blkg_free(blkg);
 	return ERR_PTR(ret);
 }
 
@@ -455,17 +448,19 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,
  * blkg_lookup_create - lookup blkg, try to create one if not there
  * @blkcg: blkcg of interest
  * @disk: gendisk of interest
+ * @gfp_mask: allocation mask to use
+ * @blkgp: out parameter for the target blkg, or closest blkg on failure
  *
  * 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.
+ * On success, *@blkgp points to the target blkg and 0 is returned.  On
+ * failure, *@blkgp points to the closest blkg and the errno is returned.
  */
-static struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
-		struct gendisk *disk)
+static int blkg_lookup_create(struct blkcg *blkcg, struct gendisk *disk,
+			      gfp_t gfp_mask, struct blkcg_gq **blkgp)
 {
 	struct request_queue *q = disk->queue;
 	struct blkcg_gq *blkg;
@@ -477,7 +472,8 @@ static struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
 		    blkg != rcu_dereference(blkcg->blkg_hint))
 			rcu_assign_pointer(blkcg->blkg_hint, blkg);
 		rcu_read_unlock();
-		return blkg;
+		*blkgp = blkg;
+		return 0;
 	}
 	rcu_read_unlock();
 
@@ -491,6 +487,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,17 +498,18 @@ 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);
+		blkg = blkg_create(pos, disk, gfp_mask);
 		if (IS_ERR(blkg)) {
-			blkg = ret_blkg;
-			break;
+			*blkgp = ret_blkg;
+			return PTR_ERR(blkg);
+		}
+		if (pos == blkcg) {
+			*blkgp = blkg;
+			return 0;
 		}
-		if (pos == blkcg)
-			break;
 	}
-
-	return blkg;
 }
 
 static void blkg_destroy(struct blkcg_gq *blkg)
@@ -519,7 +517,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 +545,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 +567,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 +600,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 +817,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,81 +836,20 @@ 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;
 		goto fail_unlock;
 	}
 
-	blkg = blkg_lookup(blkcg, q);
-	if (blkg)
-		goto success;
-
-	/*
-	 * Create blkgs walking down from blkcg_root to @blkcg, so that all
-	 * non-root blkgs have access to their parents.
-	 */
-	while (true) {
-		struct blkcg *pos = blkcg;
-		struct blkcg *parent;
-		struct blkcg_gq *new_blkg;
-
-		parent = blkcg_parent(blkcg);
-		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);
-
-		new_blkg = blkg_alloc(pos, disk, GFP_NOIO);
-		if (unlikely(!new_blkg)) {
-			ret = -ENOMEM;
-			goto fail_exit;
-		}
-
-		if (radix_tree_preload(GFP_KERNEL)) {
-			blkg_free(new_blkg);
-			ret = -ENOMEM;
-			goto fail_exit;
-		}
-
-		spin_lock_irq(&q->queue_lock);
-
-		if (!blkcg_policy_enabled(q, pol)) {
-			blkg_free(new_blkg);
-			ret = -EOPNOTSUPP;
-			goto fail_preloaded;
-		}
-
-		blkg = blkg_lookup(pos, q);
-		if (blkg) {
-			blkg_free(new_blkg);
-		} else {
-			blkg = blkg_create(pos, disk, new_blkg);
-			if (IS_ERR(blkg)) {
-				ret = PTR_ERR(blkg);
-				goto fail_preloaded;
-			}
-		}
-
-		radix_tree_preload_end();
+	ret = blkg_lookup_create(blkcg, disk, GFP_NOIO, &blkg);
+	if (ret)
+		goto fail_unlock;
 
-		if (pos == blkcg)
-			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 +872,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 +1192,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 +1209,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();
@@ -1450,8 +1385,7 @@ void blkg_init_queue(struct request_queue *q)
 int blkcg_init_disk(struct gendisk *disk)
 {
 	struct request_queue *q = disk->queue;
-	struct blkcg_gq *new_blkg, *blkg;
-	bool preloaded;
+	struct blkcg_gq *blkg;
 
 	/*
 	 * If the queue is shared across disk rebind (e.g., SCSI), the
@@ -1465,30 +1399,19 @@ int blkcg_init_disk(struct gendisk *disk)
 	 */
 	wait_var_event(&q->root_blkg, !READ_ONCE(q->root_blkg));
 
-	new_blkg = blkg_alloc(&blkcg_root, disk, GFP_KERNEL);
-	if (!new_blkg)
-		return -ENOMEM;
-
-	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);
-	blkg = blkg_create(&blkcg_root, disk, new_blkg);
+	mutex_lock(&q->blkcg_mutex);
+	blkg = blkg_create(&blkcg_root, disk, GFP_KERNEL);
 	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);
-	if (preloaded)
-		radix_tree_preload_end();
+	mutex_unlock(&q->blkcg_mutex);
 	return PTR_ERR(blkg);
 }
 
@@ -1526,6 +1449,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 +1494,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.
@@ -1545,8 +1504,7 @@ EXPORT_SYMBOL_GPL(io_cgrp_subsys);
 int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
 {
 	struct request_queue *q = disk->queue;
-	struct blkg_policy_data *pd_prealloc = NULL;
-	struct blkcg_gq *blkg, *pinned_blkg = NULL;
+	struct blkcg_gq *blkg;
 	unsigned int memflags;
 	int ret;
 
@@ -1563,8 +1521,8 @@ 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,39 +1530,14 @@ 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 (blkg == pinned_blkg) {
-			pd = pd_prealloc;
-			pd_prealloc = NULL;
-		} else {
-			pd = pol->pd_alloc_fn(disk, blkg->blkcg,
-					      GFP_NOWAIT);
-		}
-
-		if (!pd) {
-			/*
-			 * GFP_NOWAIT failed.  Free the existing one and
-			 * prealloc for @blkg w/ GFP_KERNEL.
-			 */
-			if (pinned_blkg)
-				blkg_put(pinned_blkg);
-			blkg_get(blkg);
-			pinned_blkg = blkg;
-
-			spin_unlock_irq(&q->queue_lock);
-
-			if (pd_prealloc)
-				pol->pd_free_fn(pd_prealloc);
-			pd_prealloc = pol->pd_alloc_fn(disk, blkg->blkcg,
-						       GFP_KERNEL);
-			if (pd_prealloc)
-				goto retry;
-			else
-				goto enomem;
-		}
+		pd = pol->pd_alloc_fn(disk, blkg->blkcg, GFP_NOIO);
+		if (!pd)
+			goto enomem;
 
-		spin_lock(&blkg->blkcg->lock);
+		spin_lock_irq(&blkg->blkcg->lock);
 
 		pd->blkg = blkg;
 		pd->plid = pol->plid;
@@ -1617,41 +1550,22 @@ 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)
-		blkg_put(pinned_blkg);
-	if (pd_prealloc)
-		pol->pd_free_fn(pd_prealloc);
 	return ret;
 
 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);
+	list_for_each_entry(blkg, &q->blkg_list, q_node)
+		blkg_free_policy_data(blkg, pol);
 	ret = -ENOMEM;
 	goto out;
 }
@@ -1679,24 +1593,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))
@@ -2083,15 +1985,39 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio,
 	if (blkg)
 		return blkg;
 
+	if (bio->bi_opf & REQ_NOWAIT) {
+		int ret;
+
+		/*
+		 * Nowait callers must not sleep on the mutex nor allocate with
+		 * sleeping GFPs.  Trylock the mutex and create the missing blkg
+		 * atomically; if the mutex is contended, the caller is atomic,
+		 * or blkg allocation fails, return NULL so the caller can fail
+		 * the bio and let the submitter retry once the blkg exists.
+		 */
+		if (!preemptible() || !mutex_trylock(&q->blkcg_mutex))
+			return NULL;
+
+		ret = blkg_lookup_create(blkcg, bio->bi_bdev->bd_disk,
+					 GFP_ATOMIC, &blkg);
+		if (ret)
+			blkg = NULL;
+		else if (blkg)
+			blkg = blkg_lookup_tryget(blkg);
+		mutex_unlock(&q->blkcg_mutex);
+
+		return blkg;
+	}
+
 	/*
 	 * 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);
-	blkg = blkg_lookup_create(blkcg, bio->bi_bdev->bd_disk);
+	mutex_lock(&q->blkcg_mutex);
+	blkg_lookup_create(blkcg, bio->bi_bdev->bd_disk, GFP_NOIO, &blkg);
 	if (blkg)
 		blkg = blkg_lookup_tryget(blkg);
-	spin_unlock_irq(&q->queue_lock);
+	mutex_unlock(&q->blkcg_mutex);
 
 	return blkg;
 }
@@ -2109,19 +2035,33 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio,
  *
  * A reference will be taken on the blkg and will be released when @bio is
  * freed.
+ *
+ * If @bio is REQ_NOWAIT and associating requires creating a new blkg, this
+ * function does not sleep; when the blkg cannot be created atomically it
+ * returns %false with @bio left unassociated so the caller can fail the I/O.
+ *
+ * Return: %true if @bio is associated with a blkg, %false on nowait failure.
  */
-void bio_associate_blkg_from_css(struct bio *bio,
+bool bio_associate_blkg_from_css(struct bio *bio,
 				 struct cgroup_subsys_state *css)
 {
-	if (bio->bi_blkg)
+	struct blkcg_gq *blkg;
+
+	if (bio->bi_blkg) {
 		blkg_put(bio->bi_blkg);
+		bio->bi_blkg = NULL;
+	}
 
 	if (css && css->parent) {
-		bio->bi_blkg = blkg_tryget_closest(bio, css);
+		blkg = blkg_tryget_closest(bio, css);
+		if (!blkg)
+			return false;
+		bio->bi_blkg = blkg;
 	} else {
 		blkg_get(bdev_get_queue(bio->bi_bdev)->root_blkg);
 		bio->bi_blkg = bdev_get_queue(bio->bi_bdev)->root_blkg;
 	}
+	return true;
 }
 EXPORT_SYMBOL_GPL(bio_associate_blkg_from_css);
 
@@ -2129,32 +2069,38 @@ EXPORT_SYMBOL_GPL(bio_associate_blkg_from_css);
  * bio_associate_blkg - associate a bio with a blkg
  * @bio: target bio
  *
- * Associate @bio with the blkg found from the bio's css and request_queue.
- * If one is not found, bio_lookup_blkg() creates the blkg.  If a blkg is
- * already associated, the css is reused and association redone as the
- * request_queue may have changed.
+ * Associate @bio with the blkg found from the bio's css and request_queue,
+ * creating it if necessary.  If a blkg is already associated, the css is
+ * reused and association redone as the request_queue may have changed.
+ *
+ * If @bio is REQ_NOWAIT, association does not sleep; if the blkg cannot be
+ * created without sleeping, %false is returned with @bio left unassociated.
+ *
+ * Return: %true on success, %false on nowait failure.
  */
-void bio_associate_blkg(struct bio *bio)
+bool bio_associate_blkg(struct bio *bio)
 {
 	struct cgroup_subsys_state *css;
+	bool ret;
 
 	if (blk_op_is_passthrough(bio->bi_opf))
-		return;
+		return true;
 
 	if (bio->bi_blkg) {
 		css = bio_blkcg_css(bio);
-		bio_associate_blkg_from_css(bio, css);
-	} else {
-		rcu_read_lock();
-		css = blkcg_css();
-		if (!css_tryget_online(css))
-			css = NULL;
-		rcu_read_unlock();
-
-		bio_associate_blkg_from_css(bio, css);
-		if (css)
-			css_put(css);
+		return bio_associate_blkg_from_css(bio, css);
 	}
+
+	rcu_read_lock();
+	css = blkcg_css();
+	if (!css_tryget_online(css))
+		css = NULL;
+	rcu_read_unlock();
+
+	ret = bio_associate_blkg_from_css(bio, css);
+	if (css)
+		css_put(css);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(bio_associate_blkg);
 
@@ -2170,6 +2116,24 @@ void bio_clone_blkg_association(struct bio *dst, struct bio *src)
 }
 EXPORT_SYMBOL_GPL(bio_clone_blkg_association);
 
+/**
+ * bio_disassociate_blkg - disassociate a bio from its blkg
+ * @bio: target bio
+ *
+ * Drop the blkg reference held by @bio and clear the association.  This is
+ * used when a bio's target device changes (e.g. via bio_set_dev()): the old
+ * blkg is tied to the previous request_queue, so it is dropped here and the
+ * bio is reassociated to the new queue from the submit path.
+ */
+void bio_disassociate_blkg(struct bio *bio)
+{
+	if (bio->bi_blkg) {
+		blkg_put(bio->bi_blkg);
+		bio->bi_blkg = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(bio_disassociate_blkg);
+
 static int blk_cgroup_io_type(struct bio *bio)
 {
 	if (op_is_discard(bio->bi_opf))
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h
index 615390f751aa25..5aaf2d54d17ede 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 365641266c9e82..9b96dd802c92d9 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -822,6 +822,28 @@ void submit_bio_noacct(struct bio *bio)
 
 	might_sleep();
 
+	/*
+	 * Associate the bio with a blkg for its target queue here, where it is
+	 * safe to sleep, instead of in bio_set_dev()/bio_init() which may run
+	 * under a spinlock.  bio_set_dev() no longer associates, so a freshly
+	 * allocated or remapped bio has no blkg until it reaches the submit path.
+	 * Reassociate only when the bio was remapped to a different queue since
+	 * it was last associated; blk_throtl_bio() and the rq_qos throttlers
+	 * rely on bio->bi_blkg matching the queue of bio->bi_bdev.
+	 */
+#ifdef CONFIG_BLK_CGROUP
+	if (!bio->bi_blkg || bio->bi_blkg->q != q) {
+		/*
+		 * For REQ_NOWAIT, do not sleep to create a new blkg: fail the
+		 * bio so the submitter retries once the blkg exists.
+		 */
+		if (!bio_associate_blkg(bio)) {
+			bio_endio_status(bio, BLK_STS_AGAIN);
+			return;
+		}
+	}
+#endif
+
 	/*
 	 * For a REQ_NOWAIT based request, return -EOPNOTSUPP
 	 * if queue does not support NOWAIT.
@@ -951,6 +973,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));
@@ -958,6 +982,18 @@ void submit_bio(struct bio *bio)
 		count_vm_events(PGPGOUT, bio_sectors(bio));
 	}
 
+	/*
+	 * bio_set_ioprio() -> blkcg_set_ioprio() reads the policy from
+	 * bio->bi_blkg, so associate the blkg (for new I/O, the first time) before
+	 * it runs.  This is the sleepable entry point for new I/O; remapped bios
+	 * that reach submit_bio_noacct() directly are reassociated there.
+	 * For REQ_NOWAIT I/O, fail the bio instead of sleeping if a new blkg
+	 * cannot be allocated.
+	 */
+	if (!bio_associate_blkg(bio)) {
+		bio_endio_status(bio, BLK_STS_AGAIN);
+		return;
+	}
 	bio_set_ioprio(bio);
 	submit_bio_noacct(bio);
 }
diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index 8b2aeba2e1e39c..ae50d143e4fcca 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 cef02b6c5fa91d..30e23fee4f1574 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 ffc3b70065d4b1..ef3edd5a4785be 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -30,6 +30,9 @@ static struct workqueue_struct *kthrotld_workqueue;
 
 struct throtl_data
 {
+	/* protects throttle service queues and group runtime state */
+	spinlock_t lock;
+
 	/* service tree for active throtl groups */
 	struct throtl_service_queue service_queue;
 
@@ -346,11 +349,16 @@ static void tg_update_has_rules(struct throtl_grp *tg)
 static void throtl_pd_online(struct blkg_policy_data *pd)
 {
 	struct throtl_grp *tg = pd_to_tg(pd);
+	struct throtl_data *td = tg->td;
+	unsigned long flags;
+
+	spin_lock_irqsave(&td->lock, flags);
 	/*
 	 * We don't want new groups to escape the limits of its ancestors.
 	 * Update has_rules[] after a new group is brought online.
 	 */
 	tg_update_has_rules(tg);
+	spin_unlock_irqrestore(&td->lock, flags);
 }
 
 static void tg_release(struct rcu_head *rcu)
@@ -368,7 +376,7 @@ static void throtl_pd_free(struct blkg_policy_data *pd)
 {
 	struct throtl_grp *tg = pd_to_tg(pd);
 
-	timer_delete_sync(&tg->service_queue.pending_timer);
+	timer_shutdown_sync(&tg->service_queue.pending_timer);
 	call_rcu(&pd->rcu_head, tg_release);
 }
 
@@ -1142,9 +1150,9 @@ static void throtl_pending_timer_fn(struct timer_list *t)
 	else
 		q = td->queue;
 
-	spin_lock_irq(&q->queue_lock);
+	spin_lock_irq(&td->lock);
 
-	if (!q->root_blkg)
+	if (!READ_ONCE(q->root_blkg))
 		goto out_unlock;
 
 again:
@@ -1168,9 +1176,9 @@ static void throtl_pending_timer_fn(struct timer_list *t)
 			break;
 
 		/* this dispatch windows is still open, relax and repeat */
-		spin_unlock_irq(&q->queue_lock);
+		spin_unlock_irq(&td->lock);
 		cpu_relax();
-		spin_lock_irq(&q->queue_lock);
+		spin_lock_irq(&td->lock);
 	}
 
 	if (!dispatched)
@@ -1193,7 +1201,7 @@ static void throtl_pending_timer_fn(struct timer_list *t)
 		queue_work(kthrotld_workqueue, &td->dispatch_work);
 	}
 out_unlock:
-	spin_unlock_irq(&q->queue_lock);
+	spin_unlock_irq(&td->lock);
 }
 
 /**
@@ -1209,7 +1217,6 @@ static void blk_throtl_dispatch_work_fn(struct work_struct *work)
 	struct throtl_data *td = container_of(work, struct throtl_data,
 					      dispatch_work);
 	struct throtl_service_queue *td_sq = &td->service_queue;
-	struct request_queue *q = td->queue;
 	struct bio_list bio_list_on_stack;
 	struct bio *bio;
 	struct blk_plug plug;
@@ -1217,11 +1224,11 @@ static void blk_throtl_dispatch_work_fn(struct work_struct *work)
 
 	bio_list_init(&bio_list_on_stack);
 
-	spin_lock_irq(&q->queue_lock);
+	spin_lock_irq(&td->lock);
 	for (rw = READ; rw <= WRITE; rw++)
 		while ((bio = throtl_pop_queued(td_sq, NULL, rw)))
 			bio_list_add(&bio_list_on_stack, bio);
-	spin_unlock_irq(&q->queue_lock);
+	spin_unlock_irq(&td->lock);
 
 	if (!bio_list_empty(&bio_list_on_stack)) {
 		blk_start_plug(&plug);
@@ -1299,7 +1306,7 @@ static void tg_conf_updated(struct throtl_grp *tg, bool global)
 	rcu_read_unlock();
 
 	/*
-	 * We're already holding queue_lock and know @tg is valid.  Let's
+	 * We're already holding td->lock and know @tg is valid.  Let's
 	 * apply the new config directly.
 	 *
 	 * Restart the slices for both READ and WRITES. It might happen
@@ -1327,6 +1334,7 @@ static int blk_throtl_init(struct gendisk *disk)
 		return -ENOMEM;
 
 	INIT_WORK(&td->dispatch_work, blk_throtl_dispatch_work_fn);
+	spin_lock_init(&td->lock);
 	throtl_service_queue_init(&td->service_queue);
 
 	memflags = blk_mq_freeze_queue(disk->queue);
@@ -1381,6 +1389,7 @@ static ssize_t tg_set_conf(struct kernfs_open_file *of,
 		v = U64_MAX;
 
 	tg = blkg_to_tg(ctx.blkg);
+	spin_lock_irq(&tg->td->lock);
 	tg_update_carryover(tg);
 
 	if (is_u64)
@@ -1389,6 +1398,7 @@ static ssize_t tg_set_conf(struct kernfs_open_file *of,
 		*(unsigned int *)((void *)tg + of_cft(of)->private) = v;
 
 	tg_conf_updated(tg, false);
+	spin_unlock_irq(&tg->td->lock);
 	ret = 0;
 
 unprep:
@@ -1563,6 +1573,7 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,
 		goto close_bdev;
 
 	tg = blkg_to_tg(ctx.blkg);
+	spin_lock_irq(&tg->td->lock);
 	tg_update_carryover(tg);
 
 	v[0] = tg->bps[READ];
@@ -1586,11 +1597,11 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,
 		p = tok;
 		strsep(&p, "=");
 		if (!p || (sscanf(p, "%llu", &val) != 1 && strcmp(p, "max")))
-			goto unprep;
+			goto unlock;
 
 		ret = -ERANGE;
 		if (!val)
-			goto unprep;
+			goto unlock;
 
 		ret = -EINVAL;
 		if (!strcmp(tok, "rbps"))
@@ -1602,7 +1613,7 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,
 		else if (!strcmp(tok, "wiops"))
 			v[3] = min_t(u64, val, UINT_MAX);
 		else
-			goto unprep;
+			goto unlock;
 	}
 
 	tg->bps[READ] = v[0];
@@ -1611,7 +1622,11 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,
 	tg->iops[WRITE] = v[3];
 
 	tg_conf_updated(tg, false);
+	spin_unlock_irq(&tg->td->lock);
 	ret = 0;
+	goto unprep;
+unlock:
+	spin_unlock_irq(&tg->td->lock);
 unprep:
 	blkg_conf_unprep(&ctx);
 close_bdev:
@@ -1636,6 +1651,28 @@ static void throtl_shutdown_wq(struct request_queue *q)
 	cancel_work_sync(&td->dispatch_work);
 }
 
+static void throtl_shutdown_timers(struct request_queue *q)
+{
+	struct throtl_data *td = q->td;
+	struct blkcg_gq *blkg;
+
+	/*
+	 * blkg_destroy_all() has already offlined the policy, but blkg policy
+	 * data is freed asynchronously.  Shut down per-group timers before
+	 * freeing td, as their callbacks still dereference tg->td.
+	 */
+	mutex_lock(&q->blkcg_mutex);
+	list_for_each_entry(blkg, &q->blkg_list, q_node) {
+		struct throtl_grp *tg = blkg_to_tg(blkg);
+
+		if (tg)
+			timer_shutdown_sync(&tg->service_queue.pending_timer);
+	}
+	mutex_unlock(&q->blkcg_mutex);
+
+	timer_shutdown_sync(&td->service_queue.pending_timer);
+}
+
 static void tg_flush_bios(struct throtl_grp *tg)
 {
 	struct throtl_service_queue *sq = &tg->service_queue;
@@ -1669,7 +1706,13 @@ static void tg_flush_bios(struct throtl_grp *tg)
 
 static void throtl_pd_offline(struct blkg_policy_data *pd)
 {
-	tg_flush_bios(pd_to_tg(pd));
+	struct throtl_grp *tg = pd_to_tg(pd);
+	struct throtl_data *td = tg->td;
+	unsigned long flags;
+
+	spin_lock_irqsave(&td->lock, flags);
+	tg_flush_bios(tg);
+	spin_unlock_irqrestore(&td->lock, flags);
 }
 
 struct blkcg_policy blkcg_policy_throtl = {
@@ -1725,6 +1768,7 @@ static void tg_cancel_writeback_bios(struct throtl_grp *tg,
 void blk_throtl_cancel_bios(struct gendisk *disk)
 {
 	struct request_queue *q = disk->queue;
+	struct throtl_data *td = q->td;
 	struct cgroup_subsys_state *pos_css;
 	struct blkcg_gq *blkg;
 	struct bio_list cancel_bios[2] = { };
@@ -1733,9 +1777,10 @@ void blk_throtl_cancel_bios(struct gendisk *disk)
 	if (!blk_throtl_activated(q))
 		return;
 
-	spin_lock_irq(&q->queue_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.
 	 */
@@ -1752,7 +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_irq(&q->queue_lock);
+	spin_unlock_irq(&td->lock);
+	mutex_unlock(&q->blkcg_mutex);
 
 	for (rw = READ; rw <= WRITE; rw++) {
 		struct bio *bio;
@@ -1791,7 +1837,6 @@ static bool tg_within_limit(struct throtl_grp *tg, struct bio *bio, bool rw)
 
 bool __blk_throtl_bio(struct bio *bio)
 {
-	struct request_queue *q = bdev_get_queue(bio->bi_bdev);
 	struct blkcg_gq *blkg = bio->bi_blkg;
 	struct throtl_qnode *qn = NULL;
 	struct throtl_grp *tg = blkg_to_tg(blkg);
@@ -1801,7 +1846,7 @@ bool __blk_throtl_bio(struct bio *bio)
 	struct throtl_data *td = tg->td;
 
 	rcu_read_lock();
-	spin_lock_irq(&q->queue_lock);
+	spin_lock_irq(&td->lock);
 	sq = &tg->service_queue;
 
 	while (true) {
@@ -1877,7 +1922,7 @@ bool __blk_throtl_bio(struct bio *bio)
 	}
 
 out_unlock:
-	spin_unlock_irq(&q->queue_lock);
+	spin_unlock_irq(&td->lock);
 
 	rcu_read_unlock();
 	return throttled;
@@ -1886,17 +1931,19 @@ bool __blk_throtl_bio(struct bio *bio)
 void blk_throtl_exit(struct gendisk *disk)
 {
 	struct request_queue *q = disk->queue;
+	struct throtl_data *td = q->td;
 
 	/*
 	 * blkg_destroy_all() already deactivate throtl policy, just check and
 	 * free throtl data.
 	 */
-	if (!q->td)
+	if (!td)
 		return;
 
-	timer_delete_sync(&q->td->service_queue.pending_timer);
+	throtl_shutdown_timers(q);
 	throtl_shutdown_wq(q);
-	kfree(q->td);
+	q->td = NULL;
+	kfree(td);
 }
 
 static int __init throtl_init(void)
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 8f33f717b14f5b..570b62e676dc1a 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -503,18 +503,19 @@ static inline void bio_release_pages(struct bio *bio, bool mark_dirty)
 	disk_devt((bio)->bi_bdev->bd_disk)
 
 #ifdef CONFIG_BLK_CGROUP
-void bio_associate_blkg(struct bio *bio);
-void bio_associate_blkg_from_css(struct bio *bio,
+bool bio_associate_blkg(struct bio *bio);
+bool bio_associate_blkg_from_css(struct bio *bio,
 				 struct cgroup_subsys_state *css);
 void bio_clone_blkg_association(struct bio *dst, struct bio *src);
+void bio_disassociate_blkg(struct bio *bio);
 void blkcg_punt_bio_submit(struct bio *bio);
 #else	/* CONFIG_BLK_CGROUP */
-static inline void bio_associate_blkg(struct bio *bio) { }
-static inline void bio_associate_blkg_from_css(struct bio *bio,
-					       struct cgroup_subsys_state *css)
-{ }
+static inline bool bio_associate_blkg(struct bio *bio) { return true; }
+static inline bool bio_associate_blkg_from_css(struct bio *bio,
+					       struct cgroup_subsys_state *css) { return true; }
 static inline void bio_clone_blkg_association(struct bio *dst,
 					      struct bio *src) { }
+static inline void bio_disassociate_blkg(struct bio *bio) { }
 static inline void blkcg_punt_bio_submit(struct bio *bio)
 {
 	submit_bio(bio);
@@ -524,10 +525,11 @@ static inline void blkcg_punt_bio_submit(struct bio *bio)
 static inline void bio_set_dev(struct bio *bio, struct block_device *bdev)
 {
 	bio_clear_flag(bio, BIO_REMAPPED);
-	if (bio->bi_bdev != bdev)
+	if (bio->bi_bdev != bdev) {
 		bio_clear_flag(bio, BIO_BPS_THROTTLED);
+		bio_disassociate_blkg(bio);
+	}
 	bio->bi_bdev = bdev;
-	bio_associate_blkg(bio);
 }
 
 /*

3/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20223
							output: 21
							thoughts: 514
						

Thoughts:
**Analyzing a Block Layer Patch Series for Fuzzing Opportunities**

Okay, so I've got this patch series in front of me, and it's touching a whole bunch of files in the block layer: `blk-cgroup.c`, `bio.c`, `blk-core.c`, `blk-throttle.c`, `blk-iocost.c`, `blk-iolatency.c`, and `bfq-cgroup.c`. Immediately, I see it's a deep dive into how blkcg (cgroup integration) is handled, which means it's worth a close look for potential vulnerabilities.

The core of the change appears to be a shift in locking from `queue_lock` to `blkcg_mutex` and `blkcg->lock` within the `blk-cgroup` implementation. This is always a key area to scrutinize during any review. They're also altering `bio_associate_blkg` to return a boolean, which seems to be related to handling `REQ_NOWAIT` failures, plus adding `bio_disassociate_blkg` and modifying `bio_set_dev` to call it. Further, it looks like they're making adjustments to `submit_bio_noacct` and `submit_bio` to handle these potential `bio_associate_blkg` failures, and that they're now returning `BLK_STS_AGAIN` in some of these failure cases. The fact that the patch is modifying those core paths like `submit_bio`, `submit_bio_noacct`, and `bio_set_dev` really hammers home that this is a significant functional change to the block layer cgroup integration.

Given the scope and core nature of these changes, my gut tells me this is a prime candidate for fuzzing. I'm going to start by identifying the functions that are modified or added as high-priority targets. These are going to be my focus: `bio_associate_blkg`, `bio_associate_blkg_from_css`, `bio_disassociate_blkg`, `blkg_lookup_create`, `blkcg_activate_policy`, `blkcg_deactivate_policy`, `blkg_conf_prep`, `blkg_conf_unprep`, `blk_throtl_bio`, `tg_set_limit`, `tg_set_conf`, and `ioc_weight_write`.

Now, I'll need to figure out the required configurations to reproduce this behavior and get the target built. I'm seeing that `CONFIG_BLK_CGROUP` is a given. And because we've got references to specific files, `CONFIG_BLK_CGROUP_IOLATENCY`, `CONFIG_BLK_CGROUP_IOCOST`, `CONFIG_BLK_DEV_THROTTLING`, and `CONFIG_BFQ_GROUP_IOSCHED` are almost certainly also going to be needed for this fuzzer to even execute. I'll need to double-check those in `Kconfig` to be absolutely certain.



4/2 2026/07/25 18:25 tool grepper
0m Args:
{
  "Expression": "config BLK_CGROUP"
}

Results:
{
  "Output": "block/Kconfig=39=config BLK_RQ_ALLOC_TIME\n--\nblock/Kconfig-41-\nblock/Kconfig:42:config BLK_CGROUP_RWSTAT\nblock/Kconfig-43-\tbool\nblock/Kconfig-44-\nblock/Kconfig:45:config BLK_CGROUP_PUNT_BIO\nblock/Kconfig-46-\tbool\n--\nblock/Kconfig=127=config BLK_WBT_MQ\n--\nblock/Kconfig-133-\nblock/Kconfig:134:config BLK_CGROUP_IOLATENCY\nblock/Kconfig-135-\tbool \"Enable support for latency based cgroup IO protection\"\n--\nblock/Kconfig-144-\nblock/Kconfig:145:config BLK_CGROUP_FC_APPID\nblock/Kconfig-146-\tbool \"Enable support to track FC I/O Traffic across cgroup applications\"\n--\nblock/Kconfig-153-\nblock/Kconfig:154:config BLK_CGROUP_IOCOST\nblock/Kconfig-155-\tbool \"Enable support for cost model based cgroup IO controller\"\n--\nblock/Kconfig-163-\nblock/Kconfig:164:config BLK_CGROUP_IOPRIO\nblock/Kconfig-165-\tbool \"Cgroup I/O controller for assigning an I/O priority class\"\n--\ninit/Kconfig=1096=config MEMCG_V1\n--\ninit/Kconfig-1112-\ninit/Kconfig:1113:config BLK_CGROUP\ninit/Kconfig-1114-\tbool \"IO controller\"\n"
}

5/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21847
							output: 24
							thoughts: 51
						

Thoughts:
Okay, here's my take on those terms, thinking as an expert:

**Understanding Block I/O Control in the Linux Kernel**

Right, so I see we're talking about various aspects of block I/O control and scheduling within the Linux kernel. First off, `BLK_CGROUP` immediately flags the connection to control groups (cgroups), which is the bedrock of resource isolation and management. This means we're dealing with a way to partition and limit block I/O bandwidth, latency, and generally manage how different processes or containers access storage.

Then, we have more specific flavors of block I/O control. `BLK_CGROUP_IOLATENCY` likely refers to a feature that lets us prioritize or limit the latency experienced by I/O operations for specific cgroups. This is crucial for applications that are sensitive to I/O delays, such as databases or interactive systems, and that ensures those types of applications get the resources to function as expected.

`BLK_CGROUP_IOCOST` immediately brings to mind cost-based I/O scheduling, possibly using estimations to regulate and fairly schedule resource allocation. I'm already thinking about the trade-offs of using estimations, how accurate they are, and the potential impact on overall system throughput.

`BLK_DEV_THROTTLING` is the general term for throttling, and is probably a lower-level function. This would be a crucial element, the mechanism used to restrict the bandwidth or IOPS (Input/Output Operations Per Second) for specific devices or partitions, either to prevent a single application from hogging all the resources or to ensure fair sharing. I'm imagining the different methods implemented to achieve this: token buckets, leaky buckets, or something more advanced.

Finally, `BFQ_GROUP_IOSCHED` suggests we're dealing with the Block Fair Queue (BFQ) I/O scheduler and that it's being used in a group context. This is interesting because BFQ has traditionally been good at providing fair access to storage. Applying it to cgroups means we can finely tune I/O fairness across different sets of applications or containers, which is absolutely critical in a modern, containerized environment. I need to think about how this interacts with other I/O schedulers like CFQ (Completely Fair Queuing) and how the configuration options affect responsiveness. It all ties back to how to maintain quality of service (QoS) in a shared storage environment.

Overall, this represents a sophisticated set of tools.



6/2 2026/07/25 18:25 tool grepper
0m Args:
{
  "Expression": "config BLK_DEV_THROTTLING"
}

Results:
{
  "Output": "block/Kconfig=97=config BLK_DEV_ZONED\n--\nblock/Kconfig-105-\nblock/Kconfig:106:config BLK_DEV_THROTTLING\nblock/Kconfig-107-\tbool \"Block layer bio throttling support\"\n"
}

7/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22529
							output: 23
							thoughts: 14
						

Thoughts:
Here's a summary of my thought process:

**Diving into I/O Scheduler Parameters**

Okay, time to dig into the details. My attention's immediately drawn to `BFQ_GROUP_IOSCHED`. It's a key parameter, and understanding its implications is critical for optimal system performance, especially in scenarios where I/O bottlenecks are a concern. I need to refresh my understanding of how it impacts the I/O scheduler's behavior, particularly in the context of workload isolation and fairness. I'm thinking about the trade-offs it presents: resource allocation, potential priority inversions, and how it meshes with other scheduler settings. I need to recall the relevant kernel documentation and any recent performance benchmarks related to this setting. Let's see what I can find.



8/2 2026/07/25 18:25 tool grepper
0m Args:
{
  "Expression": "config BFQ_GROUP_IOSCHED"
}

Results:
{
  "Output": "block/Kconfig.iosched=19=config IOSCHED_BFQ\n--\nblock/Kconfig.iosched-29-\nblock/Kconfig.iosched:30:config BFQ_GROUP_IOSCHED\nblock/Kconfig.iosched-31-       bool \"BFQ hierarchical scheduling support\"\n"
}

9/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22817
							output: 22
							thoughts: 98
						

Thoughts:
**Enabling Block IO Control and Scheduling Features**

Okay, so I need to get these block IO control and scheduling features enabled. Specifically, I'm looking at enabling `BLK_CGROUP`, `BLK_CGROUP_IOLATENCY`, `BLK_CGROUP_IOCOST`, `BLK_DEV_THROTTLING`, `IOSCHED_BFQ`, and `BFQ_GROUP_IOSCHED`. These are the core building blocks for fine-grained IO management, and I need to make sure they are active.

Before I go any further, I should check the configuration to see if there are any other related options I might have missed. I already know that `CONFIG_CGROUPS` is the broader parent configuration for `BLK_CGROUP`, so I'll want to take that into consideration as well. Gotta make sure the dependencies are sorted before I start.



10/2 2026/07/25 18:25 tool grepper
0m Args:
{
  "Expression": "depends on.*BLK_CGROUP"
}

Results:
{
  "Output": "block/Kconfig=106=config BLK_DEV_THROTTLING\nblock/Kconfig-107-\tbool \"Block layer bio throttling support\"\nblock/Kconfig:108:\tdepends on BLK_CGROUP\nblock/Kconfig-109-\tselect BLK_CGROUP_RWSTAT\n--\nblock/Kconfig=134=config BLK_CGROUP_IOLATENCY\nblock/Kconfig-135-\tbool \"Enable support for latency based cgroup IO protection\"\nblock/Kconfig:136:\tdepends on BLK_CGROUP\nblock/Kconfig-137-\thelp\n--\nblock/Kconfig=145=config BLK_CGROUP_FC_APPID\nblock/Kconfig-146-\tbool \"Enable support to track FC I/O Traffic across cgroup applications\"\nblock/Kconfig:147:\tdepends on BLK_CGROUP \u0026\u0026 NVME_FC\nblock/Kconfig-148-\thelp\n--\nblock/Kconfig=154=config BLK_CGROUP_IOCOST\nblock/Kconfig-155-\tbool \"Enable support for cost model based cgroup IO controller\"\nblock/Kconfig:156:\tdepends on BLK_CGROUP\nblock/Kconfig-157-\tselect BLK_RQ_ALLOC_TIME\n--\nblock/Kconfig=164=config BLK_CGROUP_IOPRIO\nblock/Kconfig-165-\tbool \"Cgroup I/O controller for assigning an I/O priority class\"\nblock/Kconfig:166:\tdepends on BLK_CGROUP\nblock/Kconfig-167-\thelp\n--\nblock/Kconfig.iosched=30=config BFQ_GROUP_IOSCHED\nblock/Kconfig.iosched-31-       bool \"BFQ hierarchical scheduling support\"\nblock/Kconfig.iosched:32:       depends on IOSCHED_BFQ \u0026\u0026 BLK_CGROUP\nblock/Kconfig.iosched-33-       default y\n--\nfs/f2fs/Kconfig=147=config F2FS_UNFAIR_RWSEM\nfs/f2fs/Kconfig-148-\tbool \"F2FS unfair rw_semaphore\"\nfs/f2fs/Kconfig:149:\tdepends on F2FS_FS \u0026\u0026 BLK_CGROUP\nfs/f2fs/Kconfig-150-\thelp\n--\ninit/Kconfig=1135=config CGROUP_WRITEBACK\ninit/Kconfig-1136-\tbool\ninit/Kconfig:1137:\tdepends on MEMCG \u0026\u0026 BLK_CGROUP\ninit/Kconfig-1138-\tdefault y\n"
}

11/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 23805
							output: 20
							thoughts: 18
						

Thoughts:
Here's my understanding, summarized in the first person:

**Cgroup Hierarchy: The Foundation**

Alright, let's nail this down. My understanding of the cgroup landscape begins with this crucial relationship: `CGROUPS` is the *parent* of `BLK_CGROUP`. In other words, `BLK_CGROUP` is a specific cgroup implementation, nested within the broader concept and framework provided by `CGROUPS`. That's the foundational dependency I need to remember. This also implies that any properties or configurations I manage at the `CGROUPS` level could have implications that cascade down to `BLK_CGROUP`, and that is a key thing to be aware of.



12/2 2026/07/25 18:25 tool grepper
0m Args:
{
  "Expression": "depends on.*CGROUPS"
}

Results:
{
  "Output": "init/Kconfig=1204=config EXT_SUB_SCHED\ninit/Kconfig-1205-        def_bool y\ninit/Kconfig:1206:        depends on SCHED_CLASS_EXT \u0026\u0026 CGROUPS\ninit/Kconfig-1207-\n--\nkernel/sched/ext.c=762=struct scx_task_iter {\n--\nkernel/sched/ext.c-792- * scx_tasks can be removed in favor of always using cgroup iteration if\nkernel/sched/ext.c:793: * CONFIG_SCHED_CLASS_EXT depends on CONFIG_CGROUPS.\nkernel/sched/ext.c-794- *\n--\nlib/Kconfig.debug=1916=config DEBUG_CGROUP_REF\n--\nlib/Kconfig.debug-1918-\tdepends on DEBUG_KERNEL\nlib/Kconfig.debug:1919:\tdepends on CGROUPS\nlib/Kconfig.debug-1920-\tdepends on KPROBES\n--\nnet/Kconfig=335=config CGROUP_NET_PRIO\nnet/Kconfig-336-\tbool \"Network priority cgroup\"\nnet/Kconfig:337:\tdepends on CGROUPS\nnet/Kconfig-338-\tselect SOCK_CGROUP_DATA\n--\nnet/Kconfig=343=config CGROUP_NET_CLASSID\nnet/Kconfig-344-\tbool \"Network classid cgroup\"\nnet/Kconfig:345:\tdepends on CGROUPS\nnet/Kconfig-346-\tselect SOCK_CGROUP_DATA\n--\nnet/netfilter/Kconfig=1167=config NETFILTER_XT_MATCH_CGROUP\n--\nnet/netfilter/Kconfig-1169-\tdepends on NETFILTER_ADVANCED\nnet/netfilter/Kconfig:1170:\tdepends on CGROUPS\nnet/netfilter/Kconfig-1171-\tselect SOCK_CGROUP_DATA\n--\nnet/sched/Kconfig=550=config NET_CLS_CGROUP\n--\nnet/sched/Kconfig-553-\tselect CGROUP_NET_CLASSID\nnet/sched/Kconfig:554:\tdepends on CGROUPS\nnet/sched/Kconfig-555-\thelp\n--\nsamples/Kconfig=301=config SAMPLE_CGROUP\nsamples/Kconfig-302-\tbool \"Build cgroup sample code\"\nsamples/Kconfig:303:\tdepends on CGROUPS \u0026\u0026 CC_CAN_LINK \u0026\u0026 HEADERS_INSTALL\nsamples/Kconfig-304-\thelp\n"
}

13/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24651
							output: 21
							thoughts: 13
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Diving into Block I/O Cgroup Definitions**

Alright, let's get down to brass tacks. I need a clear understanding of what `BLK_CGROUP` actually entails. No point in wasting time on assumptions or half-baked ideas. I'm going to jump straight to the source – the definition itself. I need a concrete, unambiguous understanding of its purpose, its properties, and how it interacts with the broader block I/O subsystem. This isn't just about knowing *that* it exists; it's about dissecting *what* it is and, critically, *how* it works. I'm after the low-level details that underpin its functionality. So, let's pull up that definition and dig in.



14/2 2026/07/25 18:25 tool grepper
0m Args:
{
  "Expression": "config BLK_CGROUP"
}

Results:
{
  "Output": "block/Kconfig=39=config BLK_RQ_ALLOC_TIME\n--\nblock/Kconfig-41-\nblock/Kconfig:42:config BLK_CGROUP_RWSTAT\nblock/Kconfig-43-\tbool\nblock/Kconfig-44-\nblock/Kconfig:45:config BLK_CGROUP_PUNT_BIO\nblock/Kconfig-46-\tbool\n--\nblock/Kconfig=127=config BLK_WBT_MQ\n--\nblock/Kconfig-133-\nblock/Kconfig:134:config BLK_CGROUP_IOLATENCY\nblock/Kconfig-135-\tbool \"Enable support for latency based cgroup IO protection\"\n--\nblock/Kconfig-144-\nblock/Kconfig:145:config BLK_CGROUP_FC_APPID\nblock/Kconfig-146-\tbool \"Enable support to track FC I/O Traffic across cgroup applications\"\n--\nblock/Kconfig-153-\nblock/Kconfig:154:config BLK_CGROUP_IOCOST\nblock/Kconfig-155-\tbool \"Enable support for cost model based cgroup IO controller\"\n--\nblock/Kconfig-163-\nblock/Kconfig:164:config BLK_CGROUP_IOPRIO\nblock/Kconfig-165-\tbool \"Cgroup I/O controller for assigning an I/O priority class\"\n--\ninit/Kconfig=1096=config MEMCG_V1\n--\ninit/Kconfig-1112-\ninit/Kconfig:1113:config BLK_CGROUP\ninit/Kconfig-1114-\tbool \"IO controller\"\n"
}

15/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25296
							output: 33
							thoughts: 17
						

Thoughts:
Here's my take on the thought process, as if I were the one thinking it:

**Diving into the Kernel Configuration**

Okay, time to get to the heart of the matter. Let's dig into the kernel configuration, specifically the `init/Kconfig` file. I'm thinking, based on what I'm looking at, that line 1113 is a good place to start. I have a hunch it will provide some helpful context or be relevant to the issue I'm looking into. Let's see what gems we can find there.



16/2 2026/07/25 18:25 tool read-file
0m Args:
{
  "File": "init/Kconfig",
  "FirstLine": 1100,
  "LineCount": 20
}

Results:
{
  "Contents": "1100:\t\thelp\n1101:\t\t  Legacy cgroup v1 memory controller which has been deprecated by\n1102:\t\t  cgroup v2 implementation. The v1 is there for legacy applications\n1103:\t\t  which haven't migrated to the new cgroup v2 interface yet. If you\n1104:\t\t  do not have any such application then you are completely fine leaving\n1105:\t\t  this option disabled.\n1106:\t\n1107:\t\t  Please note that feature set of the legacy memory controller is likely\n1108:\t\t  going to shrink due to deprecation process. New deployments with v1\n1109:\t\t  controller are highly discouraged.\n1110:\t\n1111:\t\t  Say N if unsure.\n1112:\t\n1113:\tconfig BLK_CGROUP\n1114:\t\tbool \"IO controller\"\n1115:\t\tdepends on BLOCK\n1116:\t\tdefault n\n1117:\t\thelp\n1118:\t\tGeneric block IO controller cgroup interface. This is the common\n1119:\t\tcgroup interface which should be used by various IO controlling\n"
}

17/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25767
							output: 19
							thoughts: 41
						

Thoughts:
Here's my thought process, distilled:

**Navigating Kernel Dependencies: BLK_CGROUP and BLOCK**

Alright, so `BLK_CGROUP`... that's the one that manages block I/O accounting within cgroups, right? And I recall that it's fundamentally built upon the `BLOCK` subsystem. Gotta be sure those dependencies are in order. Let's make sure that's correct, specifically, I want to confirm that `BLK_CGROUP` *depends* on `BLOCK`.

Now, how do I actually check this? Ah yes, menuconfig! `CGROUPS` is the key config symbol here. I'll need to dig into the kernel's `Kconfig` files – specifically `init/Kconfig` – to see how `CGROUPS` itself is defined and what dependencies it imposes. Gotta verify this dependency chain at the source.



18/2 2026/07/25 18:25 tool grepper
0m Args:
{
  "Expression": "menuconfig CGROUPS"
}

Results:
{
  "Output": "init/Kconfig=1041=config SLAB_OBJ_EXT\n--\ninit/Kconfig-1043-\ninit/Kconfig:1044:menuconfig CGROUPS\ninit/Kconfig-1045-\tbool \"Control Group support\"\n"
}

19/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26095
							output: 32
							thoughts: 108
						

Thoughts:
**My Initial Configuration Assessment**

Okay, so I'm thinking about the necessary building blocks here, and it's immediately clear that `CGROUPS` are going to be a foundational requirement for this particular setup. Given that, let's delineate the relevant configurations we'll need to enable or verify. I'm focusing on the block device aspects. From my immediate assessment, I can see that `CGROUPS` is the first obvious need.

Beyond that, I'm noting the related configurations as well: `BLK_CGROUP`, `BLK_CGROUP_IOLATENCY`, `BLK_CGROUP_IOCOST`, `BLK_DEV_THROTTLING`, `IOSCHED_BFQ`, and `BFQ_GROUP_IOSCHED`. I'm especially interested in `BLK_DEV_THROTTLING`, so let me quickly cross-reference that within `block/Kconfig`. I need to ensure its dependencies and implications are fully understood before proceeding further.



20/2 2026/07/25 18:25 tool read-file
0m Args:
{
  "File": "block/Kconfig",
  "FirstLine": 100,
  "LineCount": 20
}

Results:
{
  "Contents": " 100:\t\tBlock layer zoned block device support. This option enables\n 101:\t\tsupport for ZAC/ZBC/ZNS host-managed and host-aware zoned block\n 102:\t\tdevices.\n 103:\t\n 104:\t\tSay yes here if you have a ZAC, ZBC, or ZNS storage device.\n 105:\t\n 106:\tconfig BLK_DEV_THROTTLING\n 107:\t\tbool \"Block layer bio throttling support\"\n 108:\t\tdepends on BLK_CGROUP\n 109:\t\tselect BLK_CGROUP_RWSTAT\n 110:\t\thelp\n 111:\t\tBlock layer bio throttling support. It can be used to limit\n 112:\t\tthe IO rate to a device. IO rate policies are per cgroup and\n 113:\t\tone needs to mount and use blkio cgroup controller for creating\n 114:\t\tcgroups and specifying per device IO rate policies.\n 115:\t\n 116:\t\tSee Documentation/admin-guide/cgroup-v1/blkio-controller.rst for more information.\n 117:\t\n 118:\tconfig BLK_WBT\n 119:\t\tbool \"Enable support for block device writeback throttling\"\n"
}

21/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26779
							output: 32
							thoughts: 23
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Checking the Kconfig for BLK_CGROUP_IOLATENCY**

Right, let's dive into the kernel configuration. I need to understand the current state of `BLK_CGROUP_IOLATENCY`. My first instinct, as always, is to head directly to the `block/Kconfig` file. That's the definitive source for this kind of feature flag. I want to see if it's even enabled or if there are any dependencies I need to be aware of. What are the config options, the defaults, any relevant comments? Are there any dependencies on other cgroup subsystems, scheduler changes, or specific hardware? Has the upstream code been updated since I last looked at it? I need to be sure before I implement anything. This is my starting point to understand the available options.



22/2 2026/07/25 18:25 tool read-file
0m Args:
{
  "File": "block/Kconfig",
  "FirstLine": 130,
  "LineCount": 20
}

Results:
{
  "Contents": " 130:\t\tdepends on BLK_WBT\n 131:\t\thelp\n 132:\t\tEnable writeback throttling by default for request-based block devices.\n 133:\t\n 134:\tconfig BLK_CGROUP_IOLATENCY\n 135:\t\tbool \"Enable support for latency based cgroup IO protection\"\n 136:\t\tdepends on BLK_CGROUP\n 137:\t\thelp\n 138:\t\tEnabling this option enables the .latency interface for IO throttling.\n 139:\t\tThe IO controller will attempt to maintain average IO latencies below\n 140:\t\tthe configured latency target, throttling anybody with a higher latency\n 141:\t\ttarget than the victimized group.\n 142:\t\n 143:\t\tNote, this is an experimental interface and could be changed someday.\n 144:\t\n 145:\tconfig BLK_CGROUP_FC_APPID\n 146:\t\tbool \"Enable support to track FC I/O Traffic across cgroup applications\"\n 147:\t\tdepends on BLK_CGROUP \u0026\u0026 NVME_FC\n 148:\t\thelp\n 149:\t\t  Enabling this option enables the support to track FC I/O traffic across\n"
}

23/2 2026/07/25 18:25 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 27348
							output: 32
							thoughts: 21
						

Thoughts:
Okay, here's my interpretation of that thought process, from an expert's perspective:

**Diving into Block Device Configuration**

Right, so we need to track down the configuration option for the `BLK_CGROUP_IOCOST` feature. Given my experience, the first place I'd check is the standard configuration file for block devices. The most likely candidate would be `block/Kconfig`. This file usually houses all the relevant kernel configuration options related to block device functionalities, so it makes perfect sense to start there. This is where I'll find the definition, dependencies, and help text for that feature. Time to get digging!



24/2 2026/07/25 18:26 tool read-file
0m Args:
{
  "File": "block/Kconfig",
  "FirstLine": 150,
  "LineCount": 20
}

Results:
{
  "Contents": " 150:\t\t  cgroup applications. It enables the Fabric and the storage targets to\n 151:\t\t  identify, monitor, and handle FC traffic based on VM tags by inserting\n 152:\t\t  application specific identification into the FC frame.\n 153:\t\n 154:\tconfig BLK_CGROUP_IOCOST\n 155:\t\tbool \"Enable support for cost model based cgroup IO controller\"\n 156:\t\tdepends on BLK_CGROUP\n 157:\t\tselect BLK_RQ_ALLOC_TIME\n 158:\t\thelp\n 159:\t\tEnabling this option enables the .weight interface for cost\n 160:\t\tmodel based proportional IO control.  The IO controller\n 161:\t\tdistributes IO capacity between different groups based on\n 162:\t\ttheir share of the overall weight distribution.\n 163:\t\n 164:\tconfig BLK_CGROUP_IOPRIO\n 165:\t\tbool \"Cgroup I/O controller for assigning an I/O priority class\"\n 166:\t\tdepends on BLK_CGROUP\n 167:\t\thelp\n 168:\t\tEnable the .prio interface for assigning an I/O priority class to\n 169:\t\trequests. The I/O priority class affects the order in which an I/O\n"
}

25/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 27876
							output: 34
							thoughts: 24
						

Thoughts:
Here's my thought process, as if I were considering this myself:

**Investigating Block I/O Scheduler Configuration**

Okay, so I need to track down how `BFQ_GROUP_IOSCHED` is configured, which I assume is related to the Block I/O Scheduler. Let me take a look at the relevant Kconfig file. The path I want should be `block/Kconfig.iosched`. That's where I expect to find the conditional logic defining this option. I’ll open that file and see what's what. This should provide insights into whether this feature is enabled, disabled, and what its dependencies might be. Time to dive in!



26/2 2026/07/25 18:26 tool read-file
0m Args:
{
  "File": "block/Kconfig.iosched",
  "FirstLine": 20,
  "LineCount": 20
}

Results:
{
  "Contents": "  20:\t\ttristate \"BFQ I/O scheduler\"\n  21:\t\tselect BLK_ICQ\n  22:\t\thelp\n  23:\t\tBFQ I/O scheduler for BLK-MQ. BFQ distributes the bandwidth of\n  24:\t\tof the device among all processes according to their weights,\n  25:\t\tregardless of the device parameters and with any workload. It\n  26:\t\talso guarantees a low latency to interactive and soft\n  27:\t\treal-time applications.  Details in\n  28:\t\tDocumentation/block/bfq-iosched.rst\n  29:\t\n  30:\tconfig BFQ_GROUP_IOSCHED\n  31:\t       bool \"BFQ hierarchical scheduling support\"\n  32:\t       depends on IOSCHED_BFQ \u0026\u0026 BLK_CGROUP\n  33:\t       default y\n  34:\t       select BLK_CGROUP_RWSTAT\n  35:\t\thelp\n  36:\t\n  37:\t       Enable hierarchical scheduling in BFQ, using the blkio\n  38:\t       (cgroups-v1) or io (cgroups-v2) controller.\n  39:\t\n"
}

27/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28377
							output: 20
							thoughts: 402
						

Thoughts:
**Analyzing the BFQ I/O Scheduler Integration**

Right, so `IOSCHED_BFQ` is tristate, which suggests it's likely a module and should be compiled as built-in. That seems like a straightforward first step. Let's make that change.

Okay, let's look at the config options, just to make sure we've got all the dependencies. It's those CGROUPs options we're familiar with: `CGROUPS`, `BLK_CGROUP`, `BLK_CGROUP_IOLATENCY`, `BLK_CGROUP_IOCOST`, `BLK_DEV_THROTTLING`, naturally `IOSCHED_BFQ` itself, and finally `BFQ_GROUP_IOSCHED`.

Now for the crucial part: identifying the key functions we need to examine and potentially modify. The goal is to optimize this integration, so avoiding hot-path functions is paramount.

The functions of interest are: `bio_associate_blkg`, `bio_associate_blkg_from_css`, `bio_disassociate_blkg`, `blkg_lookup_create`, `blkcg_activate_policy`, `blkcg_deactivate_policy`, `blkg_conf_prep`, `blkg_conf_unprep`, `blk_throtl_bio`, `tg_set_limit`, `tg_set_conf`, `ioc_weight_write`, `submit_bio_noacct`, and `submit_bio`.

I'm thinking that `submit_bio` and `submit_bio_noacct` are definitely hot-path, so we need to be very careful with those. `bio_associate_blkg` looks hot too, but it seems critical to the core of the change. `blkg_lookup_create` seems like a good place to start digging deeper, a decent target. `blkcg_activate_policy` and `blkcg_deactivate_policy` are good candidates for policy changes. `blkg_conf_prep`, `blkg_conf_unprep`, `tg_set_limit`, `tg_set_conf`, and `ioc_weight_write` are all worth closer inspection as well, in terms of their roles in the BFQ scheduler.

I should check if `blkg_lookup_create` is static. Let's see if that's the case. That would affect how we approach it.



28/2 2026/07/25 18:26 tool grepper
0m Args:
{
  "Expression": "blkg_lookup_create"
}

Results:
{
  "Output": "block/blk-cgroup.c=374=static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,\n--\nblock/blk-cgroup.c-447-/**\nblock/blk-cgroup.c:448: * blkg_lookup_create - lookup blkg, try to create one if not there\nblock/blk-cgroup.c-449- * @blkcg: blkcg of interest\n--\nblock/blk-cgroup.c-461- */\nblock/blk-cgroup.c:462:static int blkg_lookup_create(struct blkcg *blkcg, struct gendisk *disk,\nblock/blk-cgroup.c-463-\t\t\t      gfp_t gfp_mask, struct blkcg_gq **blkgp)\n--\nblock/blk-cgroup.c=823=int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,\n--\nblock/blk-cgroup.c-844-\nblock/blk-cgroup.c:845:\tret = blkg_lookup_create(blkcg, disk, GFP_NOIO, \u0026blkg);\nblock/blk-cgroup.c-846-\tif (ret)\n--\nblock/blk-cgroup.c=1972=static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio,\n--\nblock/blk-cgroup.c-2000-\nblock/blk-cgroup.c:2001:\t\tret = blkg_lookup_create(blkcg, bio-\u003ebi_bdev-\u003ebd_disk,\nblock/blk-cgroup.c-2002-\t\t\t\t\t GFP_ATOMIC, \u0026blkg);\n--\nblock/blk-cgroup.c-2016-\tmutex_lock(\u0026q-\u003eblkcg_mutex);\nblock/blk-cgroup.c:2017:\tblkg_lookup_create(blkcg, bio-\u003ebi_bdev-\u003ebd_disk, GFP_NOIO, \u0026blkg);\nblock/blk-cgroup.c-2018-\tif (blkg)\n"
}

29/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29874
							output: 19
							thoughts: 198
						

Thoughts:
**Reviewing Block I/O Controller Exported Functions**

Okay, let's take a look at these functions. It appears that `blkg_lookup_create` is static, which is a bit of a concern. Ideally, we should be using `blkg_conf_prep` instead. That's the correct approach for configuration.

Good, I see that these are exported: `bio_associate_blkg`, `bio_associate_blkg_from_css`, and `bio_disassociate_blkg`. That makes sense for managing the association of block I/O groups. Similarly, we have `blkcg_activate_policy`, `blkcg_deactivate_policy`, `blkg_conf_prep`, and `blkg_conf_unprep`, which are crucial for policy management and preparation.

I'm also seeing that `blk_throtl_bio` is exported, though it is only available as `__blk_throtl_bio`, which makes perfect sense.

A couple of other functions stand out as potentially problematic. `tg_set_limit` is static. However, given that it's a cftype write function, this could be okay. I'm going to want to verify that. Same with `ioc_weight_write`. It’s also static, but it’s a cftype write function, which explains it.

Alright, time to go and check `tg_set_limit` and `tg_set_conf` a bit more carefully.



30/2 2026/07/25 18:26 tool grepper
0m Args:
{
  "Expression": "tg_set_limit"
}

Results:
{
  "Output": "block/blk-throttle.c=1543=static int tg_print_limit(struct seq_file *sf, void *v)\n--\nblock/blk-throttle.c-1549-\nblock/blk-throttle.c:1550:static ssize_t tg_set_limit(struct kernfs_open_file *of,\nblock/blk-throttle.c-1551-\t\t\t  char *buf, size_t nbytes, loff_t off)\n--\nblock/blk-throttle.c=1637=static struct cftype throtl_files[] = {\n--\nblock/blk-throttle.c-1641-\t\t.seq_show = tg_print_limit,\nblock/blk-throttle.c:1642:\t\t.write = tg_set_limit,\nblock/blk-throttle.c-1643-\t},\n"
}

31/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 30636
							output: 21
							thoughts: 157
						

Thoughts:
**Analyzing Potential Points of Interest in the Block IO Control Group (Blkcg) Code**

Okay, so I'm thinking about how to best approach this. That `tg_set_limit` function... since it's declared `static`, it could potentially get inlined by the compiler, which would make it hard to pinpoint directly as a distinct symbol during analysis. But wait, it's a callback function, right? That means the compiler probably *won't* inline it, so that's good. Still, to be efficient in my investigation, it's better to stick to exported functions or those that aren't static. It simplifies things considerably.

Alright, time to get a list. Let's see... I've got: `bio_associate_blkg`, `bio_associate_blkg_from_css`, `bio_disassociate_blkg`, `blkcg_activate_policy`, `blkcg_deactivate_policy`, `blkg_conf_prep`, `blkg_conf_unprep`, and finally `__blk_throtl_bio`. Now that I have the list I'm going to dig into `__blk_throtl_bio` first. That seems like a logical next step to understand how things are operating.



32/2 2026/07/25 18:26 tool grepper
0m Args:
{
  "Expression": "__blk_throtl_bio"
}

Results:
{
  "Output": "block/blk-throttle.c=1810=static bool tg_within_limit(struct throtl_grp *tg, struct bio *bio, bool rw)\n--\nblock/blk-throttle.c-1837-\nblock/blk-throttle.c:1838:bool __blk_throtl_bio(struct bio *bio)\nblock/blk-throttle.c-1839-{\n--\nblock/blk-throttle.h=153=void blk_throtl_exit(struct gendisk *disk);\nblock/blk-throttle.h:154:bool __blk_throtl_bio(struct bio *bio);\nblock/blk-throttle.h-155-void blk_throtl_cancel_bios(struct gendisk *disk);\n--\nblock/blk-throttle.h=196=static inline bool blk_throtl_bio(struct bio *bio)\n--\nblock/blk-throttle.h-204-\nblock/blk-throttle.h:205:\treturn __blk_throtl_bio(bio);\nblock/blk-throttle.h-206-}\n"
}

33/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31364
							output: 22
							thoughts: 31
						

Thoughts:
Okay, here's my summary, presented as a direct thought process:

**Diving into Block Device Internals**

Alright, so the task at hand involves this `__blk_throtl_bio` function. Good, I see it's *not* declared as static, which is something I wanted to confirm. Now, to understand its interactions and how it fits into the broader picture, I need to check how it's being used. I will look at its interactions with `bfq_bio_bfqg`. This should give me a good handle on how the block layer's throttling mechanism works. This should highlight how the block layer's throttling is influenced by the bfq bio queueing groups.



34/2 2026/07/25 18:26 tool grepper
0m Args:
{
  "Expression": "bfq_bio_bfqg"
}

Results:
{
  "Output": "block/bfq-cgroup.c=607=static void bfq_bio_update_blkg(struct bio *bio, struct blkcg_gq *blkg)\n--\nblock/bfq-cgroup.c-616-\nblock/bfq-cgroup.c:617:struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)\nblock/bfq-cgroup.c-618-{\n--\nblock/bfq-cgroup.c=792=void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio)\n--\nblock/bfq-cgroup.c-794-\tstruct bfq_data *bfqd = bic_to_bfqd(bic);\nblock/bfq-cgroup.c:795:\tstruct bfq_group *bfqg = bfq_bio_bfqg(bfqd, bio);\nblock/bfq-cgroup.c-796-\tuint64_t serial_nr;\n--\nblock/bfq-cgroup.c=1443=void bfq_end_wr_async(struct bfq_data *bfqd)\n--\nblock/bfq-cgroup.c-1449-\nblock/bfq-cgroup.c:1450:struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)\nblock/bfq-cgroup.c-1451-{\n--\nblock/bfq-iosched.c=5830=static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd,\n--\nblock/bfq-iosched.c-5840-\nblock/bfq-iosched.c:5841:\tbfqg = bfq_bio_bfqg(bfqd, bio);\nblock/bfq-iosched.c-5842-\tif (!is_sync) {\n--\nblock/bfq-iosched.h=1086=void bfq_end_wr_async(struct bfq_data *bfqd);\nblock/bfq-iosched.h:1087:struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio);\nblock/bfq-iosched.h-1088-struct blkcg_gq *bfqg_to_blkg(struct bfq_group *bfqg);\n"
}

35/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 32202
							output: 20
							thoughts: 28
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Investigating Non-Static Behaviors**

Alright, so `bfq_bio_bfqg` isn't static. That's a key observation. Now, I need to understand the implications of that non-static nature. It means there's potentially state being managed, and that state can change over time. My immediate thought is, "What's driving those changes? What are the dependencies?"

My mind immediately jumps to `bio_associate_blkg`. Given the naming conventions, it's very likely this function or method has some kind of relationship with `bfq_bio_bfqg`. I need to assess how they interact. Is `bio_associate_blkg` the function responsible for modifying or otherwise influencing the state of the non-static variable? Does it interact with it directly or indirectly? If it is, how? If not, what else could be the key driver? I need to understand the control flow, the data flow, the lifecycle. I need to know how these pieces fit together to get a clearer picture of how this system works. Knowing the function means I will have a more informed picture.



36/2 2026/07/25 18:26 tool grepper
0m Args:
{
  "Expression": "bio_associate_blkg"
}

Results:
{
  "Output": "Documentation/admin-guide/cgroup-v2.rst=3180=for the problem.  Filesystems can try to work around specific problem\nDocumentation/admin-guide/cgroup-v2.rst:3181:cases by skipping wbc_init_bio() and using bio_associate_blkg()\nDocumentation/admin-guide/cgroup-v2.rst-3182-directly.\n--\nblock/blk-cgroup.c=1972=static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio,\n--\nblock/blk-cgroup.c-2025-/**\nblock/blk-cgroup.c:2026: * bio_associate_blkg_from_css - associate a bio with a specified css\nblock/blk-cgroup.c-2027- * @bio: target bio\n--\nblock/blk-cgroup.c-2044- */\nblock/blk-cgroup.c:2045:bool bio_associate_blkg_from_css(struct bio *bio,\nblock/blk-cgroup.c-2046-\t\t\t\t struct cgroup_subsys_state *css)\n--\nblock/blk-cgroup.c-2065-}\nblock/blk-cgroup.c:2066:EXPORT_SYMBOL_GPL(bio_associate_blkg_from_css);\nblock/blk-cgroup.c-2067-\nblock/blk-cgroup.c-2068-/**\nblock/blk-cgroup.c:2069: * bio_associate_blkg - associate a bio with a blkg\nblock/blk-cgroup.c-2070- * @bio: target bio\n--\nblock/blk-cgroup.c-2080- */\nblock/blk-cgroup.c:2081:bool bio_associate_blkg(struct bio *bio)\nblock/blk-cgroup.c-2082-{\n--\nblock/blk-cgroup.c-2090-\t\tcss = bio_blkcg_css(bio);\nblock/blk-cgroup.c:2091:\t\treturn bio_associate_blkg_from_css(bio, css);\nblock/blk-cgroup.c-2092-\t}\n--\nblock/blk-cgroup.c-2099-\nblock/blk-cgroup.c:2100:\tret = bio_associate_blkg_from_css(bio, css);\nblock/blk-cgroup.c-2101-\tif (css)\n--\nblock/blk-cgroup.c-2104-}\nblock/blk-cgroup.c:2105:EXPORT_SYMBOL_GPL(bio_associate_blkg);\nblock/blk-cgroup.c-2106-\n--\nblock/blk-cgroup.c=2112=void bio_clone_blkg_association(struct bio *dst, struct bio *src)\n--\nblock/blk-cgroup.c-2114-\tif (src-\u003ebi_blkg)\nblock/blk-cgroup.c:2115:\t\tbio_associate_blkg_from_css(dst, bio_blkcg_css(src));\nblock/blk-cgroup.c-2116-}\n--\nblock/blk-core.c=817=void submit_bio_noacct(struct bio *bio)\n--\nblock/blk-core.c-839-\t\t */\nblock/blk-core.c:840:\t\tif (!bio_associate_blkg(bio)) {\nblock/blk-core.c-841-\t\t\tbio_endio_status(bio, BLK_STS_AGAIN);\n--\nblock/blk-core.c=974=void submit_bio(struct bio *bio)\n--\nblock/blk-core.c-992-\t */\nblock/blk-core.c:993:\tif (!bio_associate_blkg(bio)) {\nblock/blk-core.c-994-\t\tbio_endio_status(bio, BLK_STS_AGAIN);\n--\ninclude/linux/bio.h=496=static inline void bio_release_pages(struct bio *bio, bool mark_dirty)\n--\ninclude/linux/bio.h-505-#ifdef CONFIG_BLK_CGROUP\ninclude/linux/bio.h:506:bool bio_associate_blkg(struct bio *bio);\ninclude/linux/bio.h:507:bool bio_associate_blkg_from_css(struct bio *bio,\ninclude/linux/bio.h-508-\t\t\t\t struct cgroup_subsys_state *css);\n--\ninclude/linux/bio.h=511=void blkcg_punt_bio_submit(struct bio *bio);\ninclude/linux/bio.h-512-#else\t/* CONFIG_BLK_CGROUP */\ninclude/linux/bio.h:513:static inline bool bio_associate_blkg(struct bio *bio) { return true; }\ninclude/linux/bio.h:514:static inline bool bio_associate_blkg_from_css(struct bio *bio,\ninclude/linux/bio.h-515-\t\t\t\t\t       struct cgroup_subsys_state *css) { return true; }\n--\ninclude/linux/writeback.h=256=static inline void wbc_init_bio(struct writeback_control *wbc, struct bio *bio)\n--\ninclude/linux/writeback.h-264-\tif (wbc-\u003ewb)\ninclude/linux/writeback.h:265:\t\tbio_associate_blkg_from_css(bio, wbc-\u003ewb-\u003eblkcg_css);\ninclude/linux/writeback.h-266-}\n--\nmm/page_io.c=295=static inline void count_swpout_vm_event(struct folio *folio)\n--\nmm/page_io.c-308-#if defined(CONFIG_MEMCG) \u0026\u0026 defined(CONFIG_BLK_CGROUP)\nmm/page_io.c:309:static void bio_associate_blkg_from_page(struct bio *bio, struct folio *folio)\nmm/page_io.c-310-{\n--\nmm/page_io.c-323-\nmm/page_io.c:324:\tbio_associate_blkg_from_css(bio, css);\nmm/page_io.c-325-\tif (css)\n--\nmm/page_io.c-328-#else\nmm/page_io.c:329:#define bio_associate_blkg_from_page(bio, folio)\t\tdo { } while (0)\nmm/page_io.c-330-#endif /* CONFIG_MEMCG \u0026\u0026 CONFIG_BLK_CGROUP */\n--\nmm/page_io.c=419=static void swap_writepage_bdev_sync(struct folio *folio,\n--\nmm/page_io.c-428-\nmm/page_io.c:429:\tbio_associate_blkg_from_page(\u0026bio, folio);\nmm/page_io.c-430-\tcount_swpout_vm_event(folio);\n--\nmm/page_io.c=439=static void swap_writepage_bdev_async(struct folio *folio,\n--\nmm/page_io.c-448-\nmm/page_io.c:449:\tbio_associate_blkg_from_page(bio, folio);\nmm/page_io.c-450-\tcount_swpout_vm_event(folio);\n"
}

37/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 34393
							output: 21
							thoughts: 114
						

Thoughts:
**Examining the Block I/O CGroups Interface**

Okay, so I see a new set of exports related to block I/O CGroups. Let me quickly enumerate them: `bio_associate_blkg` and `bio_associate_blkg_from_css` – those are the association functions, likely for linking bios to the cgroup hierarchy. Then, `bio_disassociate_blkg` for obvious reasons. We also have `blkcg_activate_policy` and `blkcg_deactivate_policy`, which suggest control over enabling and disabling policies – presumably throttling or other forms of resource management - within the cgroup. The `blkg_conf_prep` and `blkg_conf_unprep` pair hints at some configuration preparation and teardown mechanisms for the block group cgroup. Finally, the inclusion of `__blk_throtl_bio` is interesting, suggesting potential integration or hooks into throttling specifically, which is key.

Now, let's zoom in on `bio_disassociate_blkg`. It's crucial to understand how this function works, and the implications of disassociating a bio. Is it safe to do at any point? What about pending I/O? How is this handled from the perspective of the request queue? Understanding its function call is the critical next step.



38/2 2026/07/25 18:26 tool grepper
0m Args:
{
  "Expression": "bio_disassociate_blkg"
}

Results:
{
  "Output": "block/bio.c=180=void bio_uninit(struct bio *bio)\nblock/bio.c-181-{\nblock/bio.c:182:\tbio_disassociate_blkg(bio);\nblock/bio.c-183-\tif (bio_integrity(bio))\n--\nblock/bio.c=1767=void bio_endio(struct bio *bio)\n--\nblock/bio.c-1801-\t */\nblock/bio.c:1802:\tbio_disassociate_blkg(bio);\nblock/bio.c-1803-\n--\nblock/blk-cgroup.c=2117=EXPORT_SYMBOL_GPL(bio_clone_blkg_association);\n--\nblock/blk-cgroup.c-2119-/**\nblock/blk-cgroup.c:2120: * bio_disassociate_blkg - disassociate a bio from its blkg\nblock/blk-cgroup.c-2121- * @bio: target bio\n--\nblock/blk-cgroup.c-2127- */\nblock/blk-cgroup.c:2128:void bio_disassociate_blkg(struct bio *bio)\nblock/blk-cgroup.c-2129-{\n--\nblock/blk-cgroup.c-2134-}\nblock/blk-cgroup.c:2135:EXPORT_SYMBOL_GPL(bio_disassociate_blkg);\nblock/blk-cgroup.c-2136-\n--\ninclude/linux/bio.h=509=void bio_clone_blkg_association(struct bio *dst, struct bio *src);\ninclude/linux/bio.h:510:void bio_disassociate_blkg(struct bio *bio);\ninclude/linux/bio.h-511-void blkcg_punt_bio_submit(struct bio *bio);\n--\ninclude/linux/bio.h=516=static inline void bio_clone_blkg_association(struct bio *dst,\ninclude/linux/bio.h-517-\t\t\t\t\t      struct bio *src) { }\ninclude/linux/bio.h:518:static inline void bio_disassociate_blkg(struct bio *bio) { }\ninclude/linux/bio.h-519-static inline void blkcg_punt_bio_submit(struct bio *bio)\n--\ninclude/linux/bio.h=525=static inline void bio_set_dev(struct bio *bio, struct block_device *bdev)\n--\ninclude/linux/bio.h-529-\t\tbio_clear_flag(bio, BIO_BPS_THROTTLED);\ninclude/linux/bio.h:530:\t\tbio_disassociate_blkg(bio);\ninclude/linux/bio.h-531-\t}\n"
}

39/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 35514
							output: 20
							thoughts: 160
						

Thoughts:
**My Assessment of Block I/O Control Functions**

Okay, so I'm diving into this code related to block I/O control and I need to get a handle on the key functions. Right now, I'm focusing on these specific ones: `bio_associate_blkg`, `bio_associate_blkg_from_css`, `bio_disassociate_blkg`, `blkcg_activate_policy`, `blkcg_deactivate_policy`, `blkg_conf_prep`, `blkg_conf_unprep`, `__blk_throtl_bio`, and `bfq_bio_bfqg`. These seem central to the core operations of associating, disassociating, activating, and deactivating policies, and the preparatory and clean-up work required. Plus, `__blk_throtl_bio` and `bfq_bio_bfqg` are probably critical for the actual I/O dispatch and grouping within the system.

But, I need to be thorough. I'm going to quickly scan for other relevant functions. Alright, let's see, I've got `blkcg_destroy_blkgs`, `blkcg_init_disk`, `blkg_destroy_all` and `blkg_init_queue` now. This looks like a complete picture now of the setup and teardown of the control group infrastructure.

Since I'm looking for the initialization process, I'm going to take a closer look at `blkcg_init_disk` to understand how this ties into the block layer's device initialization. That's likely the right place to begin.



40/2 2026/07/25 18:26 tool grepper
0m Args:
{
  "Expression": "blkcg_init_disk"
}

Results:
{
  "Output": "block/blk-cgroup.c=1379=void blkg_init_queue(struct request_queue *q)\n--\nblock/blk-cgroup.c-1384-\nblock/blk-cgroup.c:1385:int blkcg_init_disk(struct gendisk *disk)\nblock/blk-cgroup.c-1386-{\n--\nblock/blk-cgroup.h=196=void blkg_init_queue(struct request_queue *q);\nblock/blk-cgroup.h:197:int blkcg_init_disk(struct gendisk *disk);\nblock/blk-cgroup.h-198-void blkcg_exit_disk(struct gendisk *disk);\n--\nblock/blk-cgroup.h=485=static inline void blkg_init_queue(struct request_queue *q) { }\nblock/blk-cgroup.h:486:static inline int blkcg_init_disk(struct gendisk *disk) { return 0; }\nblock/blk-cgroup.h-487-static inline void blkcg_exit_disk(struct gendisk *disk) { }\n--\nblock/genhd.c=1442=struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id,\n--\nblock/genhd.c-1470-\nblock/genhd.c:1471:\tif (blkcg_init_disk(disk))\nblock/genhd.c-1472-\t\tgoto out_erase_part0;\n"
}

41/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 36438
							output: 20
							thoughts: 39
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own mental process:

**Diving into Block I/O CGroups: A Point of Interest**

Alright, so `blkcg_init_disk` called from `__alloc_disk_node`... that's a key entry point, isn't it? A natural place to start looking at how block I/O control groups are initialized at the device level. I'm always looking for those central functions that orchestrate the setup. I immediately see the potential here for understanding the fundamental linking.

Now, to get the full picture, I need to see the other side. Let's shift our focus to `blkcg_exit_disk`. Seeing how things are *cleaned up* is just as important as the setup. I want to see how this resource is reclaimed. I need to look for that complementary function to give me a balanced understanding.



42/2 2026/07/25 18:26 tool grepper
0m Args:
{
  "Expression": "blkcg_exit_disk"
}

Results:
{
  "Output": "block/blk-cgroup.c=1385=int blkcg_init_disk(struct gendisk *disk)\n--\nblock/blk-cgroup.c-1392-\t * previous disk's blkcg state is cleaned up asynchronously via\nblock/blk-cgroup.c:1393:\t * disk_release() -\u003e blkcg_exit_disk(). Wait for that cleanup to\nblock/blk-cgroup.c-1394-\t * finish (indicated by root_blkg becoming NULL) before setting up\n--\nblock/blk-cgroup.c-1417-\nblock/blk-cgroup.c:1418:void blkcg_exit_disk(struct gendisk *disk)\nblock/blk-cgroup.c-1419-{\n--\nblock/blk-cgroup.h=197=int blkcg_init_disk(struct gendisk *disk);\nblock/blk-cgroup.h:198:void blkcg_exit_disk(struct gendisk *disk);\nblock/blk-cgroup.h-199-\n--\nblock/blk-cgroup.h=486=static inline int blkcg_init_disk(struct gendisk *disk) { return 0; }\nblock/blk-cgroup.h:487:static inline void blkcg_exit_disk(struct gendisk *disk) { }\nblock/blk-cgroup.h-488-static inline int blkcg_policy_register(struct blkcg_policy *pol) { return 0; }\n--\nblock/genhd.c=1272=static void disk_release(struct device *dev)\n--\nblock/genhd.c-1292-\nblock/genhd.c:1293:\tblkcg_exit_disk(disk);\nblock/genhd.c-1294-\n"
}

43/2 2026/07/25 18:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 37155
							output: 20
							thoughts: 29
						

Thoughts:
Here's a summary of my thought process:

**Diving into Block Device Resource Management**

Okay, so I'm looking at `blkcg_exit_disk`.  It seems like it's being called from `disk_release`, which makes sense; we're dealing with resource cleanup upon disk detachment. I need to understand what's happening at the block layer's control group exit. My next logical step is to examine the implications of this. Let's see what happens within `blkg_destroy_all`. I suspect this function holds the key to understanding how block group resources are released when the disk is finally gone. I need to understand this to make sure block IO resources are managed correctly.



44/2 2026/07/25 18:27 tool grepper
0m Args:
{
  "Expression": "blkg_destroy_all"
}

Results:
{
  "Output": "block/blk-cgroup.c=515=static void blkg_destroy(struct blkcg_gq *blkg)\n--\nblock/blk-cgroup.c-525-\t * blkg_free_workfn(), hence this function can be called from\nblock/blk-cgroup.c:526:\t * blkcg_destroy_blkgs() first and again from blkg_destroy_all() before\nblock/blk-cgroup.c-527-\t * blkg_free_workfn().\n--\nblock/blk-cgroup.c-561-\nblock/blk-cgroup.c:562:static void blkg_destroy_all(struct gendisk *disk)\nblock/blk-cgroup.c-563-{\n--\nblock/blk-cgroup.c=1418=void blkcg_exit_disk(struct gendisk *disk)\nblock/blk-cgroup.c-1419-{\nblock/blk-cgroup.c:1420:\tblkg_destroy_all(disk);\nblock/blk-cgroup.c-1421-\tblk_throtl_exit(disk);\n--\nblock/blk-throttle.c=1654=static void throtl_shutdown_timers(struct request_queue *q)\n--\nblock/blk-throttle.c-1659-\t/*\nblock/blk-throttle.c:1660:\t * blkg_destroy_all() has already offlined the policy, but blkg policy\nblock/blk-throttle.c-1661-\t * data is freed asynchronously.  Shut down per-group timers before\n--\nblock/blk-throttle.c=1931=void blk_throtl_exit(struct gendisk *disk)\n--\nblock/blk-throttle.c-1936-\t/*\nblock/blk-throttle.c:1937:\t * blkg_destroy_all() already deactivate throtl policy, just check and\nblock/blk-throttle.c-1938-\t * free throtl data.\n"
}

45/2 2026/07/25 18:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 37863
							output: 21
							thoughts: 25
						

Thoughts:
Here's my thought process, as requested:

**Deconstructing Block Group Destruction**

Okay, so I see `blkg_destroy_all`. The first thing that jumps out at me is that it's declared `static`. That immediately tells me it's likely an internal function, probably not something exposed directly to the outside world, or easily called externally. Its scope is probably limited to the specific file or module it's defined within. Now, the next logical step, and what I *really* want to understand, is how block groups are actually cleaned up. I'll need to look at how block groups are *destroyed*, and the call hierarchy leading to the `blkg_destroy_all` function to see its role. I need to understand what triggers this `destroy_all` function. Let's delve into the function `blkcg_destroy_blkgs` and see where this function is called from, and what else is involved in cleaning up the block group structures. I will also look at the arguments for any insights on why it exists.



46/2 2026/07/25 18:27 tool grepper
0m Args:
{
  "Expression": "blkcg_destroy_blkgs"
}

Results:
{
  "Output": "block/blk-cgroup.c=515=static void blkg_destroy(struct blkcg_gq *blkg)\n--\nblock/blk-cgroup.c-525-\t * blkg_free_workfn(), hence this function can be called from\nblock/blk-cgroup.c:526:\t * blkcg_destroy_blkgs() first and again from blkg_destroy_all() before\nblock/blk-cgroup.c-527-\t * blkg_free_workfn().\n--\nblock/blk-cgroup.c=1149=struct list_head *blkcg_get_cgwb_list(struct cgroup_subsys_state *css)\n--\nblock/blk-cgroup.c-1164- *\nblock/blk-cgroup.c:1165: * 2. When the nr_cgwbs count goes to zero, blkcg_destroy_blkgs() is called\nblock/blk-cgroup.c-1166- *    and handles the destruction of blkgs.  Here the css reference held by\n--\nblock/blk-cgroup.c=1176=static struct blkcg_gq *blkcg_get_first_blkg(struct blkcg *blkcg)\n--\nblock/blk-cgroup.c-1191-/**\nblock/blk-cgroup.c:1192: * blkcg_destroy_blkgs - responsible for shooting down blkgs\nblock/blk-cgroup.c-1193- * @blkcg: blkcg of interest\n--\nblock/blk-cgroup.c-1202- */\nblock/blk-cgroup.c:1203:static void blkcg_destroy_blkgs(struct blkcg *blkcg)\nblock/blk-cgroup.c-1204-{\n--\nblock/blk-cgroup.c=1233=void blkcg_pin_online(struct cgroup_subsys_state *blkcg_css)\n--\nblock/blk-cgroup.c-1244- * When this count goes to zero, all active cgwbs have finished so the\nblock/blk-cgroup.c:1245: * blkcg can continue destruction by calling blkcg_destroy_blkgs().\nblock/blk-cgroup.c-1246- */\nblock/blk-cgroup.c=1247=void blkcg_unpin_online(struct cgroup_subsys_state *blkcg_css)\n--\nblock/blk-cgroup.c-1257-\t\tparent = blkcg_parent(blkcg);\nblock/blk-cgroup.c:1258:\t\tblkcg_destroy_blkgs(blkcg);\nblock/blk-cgroup.c-1259-\t\tblkcg = parent;\n"
}

47/2 2026/07/25 18:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 38828
							output: 20
							thoughts: 35
						

Thoughts:
Okay, here's my interpretation of those thoughts, presented as my own mental process, given my expertise:

**Checking Blkcg Online Management**

Alright, so `blkcg_destroy_blkgs` is a static function, which is interesting in itself. Makes it a bit more isolated. My immediate next thought is to understand how the online state of block cgroups is managed. It's crucial for resource management to track when a cgroup is truly active and participating. I need to refresh my memory on the mechanisms related to this, and those two functions immediately spring to mind: `blkcg_pin_online` and `blkcg_unpin_online`. I need to review how these are used to accurately reflect the online/offline state, and what locking they employ to ensure thread-safety. I'm going to quickly scan their implementations and callers to refresh my mental model of the online lifecycle.



48/2 2026/07/25 18:27 tool grepper
0m Args:
{
  "Expression": "blkcg_pin_online"
}

Results:
{
  "Output": "block/blk-cgroup.c=1203=static void blkcg_destroy_blkgs(struct blkcg *blkcg)\n--\nblock/blk-cgroup.c-1225-/**\nblock/blk-cgroup.c:1226: * blkcg_pin_online - pin online state\nblock/blk-cgroup.c-1227- * @blkcg_css: blkcg of interest\n--\nblock/blk-cgroup.c-1232- */\nblock/blk-cgroup.c:1233:void blkcg_pin_online(struct cgroup_subsys_state *blkcg_css)\nblock/blk-cgroup.c-1234-{\n--\nblock/blk-cgroup.c=1365=static int blkcg_css_online(struct cgroup_subsys_state *css)\n--\nblock/blk-cgroup.c-1369-\t/*\nblock/blk-cgroup.c:1370:\t * blkcg_pin_online() is used to delay blkcg offline so that blkgs\nblock/blk-cgroup.c-1371-\t * don't go offline while cgwbs are still active on them.  Pin the\n--\nblock/blk-cgroup.c-1374-\tif (parent)\nblock/blk-cgroup.c:1375:\t\tblkcg_pin_online(\u0026parent-\u003ecss);\nblock/blk-cgroup.c-1376-\treturn 0;\n--\ninclude/linux/blk-cgroup.h=30=bool blk_cgroup_congested(void);\ninclude/linux/blk-cgroup.h:31:void blkcg_pin_online(struct cgroup_subsys_state *blkcg_css);\ninclude/linux/blk-cgroup.h-32-void blkcg_unpin_online(struct cgroup_subsys_state *blkcg_css);\n--\nmm/backing-dev.c=666=static int cgwb_create(struct backing_dev_info *bdi,\n--\nmm/backing-dev.c-735-\t\t\tlist_add(\u0026wb-\u003eblkcg_node, blkcg_cgwb_list);\nmm/backing-dev.c:736:\t\t\tblkcg_pin_online(blkcg_css);\nmm/backing-dev.c-737-\t\t\tcss_get(memcg_css);\n"
}

49/2 2026/07/25 18:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 39683
							output: 22
							thoughts: 750
						

Thoughts:
**Analyzing the Patch's Impact on Block I/O Control**

Okay, so I see `blkcg_pin_online` is exported, but the diff doesn't touch it. Hmm, let's go back and examine the patch's scope. I need to get a handle on what's actually being modified.

Alright, here's the list of functions changed: a mix of static and exported symbols. The static ones aren't directly exposed externally, so they're probably internal implementation details. I need to focus on the *exported* functions as they're the ones that could affect other parts of the system or potentially introduce compatibility issues.

Based on the list, there are some functions in there that look crucial to block I/O control and CGroups. Focusing my attention, I should prioritize `bio_associate_blkg`, `bio_associate_blkg_from_css`, `bio_disassociate_blkg`, and all the blkcg functions: `blkcg_activate_policy`, `blkcg_deactivate_policy`, `blkg_conf_prep`, `blkg_conf_unprep`, and `blkcg_init_disk`. These are all directly related to associating, disassociating, and managing block group settings and policy activation, which is the core of Cgroup integration with block I/O.

Then there are `bfq_bio_bfqg` and `bfq_end_wr_async`, probably related to the block fair queuing aspect.

And last but not least, those `blk_throtl` functions: `blk_throtl_cancel_bios`, `__blk_throtl_bio`, and `blk_throtl_exit`, which, given their names, are about throttling and could be very important. Time to check if `blk_throtl_cancel_bios` is correctly exported.



50/2 2026/07/25 18:27 tool grepper
0m Args:
{
  "Expression": "blk_throtl_cancel_bios"
}

Results:
{
  "Output": "block/blk-throttle.c=1729=static void tg_cancel_writeback_bios(struct throtl_grp *tg,\n--\nblock/blk-throttle.c-1767-\nblock/blk-throttle.c:1768:void blk_throtl_cancel_bios(struct gendisk *disk)\nblock/blk-throttle.c-1769-{\n--\nblock/blk-throttle.h=150=static inline bool blk_throtl_bio(struct bio *bio) { return false; }\nblock/blk-throttle.h:151:static inline void blk_throtl_cancel_bios(struct gendisk *disk) { }\nblock/blk-throttle.h-152-#else /* CONFIG_BLK_DEV_THROTTLING */\n--\nblock/blk-throttle.h=154=bool __blk_throtl_bio(struct bio *bio);\nblock/blk-throttle.h:155:void blk_throtl_cancel_bios(struct gendisk *disk);\nblock/blk-throttle.h-156-\n--\nblock/genhd.c=689=static void __del_gendisk(struct gendisk *disk)\n--\nblock/genhd.c-752-\nblock/genhd.c:753:\tblk_throtl_cancel_bios(disk);\nblock/genhd.c-754-\n"
}

51/2 2026/07/25 18:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 41204
							output: 20
							thoughts: 26
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Navigating the Throtl Code**

Alright, so the thing I'm looking at isn't *exported*, which is important. But it's also *not static*, and that subtle distinction is critical in understanding its potential scope. Now, I need to understand what this means in terms of the broader lifecycle, and potential for inter-module interactions.

Let's dig into `blk_throtl_exit`. That function call feels like the logical place to start tracing, given the context. It should give me the closure and where this is used within the framework. I'm assuming that it is where the non-static nature can be put to work.



52/2 2026/07/25 18:27 tool grepper
0m Args:
{
  "Expression": "blk_throtl_exit"
}

Results:
{
  "Output": "block/blk-cgroup.c=1418=void blkcg_exit_disk(struct gendisk *disk)\n--\nblock/blk-cgroup.c-1420-\tblkg_destroy_all(disk);\nblock/blk-cgroup.c:1421:\tblk_throtl_exit(disk);\nblock/blk-cgroup.c-1422-}\n--\nblock/blk-throttle.c=1838=bool __blk_throtl_bio(struct bio *bio)\n--\nblock/blk-throttle.c-1930-\nblock/blk-throttle.c:1931:void blk_throtl_exit(struct gendisk *disk)\nblock/blk-throttle.c-1932-{\n--\nblock/blk-throttle.h=140=static inline struct throtl_grp *blkg_to_tg(struct blkcg_gq *blkg)\n--\nblock/blk-throttle.h-148-#ifndef CONFIG_BLK_DEV_THROTTLING\nblock/blk-throttle.h:149:static inline void blk_throtl_exit(struct gendisk *disk) { }\nblock/blk-throttle.h-150-static inline bool blk_throtl_bio(struct bio *bio) { return false; }\nblock/blk-throttle.h=151=static inline void blk_throtl_cancel_bios(struct gendisk *disk) { }\nblock/blk-throttle.h-152-#else /* CONFIG_BLK_DEV_THROTTLING */\nblock/blk-throttle.h:153:void blk_throtl_exit(struct gendisk *disk);\nblock/blk-throttle.h-154-bool __blk_throtl_bio(struct bio *bio);\n"
}

53/2 2026/07/25 18:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 41859
							output: 22
							thoughts: 21
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Navigating the Non-Static Nature**

Alright, so the core concept I'm working with here is undeniably non-static. That's the key. That's the immediate, driving factor that's informing the next steps. Static assumptions are off the table; the situation is dynamic. We're dealing with something that's constantly evolving or changing.

Given this understanding, the next logical move is to take a closer look at `bfq_end_wr_async`. This function seems like the crucial point of intersection, the place where things are happening in an asynchronous write context. It's almost certain that the non-static element will be manifested here, or that an understanding of its behaviour in context of this function will lead to the solution. So, let's dive into that and see what we can find.



54/2 2026/07/25 18:27 tool grepper
0m Args:
{
  "Expression": "bfq_end_wr_async"
}

Results:
{
  "Output": "block/bfq-cgroup.c=890=static void bfq_pd_offline(struct blkg_policy_data *pd)\n--\nblock/bfq-cgroup.c-953-\nblock/bfq-cgroup.c:954:void bfq_end_wr_async(struct bfq_data *bfqd)\nblock/bfq-cgroup.c-955-{\n--\nblock/bfq-cgroup.c-964-\nblock/bfq-cgroup.c:965:\t\tbfq_end_wr_async_queues(bfqd, bfqg);\nblock/bfq-cgroup.c-966-\t}\nblock/bfq-cgroup.c:967:\tbfq_end_wr_async_queues(bfqd, bfqd-\u003eroot_group);\nblock/bfq-cgroup.c-968-\n--\nblock/bfq-cgroup.c=1441=void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio) {}\nblock/bfq-cgroup.c-1442-\nblock/bfq-cgroup.c:1443:void bfq_end_wr_async(struct bfq_data *bfqd)\nblock/bfq-cgroup.c-1444-{\nblock/bfq-cgroup.c-1445-\tspin_lock_irq(\u0026bfqd-\u003elock);\nblock/bfq-cgroup.c:1446:\tbfq_end_wr_async_queues(bfqd, bfqd-\u003eroot_group);\nblock/bfq-cgroup.c-1447-\tspin_unlock_irq(\u0026bfqd-\u003elock);\n--\nblock/bfq-iosched.c=2596=static void bfq_bfqq_end_wr(struct bfq_queue *bfqq)\n--\nblock/bfq-iosched.c-2627-\nblock/bfq-iosched.c:2628:void bfq_end_wr_async_queues(struct bfq_data *bfqd,\nblock/bfq-iosched.c-2629-\t\t\t     struct bfq_group *bfqg)\n--\nblock/bfq-iosched.c=2643=static void bfq_end_wr(struct bfq_data *bfqd)\n--\nblock/bfq-iosched.c-2658-\nblock/bfq-iosched.c:2659:\tbfq_end_wr_async(bfqd);\nblock/bfq-iosched.c-2660-}\n--\nblock/bfq-iosched.h=1056=void bfq_put_cooperator(struct bfq_queue *bfqq);\nblock/bfq-iosched.h:1057:void bfq_end_wr_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg);\nblock/bfq-iosched.h-1058-void bfq_release_process_ref(struct bfq_data *bfqd, struct bfq_queue *bfqq);\n--\nblock/bfq-iosched.h=1085=void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio);\nblock/bfq-iosched.h:1086:void bfq_end_wr_async(struct bfq_data *bfqd);\nblock/bfq-iosched.h-1087-struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio);\n"
}

55/2 2026/07/25 18:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 43019
							output: 360
							thoughts: 983
						

Thoughts:
**Analyzing Block Cgroup Locking Changes for Fuzzing**

Okay, this looks like a substantial change; it's definitely non-static. Let's start by identifying the critical functions. I see `submit_bio_noacct` and `submit_bio` are exported, but they're hot-path functions, so ideally, we want to avoid them for fuzzing. Instead, I'll focus on these functions: `bio_associate_blkg`, `bio_associate_blkg_from_css`, `bio_disassociate_blkg`, `blkcg_activate_policy`, `blkcg_deactivate_policy`, `blkg_conf_prep`, `blkg_conf_unprep`, `blkcg_init_disk`, `blk_throtl_cancel_bios`, `__blk_throtl_bio`, `blk_throtl_exit`, `bfq_bio_bfqg`, and `bfq_end_wr_async`. This seems like a solid list to target.

Next, I'll review the relevant kernel configurations. I'm focusing on anything related to cgroups, block devices, and the BFQ scheduler. I'm noting `CGROUPS`, `BLK_CGROUP`, `BLK_CGROUP_IOLATENCY`, `BLK_CGROUP_IOCOST`, `BLK_DEV_THROTTLING`, `IOSCHED_BFQ`, and `BFQ_GROUP_IOSCHED`. I'll quickly check if `CONFIG_BLK_CGROUP_IOPRIO` and `CONFIG_BLK_CGROUP_FC_APPID` are needed, but since they're not modified in this patch, I'll move on. However, I note that `CONFIG_BLK_CGROUP_RWSTAT` *is* selected by `BLK_DEV_THROTTLING` and `BFQ_GROUP_IOSCHED`, so it's likely relevant.

Time to confirm dependencies. `IOSCHED_BFQ` depends on `BLOCK`. `BLK_DEV_THROTTLING` relies on `BLK_CGROUP`. Both `BLK_CGROUP_IOLATENCY` and `BLK_CGROUP_IOCOST` also depend on `BLK_CGROUP`. Finally, `BFQ_GROUP_IOSCHED` requires both `IOSCHED_BFQ` and `BLK_CGROUP`. This confirms my initial list: `CGROUPS`, `BLK_CGROUP`, `BLK_CGROUP_IOLATENCY`, `BLK_CGROUP_IOCOST`, `BLK_DEV_THROTTLING`, `IOSCHED_BFQ`, and `BFQ_GROUP_IOSCHED`.

I need to quickly double-check that `IOSCHED_BFQ` is a tristate, to be absolutely sure. Yes, it is! Syzkaller should enable this as a built-in.

Okay, now for the rationale. This patch fundamentally alters the blkcg locking mechanism. Specifically, it shifts from using the request queue's `queue_lock` to a dedicated `blkcg_mutex` and the `blkcg->lock`. It also introduces a return value to `bio_associate_blkg` to address `REQ_NOWAIT` failures and adds `bio_disassociate_blkg`. Plus, crucial blkcg policies like throttle, iocost, iolatency, and bfq are updated to use the new locking scheme. These are essential modifications to the block layer's cgroup integration, which makes them high-priority fuzzing targets.

Therefore, the `WorthFuzzing` flag is set to true. The `FocusSymbols` are the list of functions I identified. The configurations to enable are the final config list.

Now I will call the `set-results` tool.



56/2 2026/07/25 18:27 tool set-results
0m Args:
{
  "EnableConfigs": [
    "CGROUPS",
    "BLK_CGROUP",
    "BLK_CGROUP_IOLATENCY",
    "BLK_CGROUP_IOCOST",
    "BLK_DEV_THROTTLING",
    "IOSCHED_BFQ",
    "BFQ_GROUP_IOSCHED"
  ],
  "FocusSymbols": [
    "bio_associate_blkg",
    "bio_associate_blkg_from_css",
    "bio_disassociate_blkg",
    "blkcg_activate_policy",
    "blkcg_deactivate_policy",
    "blkg_conf_prep",
    "blkg_conf_unprep",
    "blkcg_init_disk",
    "blk_throtl_cancel_bios",
    "__blk_throtl_bio",
    "blk_throtl_exit",
    "bfq_bio_bfqg",
    "bfq_end_wr_async"
  ],
  "Reasoning": "The patch modifies the block cgroup (blkcg) locking mechanism, changing it from using the request queue's `queue_lock` to a dedicated `blkcg_mutex` and the `blkcg-\u003elock`. It also introduces a return value to `bio_associate_blkg` to handle `REQ_NOWAIT` failures gracefully, adds `bio_disassociate_blkg`, and updates various blkcg policies (throttle, iocost, iolatency, bfq) to use the new locking scheme. These are core functional changes to the block layer's cgroup integration and should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "CGROUPS",
    "BLK_CGROUP",
    "BLK_CGROUP_IOLATENCY",
    "BLK_CGROUP_IOCOST",
    "BLK_DEV_THROTTLING",
    "IOSCHED_BFQ",
    "BFQ_GROUP_IOSCHED"
  ],
  "FocusSymbols": [
    "bio_associate_blkg",
    "bio_associate_blkg_from_css",
    "bio_disassociate_blkg",
    "blkcg_activate_policy",
    "blkcg_deactivate_policy",
    "blkg_conf_prep",
    "blkg_conf_unprep",
    "blkcg_init_disk",
    "blk_throtl_cancel_bios",
    "__blk_throtl_bio",
    "blk_throtl_exit",
    "bfq_bio_bfqg",
    "bfq_end_wr_async"
  ],
  "Reasoning": "The patch modifies the block cgroup (blkcg) locking mechanism, changing it from using the request queue's `queue_lock` to a dedicated `blkcg_mutex` and the `blkcg-\u003elock`. It also introduces a return value to `bio_associate_blkg` to handle `REQ_NOWAIT` failures gracefully, adds `bio_disassociate_blkg`, and updates various blkcg policies (throttle, iocost, iolatency, bfq) to use the new locking scheme. These are core functional changes to the block layer's cgroup integration and should be fuzzed.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)