Skip to content

Commit

Permalink
scsi: ata: libata: Set read/write commands CDL index
Browse files Browse the repository at this point in the history
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 3 bits of the feature field.  For queued NCQ
read/write commands, the index is set as the lower 3 bits of the auxiliary
field.

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 <dlemoal@kernel.org>
Reviewed-by: Igor Pylypiv <ipylypiv@google.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Co-developed-by: Niklas Cassel <niklas.cassel@wdc.com>
Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Link: https://lore.kernel.org/r/20230511011356.227789-19-nks@flawful.org
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
Damien Le Moal authored and Martin K. Petersen committed May 22, 2023
1 parent df60f9c commit eafe804
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 5 deletions.
32 changes: 29 additions & 3 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down
16 changes: 15 additions & 1 deletion drivers/ata/libata-scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand All @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion drivers/ata/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit eafe804

Please sign in to comment.