]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
scsi: scsi_debug: Abort SCSI commands via an internal command
authorBart Van Assche <bvanassche@acm.org>
Fri, 31 Oct 2025 20:39:15 +0000 (13:39 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 12 Nov 2025 22:02:31 +0000 (17:02 -0500)
Add a .queue_reserved_command() implementation and call it from the code
path that aborts SCSI commands. This ensures that the code for
allocating a pseudo SCSI device and also the code for allocating and
processing reserved commands gets triggered while running blktests.

Most of the code in this patch is a modified version of code from John
Garry. See also
https://lore.kernel.org/linux-scsi/75018e17-4dea-4e1b-8c92-7a224a1e13b9@oracle.com/

Reviewed-by: John Garry <john.g.garry@oracle.com>
Suggested-by: John Garry <john.g.garry@oracle.com>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Link: https://patch.msgid.link/20251031204029.2883185-8-bvanassche@acm.org
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/scsi_debug.c

index b2ab97be5db3d43d5a5647968623b8db72448379..7291b7a7f1b08981e6d3b9e969f4625faaca0ffd 100644 (file)
@@ -6752,20 +6752,59 @@ static bool scsi_debug_stop_cmnd(struct scsi_cmnd *cmnd)
        return false;
 }
 
+struct sdebug_abort_cmd {
+       u32 unique_tag;
+};
+
+enum sdebug_internal_cmd_type {
+       SCSI_DEBUG_ABORT_CMD,
+};
+
+struct sdebug_internal_cmd {
+       enum sdebug_internal_cmd_type type;
+
+       union {
+               struct sdebug_abort_cmd abort_cmd;
+       };
+};
+
+union sdebug_priv {
+       struct sdebug_scsi_cmd cmd;
+       struct sdebug_internal_cmd internal_cmd;
+};
+
 /*
- * Called from scsi_debug_abort() only, which is for timed-out cmd.
+ * Abort SCSI command @cmnd. Only called from scsi_debug_abort(). Although
+ * it would be possible to call scsi_debug_stop_cmnd() directly, an internal
+ * command is allocated and submitted to trigger the reserved command
+ * infrastructure.
  */
 static bool scsi_debug_abort_cmnd(struct scsi_cmnd *cmnd)
 {
-       struct sdebug_scsi_cmd *sdsc = scsi_cmd_priv(cmnd);
-       unsigned long flags;
-       bool res;
-
-       spin_lock_irqsave(&sdsc->lock, flags);
-       res = scsi_debug_stop_cmnd(cmnd);
-       spin_unlock_irqrestore(&sdsc->lock, flags);
-
-       return res;
+       struct Scsi_Host *shost = cmnd->device->host;
+       struct request *rq = scsi_cmd_to_rq(cmnd);
+       u32 unique_tag = blk_mq_unique_tag(rq);
+       struct sdebug_internal_cmd *internal_cmd;
+       struct scsi_cmnd *abort_cmd;
+       struct request *abort_rq;
+       blk_status_t res;
+
+       abort_cmd = scsi_get_internal_cmd(shost->pseudo_sdev, DMA_NONE,
+                                         BLK_MQ_REQ_RESERVED);
+       if (!abort_cmd)
+               return false;
+       internal_cmd = scsi_cmd_priv(abort_cmd);
+       *internal_cmd = (struct sdebug_internal_cmd) {
+               .type = SCSI_DEBUG_ABORT_CMD,
+               .abort_cmd = {
+                       .unique_tag = unique_tag,
+               },
+       };
+       abort_rq = scsi_cmd_to_rq(abort_cmd);
+       abort_rq->timeout = secs_to_jiffies(3);
+       res = blk_execute_rq(abort_rq, true);
+       scsi_put_internal_cmd(abort_cmd);
+       return res == BLK_STS_OK;
 }
 
 /*
@@ -9220,6 +9259,56 @@ out_handle:
        return ret;
 }
 
+/* Process @scp, a request to abort a SCSI command by tag. */
+static void scsi_debug_abort_cmd(struct Scsi_Host *shost, struct scsi_cmnd *scp)
+{
+       struct sdebug_internal_cmd *internal_cmd = scsi_cmd_priv(scp);
+       struct sdebug_abort_cmd *abort_cmd = &internal_cmd->abort_cmd;
+       const u32 unique_tag = abort_cmd->unique_tag;
+       struct scsi_cmnd *to_be_aborted_scmd =
+               scsi_host_find_tag(shost, unique_tag);
+       struct sdebug_scsi_cmd *to_be_aborted_sdsc =
+               scsi_cmd_priv(to_be_aborted_scmd);
+       bool res = false;
+
+       if (!to_be_aborted_scmd) {
+               pr_err("%s: command with tag %#x not found\n", __func__,
+                      unique_tag);
+               return;
+       }
+
+       scoped_guard(spinlock_irqsave, &to_be_aborted_sdsc->lock)
+               res = scsi_debug_stop_cmnd(to_be_aborted_scmd);
+
+       if (res)
+               pr_info("%s: aborted command with tag %#x\n",
+                       __func__, unique_tag);
+       else
+               pr_err("%s: failed to abort command with tag %#x\n",
+                      __func__, unique_tag);
+
+       set_host_byte(scp, res ? DID_OK : DID_ERROR);
+}
+
+static int scsi_debug_process_reserved_command(struct Scsi_Host *shost,
+                                              struct scsi_cmnd *scp)
+{
+       struct sdebug_internal_cmd *internal_cmd = scsi_cmd_priv(scp);
+
+       switch (internal_cmd->type) {
+       case SCSI_DEBUG_ABORT_CMD:
+               scsi_debug_abort_cmd(shost, scp);
+               break;
+       default:
+               WARN_ON_ONCE(true);
+               set_host_byte(scp, DID_ERROR);
+               break;
+       }
+
+       scsi_done(scp);
+       return 0;
+}
+
 static int scsi_debug_queuecommand(struct Scsi_Host *shost,
                                   struct scsi_cmnd *scp)
 {
@@ -9420,6 +9509,9 @@ static int sdebug_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
        struct sdebug_scsi_cmd *sdsc = scsi_cmd_priv(cmd);
        struct sdebug_defer *sd_dp = &sdsc->sd_dp;
 
+       if (blk_mq_is_reserved_rq(scsi_cmd_to_rq(cmd)))
+               return 0;
+
        spin_lock_init(&sdsc->lock);
        hrtimer_setup(&sd_dp->hrt, sdebug_q_cmd_hrt_complete, CLOCK_MONOTONIC,
                      HRTIMER_MODE_REL_PINNED);
@@ -9439,6 +9531,7 @@ static const struct scsi_host_template sdebug_driver_template = {
        .sdev_destroy =         scsi_debug_sdev_destroy,
        .ioctl =                scsi_debug_ioctl,
        .queuecommand =         scsi_debug_queuecommand,
+       .queue_reserved_command = scsi_debug_process_reserved_command,
        .change_queue_depth =   sdebug_change_qdepth,
        .map_queues =           sdebug_map_queues,
        .mq_poll =              sdebug_blk_mq_poll,
@@ -9448,6 +9541,7 @@ static const struct scsi_host_template sdebug_driver_template = {
        .eh_bus_reset_handler = scsi_debug_bus_reset,
        .eh_host_reset_handler = scsi_debug_host_reset,
        .can_queue =            SDEBUG_CANQUEUE,
+       .nr_reserved_cmds =     1,
        .this_id =              7,
        .sg_tablesize =         SG_MAX_SEGMENTS,
        .cmd_per_lun =          DEF_CMD_PER_LUN,
@@ -9456,7 +9550,7 @@ static const struct scsi_host_template sdebug_driver_template = {
        .module =               THIS_MODULE,
        .skip_settle_delay =    1,
        .track_queue_depth =    1,
-       .cmd_size = sizeof(struct sdebug_scsi_cmd),
+       .cmd_size = sizeof(union sdebug_priv),
        .init_cmd_priv = sdebug_init_cmd_priv,
        .target_alloc =         sdebug_target_alloc,
        .target_destroy =       sdebug_target_destroy,