scsi: ata: libata: Set read/write commands CDL index
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>
This commit is contained in:
committed by
Martin K. Petersen
parent
df60f9c645
commit
eafe804bda
@ -665,12 +665,29 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)
|
|||||||
return block;
|
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
|
* ata_build_rw_tf - Build ATA taskfile for given read/write request
|
||||||
* @qc: Metadata associated with the taskfile to build
|
* @qc: Metadata associated with the taskfile to build
|
||||||
* @block: Block address
|
* @block: Block address
|
||||||
* @n_block: Number of blocks
|
* @n_block: Number of blocks
|
||||||
* @tf_flags: RW/FUA etc...
|
* @tf_flags: RW/FUA etc...
|
||||||
|
* @cdl: Command duration limit index
|
||||||
* @class: IO priority class
|
* @class: IO priority class
|
||||||
*
|
*
|
||||||
* LOCKING:
|
* 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.
|
* -EINVAL if the request is invalid.
|
||||||
*/
|
*/
|
||||||
int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block,
|
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_taskfile *tf = &qc->tf;
|
||||||
struct ata_device *dev = qc->dev;
|
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 &&
|
if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED &&
|
||||||
class == IOPRIO_CLASS_RT)
|
class == IOPRIO_CLASS_RT)
|
||||||
tf->hob_nsect |= ATA_PRIO_HIGH << ATA_SHIFT_PRIO;
|
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) {
|
} else if (dev->flags & ATA_DFLAG_LBA) {
|
||||||
tf->flags |= ATA_TFLAG_LBA;
|
tf->flags |= ATA_TFLAG_LBA;
|
||||||
|
|
||||||
/* We need LBA48 for FUA writes */
|
if ((dev->flags & ATA_DFLAG_CDL_ENABLED) && cdl)
|
||||||
if (!(tf->flags & ATA_TFLAG_FUA) && lba_28_ok(block, n_block)) {
|
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 */
|
/* use LBA28 */
|
||||||
tf->device |= (block >> 24) & 0xf;
|
tf->device |= (block >> 24) & 0xf;
|
||||||
} else if (lba_48_ok(block, n_block)) {
|
} else if (lba_48_ok(block, n_block)) {
|
||||||
|
@ -1380,6 +1380,18 @@ static inline void scsi_16_lba_len(const u8 *cdb, u64 *plba, u32 *plen)
|
|||||||
*plen = get_unaligned_be32(&cdb[10]);
|
*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
|
* ata_scsi_verify_xlat - Translate SCSI VERIFY command into an ATA one
|
||||||
* @qc: Storage for translated ATA taskfile
|
* @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);
|
struct request *rq = scsi_cmd_to_rq(scmd);
|
||||||
int class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
|
int class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
|
||||||
unsigned int tf_flags = 0;
|
unsigned int tf_flags = 0;
|
||||||
|
int dld = 0;
|
||||||
u64 block;
|
u64 block;
|
||||||
u32 n_block;
|
u32 n_block;
|
||||||
int rc;
|
int rc;
|
||||||
@ -1598,6 +1611,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
|
|||||||
goto invalid_fld;
|
goto invalid_fld;
|
||||||
}
|
}
|
||||||
scsi_16_lba_len(cdb, &block, &n_block);
|
scsi_16_lba_len(cdb, &block, &n_block);
|
||||||
|
dld = scsi_dld(cdb);
|
||||||
if (cdb[1] & (1 << 3))
|
if (cdb[1] & (1 << 3))
|
||||||
tf_flags |= ATA_TFLAG_FUA;
|
tf_flags |= ATA_TFLAG_FUA;
|
||||||
if (!ata_check_nblocks(scmd, n_block))
|
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->flags |= ATA_QCFLAG_IO;
|
||||||
qc->nbytes = n_block * scmd->device->sector_size;
|
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))
|
if (likely(rc == 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -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_lba(const struct ata_taskfile *tf);
|
||||||
extern u64 ata_tf_to_lba48(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,
|
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,
|
extern u64 ata_tf_read_block(const struct ata_taskfile *tf,
|
||||||
struct ata_device *dev);
|
struct ata_device *dev);
|
||||||
extern unsigned ata_exec_internal(struct ata_device *dev,
|
extern unsigned ata_exec_internal(struct ata_device *dev,
|
||||||
|
@ -209,6 +209,7 @@ enum {
|
|||||||
ATA_QCFLAG_CLEAR_EXCL = (1 << 5), /* clear excl_link on completion */
|
ATA_QCFLAG_CLEAR_EXCL = (1 << 5), /* clear excl_link on completion */
|
||||||
ATA_QCFLAG_QUIET = (1 << 6), /* don't report device error */
|
ATA_QCFLAG_QUIET = (1 << 6), /* don't report device error */
|
||||||
ATA_QCFLAG_RETRY = (1 << 7), /* retry after failure */
|
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_EH = (1 << 16), /* cmd aborted and owned by EH */
|
||||||
ATA_QCFLAG_SENSE_VALID = (1 << 17), /* sense data valid */
|
ATA_QCFLAG_SENSE_VALID = (1 << 17), /* sense data valid */
|
||||||
|
Reference in New Issue
Block a user