[PATCH 4/7] libata: simplify the trim implementation
From: Christoph Hellwig <hch@lst.de>
Date: 2017-03-20 20:43:16
Also in:
linux-ide, linux-scsi
Subsystem:
libata subsystem (serial and parallel ata drivers), the rest · Maintainers:
Damien Le Moal, Niklas Cassel, Linus Torvalds
Don't try to fake up basic SCSI logical block provisioning and WRITE SAME support, but offer support for the Linux Vendor Specific TRIM command instead. This simplifies the implementation a lot, and avoids rewriting the data out buffer in the I/O path. Note that this new command is only offered to the block layer and will fail for pass through commands. While this is theoretically a regression in the functionality offered through SG_IO the previous support was buggy and corrupted user memory by rewriting the data out buffer in place. Last but not least this removes the global ata_scsi_rbuf_lock from the trim path. Signed-off-by: Christoph Hellwig <hch@lst.de> --- drivers/ata/libata-scsi.c | 179 ++++++++-------------------------------------- 1 file changed, 28 insertions(+), 151 deletions(-)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index b93d7e33789a..965b9e7dbb7d 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c@@ -1322,6 +1322,16 @@ static int ata_scsi_dev_config(struct scsi_device *sdev, blk_queue_flush_queueable(q, false); + if (ata_id_has_trim(dev->id) && + !(dev->horkage & ATA_HORKAGE_NOTRIM)) { + sdev->ata_trim = 1; + if (ata_id_has_zero_after_trim(dev->id) && + (dev->horkage & ATA_HORKAGE_ZERO_AFTER_TRIM)) { + ata_dev_info(dev, "Enabling discard_zeroes_data\n"); + sdev->ata_trim_zeroes_data = 1; + } + } + dev->sdev = sdev; return 0; }
@@ -2383,21 +2393,6 @@ static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf) */ min_io_sectors = 1 << ata_id_log2_per_physical_sector(args->id); put_unaligned_be16(min_io_sectors, &rbuf[6]); - - /* - * Optimal unmap granularity. - * - * The ATA spec doesn't even know about a granularity or alignment - * for the TRIM command. We can leave away most of the unmap related - * VPD page entries, but we have specifify a granularity to signal - * that we support some form of unmap - in thise case via WRITE SAME - * with the unmap bit set. - */ - if (ata_id_has_trim(args->id)) { - put_unaligned_be64(65535 * ATA_MAX_TRIM_RNUM, &rbuf[36]); - put_unaligned_be32(1, &rbuf[28]); - } - return 0; }
@@ -2746,16 +2741,6 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf) rbuf[14] = (lowest_aligned >> 8) & 0x3f; rbuf[15] = lowest_aligned; - if (ata_id_has_trim(args->id) && - !(dev->horkage & ATA_HORKAGE_NOTRIM)) { - rbuf[14] |= 0x80; /* LBPME */ - - if (ata_id_has_zero_after_trim(args->id) && - dev->horkage & ATA_HORKAGE_ZERO_AFTER_TRIM) { - ata_dev_info(dev, "Enabling discard_zeroes_data\n"); - rbuf[14] |= 0x40; /* LBPRZ */ - } - } if (ata_id_zoned_cap(args->id) || args->dev->class == ATA_DEV_ZAC) rbuf[12] = (1 << 4); /* RC_BASIS */
@@ -3339,141 +3324,45 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) } /** - * ata_format_dsm_trim_descr() - SATL Write Same to DSM Trim - * @cmd: SCSI command being translated - * @trmax: Maximum number of entries that will fit in sector_size bytes. - * @sector: Starting sector - * @count: Total Range of request in logical sectors - * - * Rewrite the WRITE SAME descriptor to be a DSM TRIM little-endian formatted - * descriptor. - * - * Upto 64 entries of the format: - * 63:48 Range Length - * 47:0 LBA - * - * Range Length of 0 is ignored. - * LBA's should be sorted order and not overlap. - * - * NOTE: this is the same format as ADD LBA(S) TO NV CACHE PINNED SET - * - * Return: Number of bytes copied into sglist. - */ -static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax, - u64 sector, u32 count) -{ - struct scsi_device *sdp = cmd->device; - size_t len = sdp->sector_size; - size_t r; - __le64 *buf; - u32 i = 0; - unsigned long flags; - - WARN_ON(len > ATA_SCSI_RBUF_SIZE); - - if (len > ATA_SCSI_RBUF_SIZE) - len = ATA_SCSI_RBUF_SIZE; - - spin_lock_irqsave(&ata_scsi_rbuf_lock, flags); - buf = ((void *)ata_scsi_rbuf); - memset(buf, 0, len); - while (i < trmax) { - u64 entry = sector | - ((u64)(count > 0xffff ? 0xffff : count) << 48); - buf[i++] = __cpu_to_le64(entry); - if (count <= 0xffff) - break; - count -= 0xffff; - sector += 0xffff; - } - r = sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buf, len); - spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags); - - return r; -} - -/** - * ata_scsi_write_same_xlat() - SATL Write Same to ATA SCT Write Same + * ata_scsi_trim_xlat() - Handle the vendor specific TRIM command. * @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 + * Setup a DSM TRIM command (or it's queued variant) after sd already + * prepared the payload for us. */ -static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) +static unsigned int ata_scsi_trim_xlat(struct ata_queued_cmd *qc) { struct ata_taskfile *tf = &qc->tf; struct scsi_cmnd *scmd = qc->scsicmd; - struct scsi_device *sdp = scmd->device; - size_t len = sdp->sector_size; struct ata_device *dev = qc->dev; - const u8 *cdb = scmd->cmnd; - u64 block; - u32 n_block; - const u32 trmax = len >> 3; - 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)) - goto invalid_opcode; - if (unlikely(scmd->cmd_len < 16)) { - fp = 15; - goto invalid_fld; + if (unlikely(!dev->dma_mode)) { + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0); + return 1; } - scsi_16_lba_len(cdb, &block, &n_block); - if (!unmap || - (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; + /* We only allow sending this command through the block layer */ + if (unlikely(req_op(scmd->request) != REQ_OP_DISCARD)) { + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0); + return 1; } - /* - * WRITE SAME always has a sector sized buffer as payload, this - * should never be a multiple entry S/G list. - */ - if (!scsi_sg_count(scmd)) - goto invalid_param_len; - - /* - * size must match sector size in bytes - * For DATA SET MANAGEMENT TRIM in ACS-2 nsect (aka count) - * is defined as number of 512 byte blocks to be transferred. - */ - - size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); - if (size != len) - goto invalid_param_len; - 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->hob_feature = (scmd->device->sector_size / 512) >> 8; + tf->feature = scmd->device->sector_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->hob_nsect = (scmd->device->sector_size / 512) >> 8; + tf->nsect = scmd->device->sector_size / 512; tf->command = ATA_CMD_DSM; }
@@ -3483,18 +3372,6 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) ata_qc_set_pc_nbytes(qc); return 0; - -invalid_fld: - ata_scsi_set_invalid_field(dev, scmd, fp, bp); - return 1; -invalid_param_len: - /* "Parameter list length error" */ - ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0); - return 1; -invalid_opcode: - /* "Invalid command operation code" */ - ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0); - return 1; } /**
@@ -4087,9 +3964,6 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) case WRITE_16: return ata_scsi_rw_xlat; - case WRITE_SAME_16: - return ata_scsi_write_same_xlat; - case SYNCHRONIZE_CACHE: if (ata_try_flush_cache(dev)) return ata_scsi_flush_xlat;
@@ -4116,6 +3990,9 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) case START_STOP: return ata_scsi_start_stop_xlat; + + case LINUX_VS_TRIM: + return ata_scsi_trim_xlat; } return NULL;
--
2.11.0