scsi: sd: Set read/write command CDL index
authorDamien Le Moal <dlemoal@kernel.org>
Thu, 11 May 2023 01:13:43 +0000 (03:13 +0200)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 22 May 2023 21:05:19 +0000 (17:05 -0400)
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: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Christoph Hellwig <hch@lst.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-11-nks@flawful.org
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/sd.c

index a76092663246ef0d0bcab89792308e291abb1316..3825e4d159fc828508862786e369c1d764562034 100644 (file)
@@ -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,