summaryrefslogtreecommitdiff
path: root/drivers/ata/libata-scsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/libata-scsi.c')
-rw-r--r--drivers/ata/libata-scsi.c227
1 files changed, 117 insertions, 110 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 3b65df914ebb..f44612e269a4 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1649,12 +1649,16 @@ nothing_to_do:
return 1;
}
-static void ata_qc_done(struct ata_queued_cmd *qc)
+static void ata_scsi_qc_done(struct ata_queued_cmd *qc, bool set_result,
+ u32 scmd_result)
{
struct scsi_cmnd *cmd = qc->scsicmd;
void (*done)(struct scsi_cmnd *) = qc->scsidone;
ata_qc_free(qc);
+
+ if (set_result)
+ cmd->result = scmd_result;
done(cmd);
}
@@ -1685,24 +1689,19 @@ void ata_scsi_deferred_qc_work(struct work_struct *work)
void ata_scsi_requeue_deferred_qc(struct ata_port *ap)
{
struct ata_queued_cmd *qc = ap->deferred_qc;
- struct scsi_cmnd *scmd;
lockdep_assert_held(ap->lock);
/*
* If we have a deferred qc when a reset occurs or NCQ commands fail,
* do not try to be smart about what to do with this deferred command
- * and simply retry it by completing it with DID_SOFT_ERROR.
+ * and simply requeue it by completing it with DID_REQUEUE.
*/
- if (!qc)
- return;
-
- scmd = qc->scsicmd;
- ap->deferred_qc = NULL;
- cancel_work(&ap->deferred_qc_work);
- ata_qc_free(qc);
- scmd->result = (DID_SOFT_ERROR << 16);
- scsi_done(scmd);
+ if (qc) {
+ ap->deferred_qc = NULL;
+ cancel_work(&ap->deferred_qc_work);
+ ata_scsi_qc_done(qc, true, DID_REQUEUE << 16);
+ }
}
static void ata_scsi_schedule_deferred_qc(struct ata_port *ap)
@@ -1758,7 +1757,7 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
ata_scsi_set_sense_information(qc);
}
- ata_qc_done(qc);
+ ata_scsi_qc_done(qc, false, 0);
ata_scsi_schedule_deferred_qc(ap);
}
@@ -2917,17 +2916,15 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL && qc->dev->sdev)
qc->dev->sdev->locked = 0;
- qc->scsicmd->result = SAM_STAT_CHECK_CONDITION;
- ata_qc_done(qc);
+ ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION);
return;
}
/* successful completion path */
if (cmd->cmnd[0] == INQUIRY && (cmd->cmnd[1] & 0x03) == 0)
atapi_fixup_inquiry(cmd);
- cmd->result = SAM_STAT_GOOD;
- ata_qc_done(qc);
+ ata_scsi_qc_done(qc, true, SAM_STAT_GOOD);
}
/**
* atapi_xlat - Initialize PACKET taskfile
@@ -3576,28 +3573,13 @@ invalid_opcode:
return 1;
}
-/**
- * ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN
- * @dev: Target device.
- * @cmd: SCSI command of interest.
- * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
- *
- * Yields a subset to satisfy scsi_report_opcode()
- *
- * LOCKING:
- * spin_lock_irqsave(host lock)
- */
-static unsigned int ata_scsiop_maint_in(struct ata_device *dev,
- struct scsi_cmnd *cmd, u8 *rbuf)
+static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
+ struct scsi_cmnd *cmd,
+ u8 *rbuf)
{
u8 *cdb = cmd->cmnd;
u8 supported = 0, cdlp = 0, rwcdlp = 0;
- if ((cdb[1] & 0x1f) != MI_REPORT_SUPPORTED_OPERATION_CODES) {
- ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
- return 0;
- }
-
if (cdb[2] != 1 && cdb[2] != 3) {
ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
@@ -3678,6 +3660,32 @@ static unsigned int ata_scsiop_maint_in(struct ata_device *dev,
}
/**
+ * ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Yields a subset to satisfy scsi_report_opcode()
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_maint_in(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
+{
+ u8 *cdb = cmd->cmnd;
+ u8 service_action = cdb[1] & 0x1f;
+
+ switch (service_action) {
+ case MI_REPORT_SUPPORTED_OPERATION_CODES:
+ return ata_scsi_report_supported_opcodes(dev, cmd, rbuf);
+ default:
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ return 0;
+ }
+}
+
+/**
* ata_scsi_report_zones_complete - convert ATA output
* @qc: command structure returning the data
*
@@ -4424,6 +4432,79 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
return NULL;
}
+/**
+ * ata_scsi_simulate - simulate SCSI command on ATA device
+ * @dev: the target device
+ * @cmd: SCSI command being sent to device.
+ *
+ * Interprets and directly executes a select list of SCSI commands
+ * that can be handled internally.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
+{
+ const u8 *scsicmd = cmd->cmnd;
+ u8 tmp8;
+
+ switch (scsicmd[0]) {
+ case INQUIRY:
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_inquiry);
+ break;
+
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_mode_sense);
+ break;
+
+ case READ_CAPACITY:
+ case SERVICE_ACTION_IN_16:
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_read_cap);
+ break;
+
+ case REPORT_LUNS:
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_report_luns);
+ break;
+
+ case REQUEST_SENSE:
+ ata_scsi_set_sense(dev, cmd, 0, 0, 0);
+ break;
+
+ /* if we reach this, then writeback caching is disabled,
+ * turning this into a no-op.
+ */
+ case SYNCHRONIZE_CACHE:
+ case SYNCHRONIZE_CACHE_16:
+ fallthrough;
+
+ /* no-op's, complete with success */
+ case REZERO_UNIT:
+ case SEEK_6:
+ case SEEK_10:
+ case TEST_UNIT_READY:
+ break;
+
+ case SEND_DIAGNOSTIC:
+ tmp8 = scsicmd[1] & ~(1 << 3);
+ if (tmp8 != 0x4 || scsicmd[3] || scsicmd[4])
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ break;
+
+ case MAINTENANCE_IN:
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_maint_in);
+ break;
+
+ /* all other commands */
+ default:
+ ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0);
+ /* "Invalid command operation code" */
+ break;
+ }
+
+ scsi_done(cmd);
+}
+
enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd,
struct ata_device *dev)
{
@@ -4526,80 +4607,6 @@ enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *shost,
}
EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
-/**
- * ata_scsi_simulate - simulate SCSI command on ATA device
- * @dev: the target device
- * @cmd: SCSI command being sent to device.
- *
- * Interprets and directly executes a select list of SCSI commands
- * that can be handled internally.
- *
- * LOCKING:
- * spin_lock_irqsave(host lock)
- */
-
-void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
-{
- const u8 *scsicmd = cmd->cmnd;
- u8 tmp8;
-
- switch(scsicmd[0]) {
- case INQUIRY:
- ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_inquiry);
- break;
-
- case MODE_SENSE:
- case MODE_SENSE_10:
- ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_mode_sense);
- break;
-
- case READ_CAPACITY:
- case SERVICE_ACTION_IN_16:
- ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_read_cap);
- break;
-
- case REPORT_LUNS:
- ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_report_luns);
- break;
-
- case REQUEST_SENSE:
- ata_scsi_set_sense(dev, cmd, 0, 0, 0);
- break;
-
- /* if we reach this, then writeback caching is disabled,
- * turning this into a no-op.
- */
- case SYNCHRONIZE_CACHE:
- case SYNCHRONIZE_CACHE_16:
- fallthrough;
-
- /* no-op's, complete with success */
- case REZERO_UNIT:
- case SEEK_6:
- case SEEK_10:
- case TEST_UNIT_READY:
- break;
-
- case SEND_DIAGNOSTIC:
- tmp8 = scsicmd[1] & ~(1 << 3);
- if (tmp8 != 0x4 || scsicmd[3] || scsicmd[4])
- ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
- break;
-
- case MAINTENANCE_IN:
- ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_maint_in);
- break;
-
- /* all other commands */
- default:
- ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0);
- /* "Invalid command operation code" */
- break;
- }
-
- scsi_done(cmd);
-}
-
int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *sht)
{
int i, rc;
@@ -4617,7 +4624,7 @@ int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *s
*(struct ata_port **)&shost->hostdata[0] = ap;
ap->scsi_host = shost;
- shost->transportt = ata_scsi_transport_template;
+ shost->transportt = &ata_scsi_transportt;
shost->unique_id = ap->print_id;
shost->max_id = 16;
shost->max_lun = 1;