ublk: make sure ubq->canceling is set when queue is frozen
[ Upstream commit 8741d0737921ec1c03cf59aebf4d01400c2b461a ]
Now ublk driver depends on `ubq->canceling` for deciding if the request
can be dispatched via uring_cmd & io_uring_cmd_complete_in_task().
Once ubq->canceling is set, the uring_cmd can be done via ublk_cancel_cmd()
and io_uring_cmd_done().
So set ubq->canceling when queue is frozen, this way makes sure that the
flag can be observed from ublk_queue_rq() reliably, and avoids
use-after-free on uring_cmd.
Fixes: 216c8f5ef0 ("ublk: replace monitor with cancelable uring_cmd")
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250327095123.179113-2-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
efd101b1f0
commit
7e3497d7da
+30
-11
@@ -1416,18 +1416,28 @@ static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ublk_abort_requests(struct ublk_device *ub, struct ublk_queue *ubq)
|
/* Must be called when queue is frozen */
|
||||||
|
static bool ublk_mark_queue_canceling(struct ublk_queue *ubq)
|
||||||
{
|
{
|
||||||
struct gendisk *disk;
|
bool canceled;
|
||||||
|
|
||||||
spin_lock(&ubq->cancel_lock);
|
spin_lock(&ubq->cancel_lock);
|
||||||
if (ubq->canceling) {
|
canceled = ubq->canceling;
|
||||||
spin_unlock(&ubq->cancel_lock);
|
if (!canceled)
|
||||||
return false;
|
ubq->canceling = true;
|
||||||
}
|
|
||||||
ubq->canceling = true;
|
|
||||||
spin_unlock(&ubq->cancel_lock);
|
spin_unlock(&ubq->cancel_lock);
|
||||||
|
|
||||||
|
return canceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ublk_abort_requests(struct ublk_device *ub, struct ublk_queue *ubq)
|
||||||
|
{
|
||||||
|
bool was_canceled = ubq->canceling;
|
||||||
|
struct gendisk *disk;
|
||||||
|
|
||||||
|
if (was_canceled)
|
||||||
|
return false;
|
||||||
|
|
||||||
spin_lock(&ub->lock);
|
spin_lock(&ub->lock);
|
||||||
disk = ub->ub_disk;
|
disk = ub->ub_disk;
|
||||||
if (disk)
|
if (disk)
|
||||||
@@ -1438,14 +1448,23 @@ static bool ublk_abort_requests(struct ublk_device *ub, struct ublk_queue *ubq)
|
|||||||
if (!disk)
|
if (!disk)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Now we are serialized with ublk_queue_rq() */
|
/*
|
||||||
|
* Now we are serialized with ublk_queue_rq()
|
||||||
|
*
|
||||||
|
* Make sure that ubq->canceling is set when queue is frozen,
|
||||||
|
* because ublk_queue_rq() has to rely on this flag for avoiding to
|
||||||
|
* touch completed uring_cmd
|
||||||
|
*/
|
||||||
blk_mq_quiesce_queue(disk->queue);
|
blk_mq_quiesce_queue(disk->queue);
|
||||||
/* abort queue is for making forward progress */
|
was_canceled = ublk_mark_queue_canceling(ubq);
|
||||||
ublk_abort_queue(ub, ubq);
|
if (!was_canceled) {
|
||||||
|
/* abort queue is for making forward progress */
|
||||||
|
ublk_abort_queue(ub, ubq);
|
||||||
|
}
|
||||||
blk_mq_unquiesce_queue(disk->queue);
|
blk_mq_unquiesce_queue(disk->queue);
|
||||||
put_device(disk_to_dev(disk));
|
put_device(disk_to_dev(disk));
|
||||||
|
|
||||||
return true;
|
return !was_canceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ublk_cancel_cmd(struct ublk_queue *ubq, struct ublk_io *io,
|
static void ublk_cancel_cmd(struct ublk_queue *ubq, struct ublk_io *io,
|
||||||
|
|||||||
Reference in New Issue
Block a user