|
@@ -1159,8 +1159,6 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
|
|
|
{
|
|
|
sdev->use_10_for_rw = 1;
|
|
|
sdev->use_10_for_ms = 1;
|
|
|
- sdev->no_report_opcodes = 1;
|
|
|
- sdev->no_write_same = 1;
|
|
|
|
|
|
/* Schedule policy is determined by ->qc_defer() callback and
|
|
|
* it needs to see every deferred qc. Set dev_blocked to 1 to
|
|
@@ -3287,7 +3285,7 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
|
|
|
* @cmd: SCSI command being translated
|
|
|
* @num: Maximum number of entries (nominally 64).
|
|
|
* @sector: Starting sector
|
|
|
- * @count: Total Range of request
|
|
|
+ * @count: Total Range of request in logical sectors
|
|
|
*
|
|
|
* Rewrite the WRITE SAME descriptor to be a DSM TRIM little-endian formatted
|
|
|
* descriptor.
|
|
@@ -3330,6 +3328,45 @@ static unsigned int ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 num,
|
|
|
return used_bytes;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * ata_format_dsm_trim_descr() - SATL Write Same to ATA SCT Write Same
|
|
|
+ * @cmd: SCSI command being translated
|
|
|
+ * @lba: Starting sector
|
|
|
+ * @num: Number of logical sectors to be zero'd.
|
|
|
+ *
|
|
|
+ * Rewrite the WRITE SAME descriptor to be an SCT Write Same formatted
|
|
|
+ * descriptor.
|
|
|
+ * NOTE: Writes a pattern (0's) in the foreground.
|
|
|
+ * Large write-same requents can timeout.
|
|
|
+ */
|
|
|
+static void ata_format_sct_write_same(struct scsi_cmnd *cmd, u64 lba, u64 num)
|
|
|
+{
|
|
|
+ u16 *sctpg;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
|
|
|
+ sctpg = ((void *)ata_scsi_rbuf);
|
|
|
+
|
|
|
+ put_unaligned_le16(0x0002, &sctpg[0]); /* SCT_ACT_WRITE_SAME */
|
|
|
+ put_unaligned_le16(0x0101, &sctpg[1]); /* WRITE PTRN FG */
|
|
|
+ put_unaligned_le64(lba, &sctpg[2]);
|
|
|
+ put_unaligned_le64(num, &sctpg[6]);
|
|
|
+ put_unaligned_le32(0u, &sctpg[10]);
|
|
|
+
|
|
|
+ sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), sctpg, 512);
|
|
|
+ spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ata_scsi_write_same_xlat() - SATL Write Same to ATA SCT Write Same
|
|
|
+ * @qc: Command to be translated
|
|
|
+ *
|
|
|
+ * Translate a SCSI WRITE SAME command to be either a DSM TRIM command or
|
|
|
+ * an SCT Write Same command.
|
|
|
+ * Based on WRITE SAME has the UNMAP flag
|
|
|
+ * When set translate to DSM TRIM
|
|
|
+ * When clear translate to SCT Write Same
|
|
|
+ */
|
|
|
static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
|
|
|
{
|
|
|
struct ata_taskfile *tf = &qc->tf;
|
|
@@ -3342,6 +3379,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
|
|
|
u32 size;
|
|
|
u16 fp;
|
|
|
u8 bp = 0xff;
|
|
|
+ u8 unmap = cdb[1] & 0x8;
|
|
|
|
|
|
/* we may not issue DMA commands if no DMA mode is set */
|
|
|
if (unlikely(!dev->dma_mode))
|
|
@@ -3353,11 +3391,26 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
|
|
|
}
|
|
|
scsi_16_lba_len(cdb, &block, &n_block);
|
|
|
|
|
|
- /* for now we only support WRITE SAME with the unmap bit set */
|
|
|
- if (unlikely(!(cdb[1] & 0x8))) {
|
|
|
- fp = 1;
|
|
|
- bp = 3;
|
|
|
- goto invalid_fld;
|
|
|
+ if (unmap) {
|
|
|
+ /* If trim is not enabled the cmd is invalid. */
|
|
|
+ if ((dev->horkage & ATA_HORKAGE_NOTRIM) ||
|
|
|
+ !ata_id_has_trim(dev->id)) {
|
|
|
+ fp = 1;
|
|
|
+ bp = 3;
|
|
|
+ goto invalid_fld;
|
|
|
+ }
|
|
|
+ /* If the request is too large the cmd is invalid */
|
|
|
+ if (n_block > 0xffff * trmax) {
|
|
|
+ fp = 2;
|
|
|
+ goto invalid_fld;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* If write same is not available the cmd is invalid */
|
|
|
+ if (!ata_id_sct_write_same(dev->id)) {
|
|
|
+ fp = 1;
|
|
|
+ bp = 3;
|
|
|
+ goto invalid_fld;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -3367,30 +3420,42 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
|
|
|
if (!scsi_sg_count(scmd))
|
|
|
goto invalid_param_len;
|
|
|
|
|
|
- if (n_block <= 0xffff * trmax) {
|
|
|
+ if (unmap) {
|
|
|
size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block);
|
|
|
+ if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) {
|
|
|
+ /* Newer devices support queued TRIM commands */
|
|
|
+ tf->protocol = ATA_PROT_NCQ;
|
|
|
+ tf->command = ATA_CMD_FPDMA_SEND;
|
|
|
+ tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f;
|
|
|
+ tf->nsect = qc->tag << 3;
|
|
|
+ tf->hob_feature = (size / 512) >> 8;
|
|
|
+ tf->feature = size / 512;
|
|
|
+
|
|
|
+ tf->auxiliary = 1;
|
|
|
+ } else {
|
|
|
+ tf->protocol = ATA_PROT_DMA;
|
|
|
+ tf->hob_feature = 0;
|
|
|
+ tf->feature = ATA_DSM_TRIM;
|
|
|
+ tf->hob_nsect = (size / 512) >> 8;
|
|
|
+ tf->nsect = size / 512;
|
|
|
+ tf->command = ATA_CMD_DSM;
|
|
|
+ }
|
|
|
} else {
|
|
|
- fp = 2;
|
|
|
- goto invalid_fld;
|
|
|
- }
|
|
|
-
|
|
|
- if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) {
|
|
|
- /* Newer devices support queued TRIM commands */
|
|
|
- tf->protocol = ATA_PROT_NCQ;
|
|
|
- tf->command = ATA_CMD_FPDMA_SEND;
|
|
|
- tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f;
|
|
|
- tf->nsect = qc->tag << 3;
|
|
|
- tf->hob_feature = (size / 512) >> 8;
|
|
|
- tf->feature = size / 512;
|
|
|
+ ata_format_sct_write_same(scmd, block, n_block);
|
|
|
|
|
|
- tf->auxiliary = 1;
|
|
|
- } else {
|
|
|
- tf->protocol = ATA_PROT_DMA;
|
|
|
tf->hob_feature = 0;
|
|
|
- tf->feature = ATA_DSM_TRIM;
|
|
|
- tf->hob_nsect = (size / 512) >> 8;
|
|
|
- tf->nsect = size / 512;
|
|
|
- tf->command = ATA_CMD_DSM;
|
|
|
+ tf->feature = 0;
|
|
|
+ tf->hob_nsect = 0;
|
|
|
+ tf->nsect = 1;
|
|
|
+ tf->lbah = 0;
|
|
|
+ tf->lbam = 0;
|
|
|
+ tf->lbal = ATA_CMD_STANDBYNOW1;
|
|
|
+ tf->hob_lbah = 0;
|
|
|
+ tf->hob_lbam = 0;
|
|
|
+ tf->hob_lbal = 0;
|
|
|
+ tf->device = ATA_CMD_STANDBYNOW1;
|
|
|
+ tf->protocol = ATA_PROT_DMA;
|
|
|
+ tf->command = ATA_CMD_WRITE_LOG_DMA_EXT;
|
|
|
}
|
|
|
|
|
|
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 |
|
|
@@ -3413,6 +3478,76 @@ invalid_opcode:
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN
|
|
|
+ * @args: device MAINTENANCE_IN data / 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_scsi_args *args, u8 *rbuf)
|
|
|
+{
|
|
|
+ struct ata_device *dev = args->dev;
|
|
|
+ u8 *cdb = args->cmd->cmnd;
|
|
|
+ u8 supported = 0;
|
|
|
+ unsigned int err = 0;
|
|
|
+
|
|
|
+ if (cdb[2] != 1) {
|
|
|
+ ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
|
|
|
+ err = 2;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ switch (cdb[3]) {
|
|
|
+ case INQUIRY:
|
|
|
+ case MODE_SENSE:
|
|
|
+ case MODE_SENSE_10:
|
|
|
+ case READ_CAPACITY:
|
|
|
+ case SERVICE_ACTION_IN_16:
|
|
|
+ case REPORT_LUNS:
|
|
|
+ case REQUEST_SENSE:
|
|
|
+ case SYNCHRONIZE_CACHE:
|
|
|
+ case REZERO_UNIT:
|
|
|
+ case SEEK_6:
|
|
|
+ case SEEK_10:
|
|
|
+ case TEST_UNIT_READY:
|
|
|
+ case SEND_DIAGNOSTIC:
|
|
|
+ case MAINTENANCE_IN:
|
|
|
+ case READ_6:
|
|
|
+ case READ_10:
|
|
|
+ case READ_16:
|
|
|
+ case WRITE_6:
|
|
|
+ case WRITE_10:
|
|
|
+ case WRITE_16:
|
|
|
+ case ATA_12:
|
|
|
+ case ATA_16:
|
|
|
+ case VERIFY:
|
|
|
+ case VERIFY_16:
|
|
|
+ case MODE_SELECT:
|
|
|
+ case MODE_SELECT_10:
|
|
|
+ case START_STOP:
|
|
|
+ supported = 3;
|
|
|
+ break;
|
|
|
+ case WRITE_SAME_16:
|
|
|
+ if (ata_id_sct_write_same(dev->id))
|
|
|
+ supported = 3;
|
|
|
+ break;
|
|
|
+ case ZBC_IN:
|
|
|
+ case ZBC_OUT:
|
|
|
+ if (ata_id_zoned_cap(dev->id) ||
|
|
|
+ dev->class == ATA_DEV_ZAC)
|
|
|
+ supported = 3;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+out:
|
|
|
+ rbuf[1] = supported; /* supported */
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ata_scsi_report_zones_complete - convert ATA output
|
|
|
* @qc: command structure returning the data
|
|
@@ -4193,6 +4328,13 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
|
|
|
ata_scsi_invalid_field(dev, cmd, 1);
|
|
|
break;
|
|
|
|
|
|
+ case MAINTENANCE_IN:
|
|
|
+ if (scsicmd[1] == MI_REPORT_SUPPORTED_OPERATION_CODES)
|
|
|
+ ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in);
|
|
|
+ else
|
|
|
+ ata_scsi_invalid_field(dev, cmd, 1);
|
|
|
+ break;
|
|
|
+
|
|
|
/* all other commands */
|
|
|
default:
|
|
|
ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0);
|
|
@@ -4225,7 +4367,6 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
|
|
|
shost->max_lun = 1;
|
|
|
shost->max_channel = 1;
|
|
|
shost->max_cmd_len = 16;
|
|
|
- shost->no_write_same = 1;
|
|
|
|
|
|
/* Schedule policy is determined by ->qc_defer()
|
|
|
* callback and it needs to see every deferred qc.
|