summaryrefslogtreecommitdiff
path: root/drivers/scsi/scsi.c
diff options
context:
space:
mode:
authorDamien Le Moal <dlemoal@kernel.org>2023-05-11 03:13:42 +0200
committerMartin K. Petersen <martin.petersen@oracle.com>2023-05-22 17:05:19 -0400
commit1b22cfb14142aba7742d307c4f8d7006f919308c (patch)
tree27f10d331c3e090a7e9903cd7c16663eaed9d4c7 /drivers/scsi/scsi.c
parent624885209f31eb9985bf51abe204ecbffe2fdeea (diff)
downloadlwn-1b22cfb14142aba7742d307c4f8d7006f919308c.tar.gz
lwn-1b22cfb14142aba7742d307c4f8d7006f919308c.zip
scsi: core: Allow enabling and disabling command duration limits
Add the sysfs scsi_device attribute cdl_enable to allow a user to enable or disable a device command duration limits feature. CDL is disabled by default. This feature must be explicitly enabled by a user by setting the cdl_enable attribute to 1. The new function scsi_cdl_enable() does not do anything beside setting the cdl_enable field of struct scsi_device in the case of a (real) SCSI device (e.g. a SAS HDD). For ATA devices, the command duration limits feature needs to be enabled/disabled using the ATA feature sub-page of the control mode page. To do so, the scsi_cdl_enable() function checks if this mode page is supported using scsi_mode_sense(). If it is, scsi_mode_select() is used to enable and disable CDL. Signed-off-by: Damien Le Moal <dlemoal@kernel.org> 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-10-nks@flawful.org Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/scsi.c')
-rw-r--r--drivers/scsi/scsi.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index c03814ce23ca..c4bf99a842f3 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -652,6 +652,68 @@ void scsi_cdl_check(struct scsi_device *sdev)
}
/**
+ * scsi_cdl_enable - Enable or disable a SCSI device supports for Command
+ * Duration Limits
+ * @sdev: The target device
+ * @enable: the target state
+ */
+int scsi_cdl_enable(struct scsi_device *sdev, bool enable)
+{
+ struct scsi_mode_data data;
+ struct scsi_sense_hdr sshdr;
+ struct scsi_vpd *vpd;
+ bool is_ata = false;
+ char buf[64];
+ int ret;
+
+ if (!sdev->cdl_supported)
+ return -EOPNOTSUPP;
+
+ rcu_read_lock();
+ vpd = rcu_dereference(sdev->vpd_pg89);
+ if (vpd)
+ is_ata = true;
+ rcu_read_unlock();
+
+ /*
+ * For ATA devices, CDL needs to be enabled with a SET FEATURES command.
+ */
+ if (is_ata) {
+ char *buf_data;
+ int len;
+
+ ret = scsi_mode_sense(sdev, 0x08, 0x0a, 0xf2, buf, sizeof(buf),
+ 5 * HZ, 3, &data, NULL);
+ if (ret)
+ return -EINVAL;
+
+ /* Enable CDL using the ATA feature page */
+ len = min_t(size_t, sizeof(buf),
+ data.length - data.header_length -
+ data.block_descriptor_length);
+ buf_data = buf + data.header_length +
+ data.block_descriptor_length;
+ if (enable)
+ buf_data[4] = 0x02;
+ else
+ buf_data[4] = 0;
+
+ ret = scsi_mode_select(sdev, 1, 0, buf_data, len, 5 * HZ, 3,
+ &data, &sshdr);
+ if (ret) {
+ if (scsi_sense_valid(&sshdr))
+ scsi_print_sense_hdr(sdev,
+ dev_name(&sdev->sdev_gendev), &sshdr);
+ return ret;
+ }
+ }
+
+ sdev->cdl_enable = enable;
+
+ return 0;
+}
+
+/**
* scsi_device_get - get an additional reference to a scsi_device
* @sdev: device to get a reference to
*