Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ublk: introduce and use ublk_set_canceling helper

For performance reasons (minimizing the number of cache lines accessed
in the hot path), we store the "canceling" state redundantly - there is
one flag in the device, which can be considered the source of truth, and
per-queue copies of that flag. This redundancy can cause confusion, and
opens the door to bugs where the state is set inconsistently. Try to
guard against these bugs by introducing a ublk_set_canceling helper
which is the sole mutator of both the per-device and per-queue canceling
state. This helper always sets the state consistently. Use the helper in
all places where we need to modify the canceling state.

No functional changes are expected.

Signed-off-by: Uday Shankar <ushankar@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250703-ublk_too_many_quiesce-v2-2-3527b5339eeb@purestorage.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Uday Shankar and committed by
Jens Axboe
10d77a8c 2fa9c930

+34 -20
+34 -20
drivers/block/ublk_drv.c
··· 1563 1563 put_device(disk_to_dev(disk)); 1564 1564 } 1565 1565 1566 + /* 1567 + * Use this function to ensure that ->canceling is consistently set for 1568 + * the device and all queues. Do not set these flags directly. 1569 + * 1570 + * Caller must ensure that: 1571 + * - cancel_mutex is held. This ensures that there is no concurrent 1572 + * access to ub->canceling and no concurrent writes to ubq->canceling. 1573 + * - there are no concurrent reads of ubq->canceling from the queue_rq 1574 + * path. This can be done by quiescing the queue, or through other 1575 + * means. 1576 + */ 1577 + static void ublk_set_canceling(struct ublk_device *ub, bool canceling) 1578 + __must_hold(&ub->cancel_mutex) 1579 + { 1580 + int i; 1581 + 1582 + ub->canceling = canceling; 1583 + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) 1584 + ublk_get_queue(ub, i)->canceling = canceling; 1585 + } 1586 + 1566 1587 static int ublk_ch_release(struct inode *inode, struct file *filp) 1567 1588 { 1568 1589 struct ublk_device *ub = filp->private_data; ··· 1612 1591 * All requests may be inflight, so ->canceling may not be set, set 1613 1592 * it now. 1614 1593 */ 1615 - ub->canceling = true; 1616 - for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { 1617 - struct ublk_queue *ubq = ublk_get_queue(ub, i); 1618 - 1619 - ubq->canceling = true; 1620 - ublk_abort_queue(ub, ubq); 1621 - } 1594 + mutex_lock(&ub->cancel_mutex); 1595 + ublk_set_canceling(ub, true); 1596 + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) 1597 + ublk_abort_queue(ub, ublk_get_queue(ub, i)); 1598 + mutex_unlock(&ub->cancel_mutex); 1622 1599 blk_mq_kick_requeue_list(disk->queue); 1623 1600 1624 1601 /* ··· 1742 1723 static void ublk_start_cancel(struct ublk_device *ub) 1743 1724 { 1744 1725 struct gendisk *disk = ublk_get_disk(ub); 1745 - int i; 1746 1726 1747 1727 /* Our disk has been dead */ 1748 1728 if (!disk) ··· 1758 1740 * touch completed uring_cmd 1759 1741 */ 1760 1742 blk_mq_quiesce_queue(disk->queue); 1761 - ub->canceling = true; 1762 - for (i = 0; i < ub->dev_info.nr_hw_queues; i++) 1763 - ublk_get_queue(ub, i)->canceling = true; 1743 + ublk_set_canceling(ub, true); 1764 1744 blk_mq_unquiesce_queue(disk->queue); 1765 1745 out: 1766 1746 mutex_unlock(&ub->cancel_mutex); ··· 1958 1942 for (j = 0; j < ubq->q_depth; j++) 1959 1943 ubq->ios[j].flags &= ~UBLK_IO_FLAG_CANCELED; 1960 1944 spin_unlock(&ubq->cancel_lock); 1961 - ubq->canceling = false; 1962 1945 ubq->fail_io = false; 1963 1946 } 1964 - ub->canceling = false; 1947 + mutex_lock(&ub->cancel_mutex); 1948 + ublk_set_canceling(ub, false); 1949 + mutex_unlock(&ub->cancel_mutex); 1965 1950 } 1966 1951 1967 1952 /* device can only be started after all IOs are ready */ ··· 3434 3417 /* zero means wait forever */ 3435 3418 u64 timeout_ms = header->data[0]; 3436 3419 struct gendisk *disk; 3437 - int i, ret = -ENODEV; 3420 + int ret = -ENODEV; 3438 3421 3439 3422 if (!(ub->dev_info.flags & UBLK_F_QUIESCE)) 3440 3423 return -EOPNOTSUPP; ··· 3452 3435 goto put_disk; 3453 3436 3454 3437 /* Mark the device as canceling */ 3438 + mutex_lock(&ub->cancel_mutex); 3455 3439 blk_mq_quiesce_queue(disk->queue); 3456 - ub->canceling = true; 3457 - for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { 3458 - struct ublk_queue *ubq = ublk_get_queue(ub, i); 3459 - 3460 - ubq->canceling = true; 3461 - } 3440 + ublk_set_canceling(ub, true); 3462 3441 blk_mq_unquiesce_queue(disk->queue); 3442 + mutex_unlock(&ub->cancel_mutex); 3463 3443 3464 3444 if (!timeout_ms) 3465 3445 timeout_ms = UINT_MAX;