Root Cause Analysis
1. Raced Variable and Involved Components
- Variable / Field:
rq->tag (offset 32 bytes / 256 bits, size 4 bytes in struct request), holding the allocated block driver tag or BLK_MQ_NO_TAG (-1U / 0xffffffff).
- Files:
2. Race Mechanism and Lifecycle Violation
When an I/O scheduler is configured (e.g. RQF_SCHED_TAGS), requests are assigned a scheduler tag (rq->internal_tag) upon creation, while the hardware driver tag (rq->tag) is dynamically acquired right before dispatch to the device driver via blk_mq_prep_dispatch_rq() $\to$ blk_mq_get_driver_tag() $\to$ __blk_mq_alloc_driver_tag().
During request dispatch in blk_mq_dispatch_rq_list():
- If the target device driver returns
BLK_STS_DEV_RESOURCE or BLK_STS_RESOURCE, blk_mq_handle_dev_resource() invokes __blk_mq_requeue_request(), which calls blk_mq_put_driver_tag() to release the driver tag while keeping the scheduler tag intact.
__blk_mq_put_driver_tag() in block/blk-mq.h:
static inline void __blk_mq_put_driver_tag(struct blk_mq_hw_ctx *hctx,
struct request *rq)
{
blk_mq_dec_active_requests(hctx);
blk_mq_put_tag(hctx->tags, rq->mq_ctx, rq->tag);
rq->tag = BLK_MQ_NO_TAG;
}
First, blk_mq_put_tag() clears the tag bit in the tag sbitmap (tags->bitmap_tags). Immediately after, a plain store rq->tag = BLK_MQ_NO_TAG overwrites rq->tag.
Concurrently on another CPU (task 426):
Runtime Impact in Production
- Memory Corruption (
false):
On production architectures (e.g., x86_64, arm64), 32-bit aligned reads and writes are single-copy atomic. The value read by blk_mq_find_and_get_req() will either be the old tag bitnr or BLK_MQ_NO_TAG. If BLK_MQ_NO_TAG is read, rq->tag != bitnr evaluates to true, rq is set to NULL, and iteration moves on. If bitnr is read, req_ref_inc_not_zero(rq) safely checks whether the request is still referenced before calling the iteration callback (scsi_host_check_in_flight()), which only inspects the command state. The request object belongs to the preallocated static request pool (sched_tags->static_rqs), so no memory safety or use-after-free issue can occur.
- Information Leak (
none):
No kernel memory or pointer values are exposed to userspace or external channels.
- Raw Denial of Service (
false):
The race only impacts whether a requeued command is transiently included in scsi_host_busy() or skipped. This does not cause kernel panics, deadlocks, infinite loops, or resource leaks.
- Networking / KVM Candidates (
false):
The issue is entirely within the block multiqueue subsystem (block/blk-mq*) and SCSI core (drivers/scsi/), with no involvement of virtualization, hypervisors, or networking.