]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ublk: check list membership before cancelling batch fetch command
authorMing Lei <ming.lei@redhat.com>
Thu, 29 Jan 2026 16:19:50 +0000 (00:19 +0800)
committerJens Axboe <axboe@kernel.dk>
Sat, 31 Jan 2026 13:36:41 +0000 (06:36 -0700)
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>
drivers/block/ublk_drv.c

index 60d07480a24c2723ef2f45b14cded6effa1bfd7c..92bd2351e3ad2192314b2f2bbdc269f8c8d7d44f 100644 (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) {