Message ID | 20230404182428.715140-19-nks@flawful.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add Command Duration Limits support | expand |
On Tue, Apr 04, 2023 at 08:24:23PM +0200, Niklas Cassel wrote: > From: Damien Le Moal <damien.lemoal@opensource.wdc.com> > > For devices supporting the command duration limits feature, translate > the dld field of read and write operation to set the command duration > limit index field of the command task file when the duration limit > feature is enabled. > > The function ata_set_tf_cdl() is introduced to do this. For unqueued > (non NCQ) read and write operations, this function sets the command > duration limit index set as the lower 2 bits of the feature field. > For queued NCQ read/write commands, the index is set as the lower > 2 bits of the auxiliary field. CDL index is lower 3 bits, not 2 bits. > > The flag ATA_QCFLAG_HAS_CDL is introduced to indicate that a command > taskfile has a non zero cdl field. > > Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com> > Co-developed-by: Niklas Cassel <niklas.cassel@wdc.com> > Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com> > --- > drivers/ata/libata-core.c | 32 +++++++++++++++++++++++++++++--- > drivers/ata/libata-scsi.c | 16 +++++++++++++++- > drivers/ata/libata.h | 2 +- > include/linux/libata.h | 1 + > 4 files changed, 46 insertions(+), 5 deletions(-) Reviewed-by: Igor Pylypiv <ipylypiv@google.com> Thanks, Igor > > diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c > index 62e100fa90e2..c68e7b684a87 100644 > --- a/drivers/ata/libata-core.c > +++ b/drivers/ata/libata-core.c > @@ -665,12 +665,29 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) > return block; > } > > +/* > + * Set a taskfile command duration limit index. > + */ > +static inline void ata_set_tf_cdl(struct ata_queued_cmd *qc, int cdl) > +{ > + struct ata_taskfile *tf = &qc->tf; > + > + if (tf->protocol == ATA_PROT_NCQ) > + tf->auxiliary |= cdl; > + else > + tf->feature |= cdl; > + > + /* Mark this command as having a CDL */ > + qc->flags |= ATA_QCFLAG_HAS_CDL; > +} > + > /** > * ata_build_rw_tf - Build ATA taskfile for given read/write request > * @qc: Metadata associated with the taskfile to build > * @block: Block address > * @n_block: Number of blocks > * @tf_flags: RW/FUA etc... > + * @cdl: Command duration limit index > * @class: IO priority class > * > * LOCKING: > @@ -685,7 +702,7 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) > * -EINVAL if the request is invalid. > */ > int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, > - unsigned int tf_flags, int class) > + unsigned int tf_flags, int cdl, int class) > { > struct ata_taskfile *tf = &qc->tf; > struct ata_device *dev = qc->dev; > @@ -724,11 +741,20 @@ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, > if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED && > class == IOPRIO_CLASS_RT) > tf->hob_nsect |= ATA_PRIO_HIGH << ATA_SHIFT_PRIO; > + > + if ((dev->flags & ATA_DFLAG_CDL_ENABLED) && cdl) > + ata_set_tf_cdl(qc, cdl); > + > } else if (dev->flags & ATA_DFLAG_LBA) { > tf->flags |= ATA_TFLAG_LBA; > > - /* We need LBA48 for FUA writes */ > - if (!(tf->flags & ATA_TFLAG_FUA) && lba_28_ok(block, n_block)) { > + if ((dev->flags & ATA_DFLAG_CDL_ENABLED) && cdl) > + ata_set_tf_cdl(qc, cdl); > + > + /* Both FUA writes and a CDL index require 48-bit commands */ > + if (!(tf->flags & ATA_TFLAG_FUA) && > + !(qc->flags & ATA_QCFLAG_HAS_CDL) && > + lba_28_ok(block, n_block)) { > /* use LBA28 */ > tf->device |= (block >> 24) & 0xf; > } else if (lba_48_ok(block, n_block)) { > diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c > index 8dde1cede5ca..05bde27947a2 100644 > --- a/drivers/ata/libata-scsi.c > +++ b/drivers/ata/libata-scsi.c > @@ -1380,6 +1380,18 @@ static inline void scsi_16_lba_len(const u8 *cdb, u64 *plba, u32 *plen) > *plen = get_unaligned_be32(&cdb[10]); > } > > +/** > + * scsi_dld - Get duration limit descriptor index > + * @cdb: SCSI command to translate > + * > + * Returns the dld bits indicating the index of a command duration limit > + * descriptor. > + */ > +static inline int scsi_dld(const u8 *cdb) > +{ > + return ((cdb[1] & 0x01) << 2) | ((cdb[14] >> 6) & 0x03); > +} > + > /** > * ata_scsi_verify_xlat - Translate SCSI VERIFY command into an ATA one > * @qc: Storage for translated ATA taskfile > @@ -1548,6 +1560,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) > struct request *rq = scsi_cmd_to_rq(scmd); > int class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq)); > unsigned int tf_flags = 0; > + int dld = 0; > u64 block; > u32 n_block; > int rc; > @@ -1598,6 +1611,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) > goto invalid_fld; > } > scsi_16_lba_len(cdb, &block, &n_block); > + dld = scsi_dld(cdb); > if (cdb[1] & (1 << 3)) > tf_flags |= ATA_TFLAG_FUA; > if (!ata_check_nblocks(scmd, n_block)) > @@ -1622,7 +1636,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) > qc->flags |= ATA_QCFLAG_IO; > qc->nbytes = n_block * scmd->device->sector_size; > > - rc = ata_build_rw_tf(qc, block, n_block, tf_flags, class); > + rc = ata_build_rw_tf(qc, block, n_block, tf_flags, dld, class); > if (likely(rc == 0)) > return 0; > > diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h > index 2cd6124a01e8..73dd2ebc277c 100644 > --- a/drivers/ata/libata.h > +++ b/drivers/ata/libata.h > @@ -45,7 +45,7 @@ static inline void ata_force_cbl(struct ata_port *ap) { } > extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); > extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf); > extern int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, > - unsigned int tf_flags, int class); > + unsigned int tf_flags, int dld, int class); > extern u64 ata_tf_read_block(const struct ata_taskfile *tf, > struct ata_device *dev); > extern unsigned ata_exec_internal(struct ata_device *dev, > diff --git a/include/linux/libata.h b/include/linux/libata.h > index d7fe735e6322..ab8b62036c12 100644 > --- a/include/linux/libata.h > +++ b/include/linux/libata.h > @@ -209,6 +209,7 @@ enum { > ATA_QCFLAG_CLEAR_EXCL = (1 << 5), /* clear excl_link on completion */ > ATA_QCFLAG_QUIET = (1 << 6), /* don't report device error */ > ATA_QCFLAG_RETRY = (1 << 7), /* retry after failure */ > + ATA_QCFLAG_HAS_CDL = (1 << 8), /* qc has CDL a descriptor set */ > > ATA_QCFLAG_EH = (1 << 16), /* cmd aborted and owned by EH */ > ATA_QCFLAG_SENSE_VALID = (1 << 17), /* sense data valid */ > -- > 2.39.2 >
On 4/6/23 03:26, Igor Pylypiv wrote: > On Tue, Apr 04, 2023 at 08:24:23PM +0200, Niklas Cassel wrote: >> From: Damien Le Moal <damien.lemoal@opensource.wdc.com> >> >> For devices supporting the command duration limits feature, translate >> the dld field of read and write operation to set the command duration >> limit index field of the command task file when the duration limit >> feature is enabled. >> >> The function ata_set_tf_cdl() is introduced to do this. For unqueued >> (non NCQ) read and write operations, this function sets the command >> duration limit index set as the lower 2 bits of the feature field. >> For queued NCQ read/write commands, the index is set as the lower >> 2 bits of the auxiliary field. > > CDL index is lower 3 bits, not 2 bits. Yes, typo. We will correct that. > >> >> The flag ATA_QCFLAG_HAS_CDL is introduced to indicate that a command >> taskfile has a non zero cdl field. >> >> Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com> >> Co-developed-by: Niklas Cassel <niklas.cassel@wdc.com> >> Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com> >> --- >> drivers/ata/libata-core.c | 32 +++++++++++++++++++++++++++++--- >> drivers/ata/libata-scsi.c | 16 +++++++++++++++- >> drivers/ata/libata.h | 2 +- >> include/linux/libata.h | 1 + >> 4 files changed, 46 insertions(+), 5 deletions(-) > Reviewed-by: Igor Pylypiv <ipylypiv@google.com> Thanks.
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 62e100fa90e2..c68e7b684a87 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -665,12 +665,29 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) return block; } +/* + * Set a taskfile command duration limit index. + */ +static inline void ata_set_tf_cdl(struct ata_queued_cmd *qc, int cdl) +{ + struct ata_taskfile *tf = &qc->tf; + + if (tf->protocol == ATA_PROT_NCQ) + tf->auxiliary |= cdl; + else + tf->feature |= cdl; + + /* Mark this command as having a CDL */ + qc->flags |= ATA_QCFLAG_HAS_CDL; +} + /** * ata_build_rw_tf - Build ATA taskfile for given read/write request * @qc: Metadata associated with the taskfile to build * @block: Block address * @n_block: Number of blocks * @tf_flags: RW/FUA etc... + * @cdl: Command duration limit index * @class: IO priority class * * LOCKING: @@ -685,7 +702,7 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) * -EINVAL if the request is invalid. */ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, - unsigned int tf_flags, int class) + unsigned int tf_flags, int cdl, int class) { struct ata_taskfile *tf = &qc->tf; struct ata_device *dev = qc->dev; @@ -724,11 +741,20 @@ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED && class == IOPRIO_CLASS_RT) tf->hob_nsect |= ATA_PRIO_HIGH << ATA_SHIFT_PRIO; + + if ((dev->flags & ATA_DFLAG_CDL_ENABLED) && cdl) + ata_set_tf_cdl(qc, cdl); + } else if (dev->flags & ATA_DFLAG_LBA) { tf->flags |= ATA_TFLAG_LBA; - /* We need LBA48 for FUA writes */ - if (!(tf->flags & ATA_TFLAG_FUA) && lba_28_ok(block, n_block)) { + if ((dev->flags & ATA_DFLAG_CDL_ENABLED) && cdl) + ata_set_tf_cdl(qc, cdl); + + /* Both FUA writes and a CDL index require 48-bit commands */ + if (!(tf->flags & ATA_TFLAG_FUA) && + !(qc->flags & ATA_QCFLAG_HAS_CDL) && + lba_28_ok(block, n_block)) { /* use LBA28 */ tf->device |= (block >> 24) & 0xf; } else if (lba_48_ok(block, n_block)) { diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 8dde1cede5ca..05bde27947a2 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1380,6 +1380,18 @@ static inline void scsi_16_lba_len(const u8 *cdb, u64 *plba, u32 *plen) *plen = get_unaligned_be32(&cdb[10]); } +/** + * scsi_dld - Get duration limit descriptor index + * @cdb: SCSI command to translate + * + * Returns the dld bits indicating the index of a command duration limit + * descriptor. + */ +static inline int scsi_dld(const u8 *cdb) +{ + return ((cdb[1] & 0x01) << 2) | ((cdb[14] >> 6) & 0x03); +} + /** * ata_scsi_verify_xlat - Translate SCSI VERIFY command into an ATA one * @qc: Storage for translated ATA taskfile @@ -1548,6 +1560,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) struct request *rq = scsi_cmd_to_rq(scmd); int class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq)); unsigned int tf_flags = 0; + int dld = 0; u64 block; u32 n_block; int rc; @@ -1598,6 +1611,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) goto invalid_fld; } scsi_16_lba_len(cdb, &block, &n_block); + dld = scsi_dld(cdb); if (cdb[1] & (1 << 3)) tf_flags |= ATA_TFLAG_FUA; if (!ata_check_nblocks(scmd, n_block)) @@ -1622,7 +1636,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) qc->flags |= ATA_QCFLAG_IO; qc->nbytes = n_block * scmd->device->sector_size; - rc = ata_build_rw_tf(qc, block, n_block, tf_flags, class); + rc = ata_build_rw_tf(qc, block, n_block, tf_flags, dld, class); if (likely(rc == 0)) return 0; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 2cd6124a01e8..73dd2ebc277c 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -45,7 +45,7 @@ static inline void ata_force_cbl(struct ata_port *ap) { } extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf); extern int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, - unsigned int tf_flags, int class); + unsigned int tf_flags, int dld, int class); extern u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev); extern unsigned ata_exec_internal(struct ata_device *dev, diff --git a/include/linux/libata.h b/include/linux/libata.h index d7fe735e6322..ab8b62036c12 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -209,6 +209,7 @@ enum { ATA_QCFLAG_CLEAR_EXCL = (1 << 5), /* clear excl_link on completion */ ATA_QCFLAG_QUIET = (1 << 6), /* don't report device error */ ATA_QCFLAG_RETRY = (1 << 7), /* retry after failure */ + ATA_QCFLAG_HAS_CDL = (1 << 8), /* qc has CDL a descriptor set */ ATA_QCFLAG_EH = (1 << 16), /* cmd aborted and owned by EH */ ATA_QCFLAG_SENSE_VALID = (1 << 17), /* sense data valid */