ublk: check list membership before cancelling batch fetch command

Add !list_empty(&fcmd->node) check in ublk_batch_cancel_cmd() to ensure
the fcmd hasn't already been removed from the list. Once an fcmd is
removed from the list, it's considered claimed by whoever removed it
and will be freed by that path.

Meantime switch to list_del_init() for deleting it from list.

Signed-off-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Ming Lei 2026-01-30 00:19:50 +08:00 committed by Jens Axboe
parent 373df2c025
commit 66d3af8d5d
1 changed files with 13 additions and 3 deletions

View File

@ -738,7 +738,7 @@ static void ublk_batch_deinit_fetch_buf(struct ublk_queue *ubq,
int res)
{
spin_lock(&ubq->evts_lock);
list_del(&fcmd->node);
list_del_init(&fcmd->node);
WARN_ON_ONCE(fcmd != ubq->active_fcmd);
__ublk_release_fcmd(ubq);
spin_unlock(&ubq->evts_lock);
@ -2693,6 +2693,16 @@ static void ublk_cancel_cmd(struct ublk_queue *ubq, unsigned tag,
io_uring_cmd_done(io->cmd, UBLK_IO_RES_ABORT, issue_flags);
}
/*
* Cancel a batch fetch command if it hasn't been claimed by another path.
*
* An fcmd can only be cancelled if:
* 1. It's not the active_fcmd (which is currently being processed)
* 2. It's still on the list (!list_empty check) - once removed from the list,
* the fcmd is considered claimed and will be freed by whoever removed it
*
* Use list_del_init() so subsequent list_empty() checks work correctly.
*/
static void ublk_batch_cancel_cmd(struct ublk_queue *ubq,
struct ublk_batch_fetch_cmd *fcmd,
unsigned int issue_flags)
@ -2700,9 +2710,9 @@ static void ublk_batch_cancel_cmd(struct ublk_queue *ubq,
bool done;
spin_lock(&ubq->evts_lock);
done = (READ_ONCE(ubq->active_fcmd) != fcmd);
done = (READ_ONCE(ubq->active_fcmd) != fcmd) && !list_empty(&fcmd->node);
if (done)
list_del(&fcmd->node);
list_del_init(&fcmd->node);
spin_unlock(&ubq->evts_lock);
if (done) {