]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ata: libata-scsi: refactor ata_scsi_translate()
authorDamien Le Moal <dlemoal@kernel.org>
Wed, 17 Dec 2025 05:05:25 +0000 (14:05 +0900)
committerDamien Le Moal <dlemoal@kernel.org>
Tue, 13 Jan 2026 12:57:26 +0000 (21:57 +0900)
Factor out of ata_scsi_translate() the code handling queued command
deferral using the port qc_defer callback and issuing the queued
command with ata_qc_issue() into the new function ata_scsi_qc_issue(),
and simplify the goto used in ata_scsi_translate().
While at it, also add a lockdep annotation to check that the port lock
is held when ata_scsi_translate() is called.

No functional changes.

Cc: stable@vger.kernel.org
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: John Garry <john.g.garry@oracle.com>
Reviewed-by: Igor Pylypiv <ipylypiv@google.com>
drivers/ata/libata-scsi.c

index d388a4ff9ae44a71278d6974df8fc37e07b75c0a..be620bc04584866701096efac4b6d05fdc9bff81 100644 (file)
@@ -1691,6 +1691,42 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
        ata_qc_done(qc);
 }
 
+static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+       int ret;
+
+       if (!ap->ops->qc_defer)
+               goto issue;
+
+       /* Check if the command needs to be deferred. */
+       ret = ap->ops->qc_defer(qc);
+       switch (ret) {
+       case 0:
+               break;
+       case ATA_DEFER_LINK:
+               ret = SCSI_MLQUEUE_DEVICE_BUSY;
+               break;
+       case ATA_DEFER_PORT:
+               ret = SCSI_MLQUEUE_HOST_BUSY;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               ret = SCSI_MLQUEUE_HOST_BUSY;
+               break;
+       }
+
+       if (ret) {
+               /* Force a requeue of the command to defer its execution. */
+               ata_qc_free(qc);
+               return ret;
+       }
+
+issue:
+       ata_qc_issue(qc);
+
+       return 0;
+}
+
 /**
  *     ata_scsi_translate - Translate then issue SCSI command to ATA device
  *     @dev: ATA device to which the command is addressed
@@ -1714,66 +1750,49 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
  *     spin_lock_irqsave(host lock)
  *
  *     RETURNS:
- *     0 on success, SCSI_ML_QUEUE_DEVICE_BUSY if the command
- *     needs to be deferred.
+ *     0 on success, SCSI_ML_QUEUE_DEVICE_BUSY or SCSI_MLQUEUE_HOST_BUSY if the
+ *     command needs to be deferred.
  */
 static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
                              ata_xlat_func_t xlat_func)
 {
        struct ata_port *ap = dev->link->ap;
        struct ata_queued_cmd *qc;
-       int rc;
 
+       lockdep_assert_held(ap->lock);
+
+       /*
+        * ata_scsi_qc_new() calls scsi_done(cmd) in case of failure. So we
+        * have nothing further to do when allocating a qc fails.
+        */
        qc = ata_scsi_qc_new(dev, cmd);
        if (!qc)
-               goto err_mem;
+               return 0;
 
        /* data is present; dma-map it */
        if (cmd->sc_data_direction == DMA_FROM_DEVICE ||
            cmd->sc_data_direction == DMA_TO_DEVICE) {
                if (unlikely(scsi_bufflen(cmd) < 1)) {
                        ata_dev_warn(dev, "WARNING: zero len r/w req\n");
-                       goto err_did;
+                       cmd->result = (DID_ERROR << 16);
+                       goto done;
                }
 
                ata_sg_init(qc, scsi_sglist(cmd), scsi_sg_count(cmd));
-
                qc->dma_dir = cmd->sc_data_direction;
        }
 
        qc->complete_fn = ata_scsi_qc_complete;
 
        if (xlat_func(qc))
-               goto early_finish;
-
-       if (ap->ops->qc_defer) {
-               if ((rc = ap->ops->qc_defer(qc)))
-                       goto defer;
-       }
-
-       /* select device, send command to hardware */
-       ata_qc_issue(qc);
+               goto done;
 
-       return 0;
-
-early_finish:
-       ata_qc_free(qc);
-       scsi_done(cmd);
-       return 0;
+       return ata_scsi_qc_issue(ap, qc);
 
-err_did:
+done:
        ata_qc_free(qc);
-       cmd->result = (DID_ERROR << 16);
        scsi_done(cmd);
-err_mem:
        return 0;
-
-defer:
-       ata_qc_free(qc);
-       if (rc == ATA_DEFER_LINK)
-               return SCSI_MLQUEUE_DEVICE_BUSY;
-       else
-               return SCSI_MLQUEUE_HOST_BUSY;
 }
 
 /**