On Mon, Aug 22, 2016 at 2:20 PM, Tom Yan <tom.ty89@xxxxxxxxx> wrote: > On 22 August 2016 at 12:23, Shaun Tancheff <shaun@xxxxxxxxxxxx> wrote: >> SATA drives may support write same via SCT. This is useful >> for setting the drive contents to a specific pattern (0's). >> >> Translate a SCSI WRITE SAME 16 command to be either a DSM TRIM >> command or an SCT Write Same command. >> >> Based on the UNMAP flag: >> - When set translate to DSM TRIM >> - When not set translate to SCT Write Same >> >> Signed-off-by: Shaun Tancheff <shaun.tancheff@xxxxxxxxxxx> >> --- >> v6: >> - Change to use sg_copy_from_buffer as per Christoph Hellwig <hch@xxxxxx> >> v5: >> - Addressed review comments >> - Report support for ZBC only for zoned devices. >> - kmap page during rewrite >> - Fix unmap set to require trim or error, if not unmap then sct write >> same or error. >> v4: >> - Added partial MAINTENANCE_IN opcode simulation >> - Dropped all changes in drivers/scsi/* >> - Changed to honor the UNMAP flag -> TRIM, no UNMAP -> SCT. >> v3: >> - Demux UNMAP/TRIM from WRITE SAME >> v2: >> - Remove fugly ata hacking from sd.c >> >> drivers/ata/libata-scsi.c | 199 +++++++++++++++++++++++++++++++++++++++------- >> include/linux/ata.h | 43 ++++++++++ >> 2 files changed, 213 insertions(+), 29 deletions(-) >> >> diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c >> index 7990cb2..ebf1a04 100644 >> --- a/drivers/ata/libata-scsi.c >> +++ b/drivers/ata/libata-scsi.c >> @@ -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; >> + } > > This response should be generally applied to the Write Same (16) > translation, since it is required by SBC, > >> + } 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; >> + } > > therefore, you should add an n_block check here as well, if you are > going to advertise an Maximum Write Same Length even when the device > supports only SCT Write Same but not TRIM. Most likely you would want > to simply move the existing check one-level up (if the same limit is > advertised no matter TRIM is supported not or not). Why would we enforce upper level limits on something that doesn't have any? If the upper level, or SG_IO, chooses to set a timeout of 10 hours and wipe a whole disk it should be free to do so. >> } >> >> /* >> @@ -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 | >> @@ -3414,6 +3479,76 @@ invalid_opcode: >> } >> >> /** >> + * 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. >> diff --git a/include/linux/ata.h b/include/linux/ata.h >> index 45a1d71..fdb1803 100644 >> --- a/include/linux/ata.h >> +++ b/include/linux/ata.h >> @@ -105,6 +105,7 @@ enum { >> ATA_ID_CFA_KEY_MGMT = 162, >> ATA_ID_CFA_MODES = 163, >> ATA_ID_DATA_SET_MGMT = 169, >> + ATA_ID_SCT_CMD_XPORT = 206, >> ATA_ID_ROT_SPEED = 217, >> ATA_ID_PIO4 = (1 << 1), >> >> @@ -789,6 +790,48 @@ static inline bool ata_id_sense_reporting_enabled(const u16 *id) >> } >> >> /** >> + * >> + * Word: 206 - SCT Command Transport >> + * 15:12 - Vendor Specific >> + * 11:6 - Reserved >> + * 5 - SCT Command Transport Data Tables supported >> + * 4 - SCT Command Transport Features Control supported >> + * 3 - SCT Command Transport Error Recovery Control supported >> + * 2 - SCT Command Transport Write Same supported >> + * 1 - SCT Command Transport Long Sector Access supported >> + * 0 - SCT Command Transport supported >> + */ >> +static inline bool ata_id_sct_data_tables(const u16 *id) >> +{ >> + return id[ATA_ID_SCT_CMD_XPORT] & (1 << 5) ? true : false; >> +} >> + >> +static inline bool ata_id_sct_features_ctrl(const u16 *id) >> +{ >> + return id[ATA_ID_SCT_CMD_XPORT] & (1 << 4) ? true : false; >> +} >> + >> +static inline bool ata_id_sct_error_recovery_ctrl(const u16 *id) >> +{ >> + return id[ATA_ID_SCT_CMD_XPORT] & (1 << 3) ? true : false; >> +} >> + >> +static inline bool ata_id_sct_write_same(const u16 *id) >> +{ >> + return id[ATA_ID_SCT_CMD_XPORT] & (1 << 2) ? true : false; >> +} >> + >> +static inline bool ata_id_sct_long_sector_access(const u16 *id) >> +{ >> + return id[ATA_ID_SCT_CMD_XPORT] & (1 << 1) ? true : false; >> +} >> + >> +static inline bool ata_id_sct_supported(const u16 *id) >> +{ >> + return id[ATA_ID_SCT_CMD_XPORT] & (1 << 0) ? true : false; >> +} >> + >> +/** >> * ata_id_major_version - get ATA level of drive >> * @id: Identify data >> * >> -- >> 2.9.3 >> -- Shaun Tancheff -- To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html