Commit e59e80cf authored by Damien Le Moal's avatar Damien Le Moal Committed by Martin K. Petersen
Browse files

scsi: sd: Set read/write command CDL index



Introduce the command duration limits helper function sd_cdl_dld() to set
the DLD bits of READ/WRITE 16 and READ/WRITE 32 commands to indicate to the
device the command duration limit descriptor to apply to the commands.

When command duration limits are enabled, sd_cdl_dld() obtains the index of
the descriptor to apply to the command using the hints field of the request
IO priority value (hints IOPRIO_HINT_DEV_DURATION_LIMIT_1 to
IOPRIO_HINT_DEV_DURATION_LIMIT_7).

If command duration limits is disabled (which is the default), the limit
index "0" is always used to indicate "no limit" for a command.

Signed-off-by: default avatarDamien Le Moal <dlemoal@kernel.org>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Co-developed-by: default avatarNiklas Cassel <niklas.cassel@wdc.com>
Signed-off-by: default avatarNiklas Cassel <niklas.cassel@wdc.com>
Link: https://lore.kernel.org/r/20230511011356.227789-11-nks@flawful.org


Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 1b22cfb1
Loading
Loading
Loading
Loading
+34 −6
Original line number Diff line number Diff line
@@ -1041,13 +1041,14 @@ static blk_status_t sd_setup_flush_cmnd(struct scsi_cmnd *cmd)

static blk_status_t sd_setup_rw32_cmnd(struct scsi_cmnd *cmd, bool write,
				       sector_t lba, unsigned int nr_blocks,
				       unsigned char flags)
				       unsigned char flags, unsigned int dld)
{
	cmd->cmd_len = SD_EXT_CDB_SIZE;
	cmd->cmnd[0]  = VARIABLE_LENGTH_CMD;
	cmd->cmnd[7]  = 0x18; /* Additional CDB len */
	cmd->cmnd[9]  = write ? WRITE_32 : READ_32;
	cmd->cmnd[10] = flags;
	cmd->cmnd[11] = dld & 0x07;
	put_unaligned_be64(lba, &cmd->cmnd[12]);
	put_unaligned_be32(lba, &cmd->cmnd[20]); /* Expected Indirect LBA */
	put_unaligned_be32(nr_blocks, &cmd->cmnd[28]);
@@ -1057,12 +1058,12 @@ static blk_status_t sd_setup_rw32_cmnd(struct scsi_cmnd *cmd, bool write,

static blk_status_t sd_setup_rw16_cmnd(struct scsi_cmnd *cmd, bool write,
				       sector_t lba, unsigned int nr_blocks,
				       unsigned char flags)
				       unsigned char flags, unsigned int dld)
{
	cmd->cmd_len  = 16;
	cmd->cmnd[0]  = write ? WRITE_16 : READ_16;
	cmd->cmnd[1]  = flags;
	cmd->cmnd[14] = 0;
	cmd->cmnd[1]  = flags | ((dld >> 2) & 0x01);
	cmd->cmnd[14] = (dld & 0x03) << 6;
	cmd->cmnd[15] = 0;
	put_unaligned_be64(lba, &cmd->cmnd[2]);
	put_unaligned_be32(nr_blocks, &cmd->cmnd[10]);
@@ -1114,6 +1115,31 @@ static blk_status_t sd_setup_rw6_cmnd(struct scsi_cmnd *cmd, bool write,
	return BLK_STS_OK;
}

/*
 * Check if a command has a duration limit set. If it does, and the target
 * device supports CDL and the feature is enabled, return the limit
 * descriptor index to use. Return 0 (no limit) otherwise.
 */
static int sd_cdl_dld(struct scsi_disk *sdkp, struct scsi_cmnd *scmd)
{
	struct scsi_device *sdp = sdkp->device;
	int hint;

	if (!sdp->cdl_supported || !sdp->cdl_enable)
		return 0;

	/*
	 * Use "no limit" if the request ioprio does not specify a duration
	 * limit hint.
	 */
	hint = IOPRIO_PRIO_HINT(req_get_ioprio(scsi_cmd_to_rq(scmd)));
	if (hint < IOPRIO_HINT_DEV_DURATION_LIMIT_1 ||
	    hint > IOPRIO_HINT_DEV_DURATION_LIMIT_7)
		return 0;

	return (hint - IOPRIO_HINT_DEV_DURATION_LIMIT_1) + 1;
}

static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd)
{
	struct request *rq = scsi_cmd_to_rq(cmd);
@@ -1125,6 +1151,7 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd)
	unsigned int mask = logical_to_sectors(sdp, 1) - 1;
	bool write = rq_data_dir(rq) == WRITE;
	unsigned char protect, fua;
	unsigned int dld;
	blk_status_t ret;
	unsigned int dif;
	bool dix;
@@ -1174,6 +1201,7 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd)
	fua = rq->cmd_flags & REQ_FUA ? 0x8 : 0;
	dix = scsi_prot_sg_count(cmd);
	dif = scsi_host_dif_capable(cmd->device->host, sdkp->protection_type);
	dld = sd_cdl_dld(sdkp, cmd);

	if (dif || dix)
		protect = sd_setup_protect_cmnd(cmd, dix, dif);
@@ -1182,10 +1210,10 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd)

	if (protect && sdkp->protection_type == T10_PI_TYPE2_PROTECTION) {
		ret = sd_setup_rw32_cmnd(cmd, write, lba, nr_blocks,
					 protect | fua);
					 protect | fua, dld);
	} else if (sdp->use_16_for_rw || (nr_blocks > 0xffff)) {
		ret = sd_setup_rw16_cmnd(cmd, write, lba, nr_blocks,
					 protect | fua);
					 protect | fua, dld);
	} else if ((nr_blocks > 0xff) || (lba > 0x1fffff) ||
		   sdp->use_10_for_rw || protect) {
		ret = sd_setup_rw10_cmnd(cmd, write, lba, nr_blocks,