Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Damien Le Moal and committed by
Martin K. Petersen
1b22cfb1 62488520

+105
+13
Documentation/ABI/testing/sysfs-block-device
··· 104 104 Description: 105 105 (RO) Indicates if the device supports the command duration 106 106 limits feature found in some ATA and SCSI devices. 107 + 108 + 109 + What: /sys/block/*/device/cdl_enable 110 + Date: May, 2023 111 + KernelVersion: v6.5 112 + Contact: linux-scsi@vger.kernel.org 113 + Description: 114 + (RW) For a device supporting the command duration limits 115 + feature, write to the file to turn on or off the feature. 116 + By default this feature is turned off. 117 + Writing "1" to this file enables the use of command duration 118 + limits for read and write commands in the kernel and turns on 119 + the feature on the device. Writing "0" disables the feature.
+62
drivers/scsi/scsi.c
··· 652 652 } 653 653 654 654 /** 655 + * scsi_cdl_enable - Enable or disable a SCSI device supports for Command 656 + * Duration Limits 657 + * @sdev: The target device 658 + * @enable: the target state 659 + */ 660 + int scsi_cdl_enable(struct scsi_device *sdev, bool enable) 661 + { 662 + struct scsi_mode_data data; 663 + struct scsi_sense_hdr sshdr; 664 + struct scsi_vpd *vpd; 665 + bool is_ata = false; 666 + char buf[64]; 667 + int ret; 668 + 669 + if (!sdev->cdl_supported) 670 + return -EOPNOTSUPP; 671 + 672 + rcu_read_lock(); 673 + vpd = rcu_dereference(sdev->vpd_pg89); 674 + if (vpd) 675 + is_ata = true; 676 + rcu_read_unlock(); 677 + 678 + /* 679 + * For ATA devices, CDL needs to be enabled with a SET FEATURES command. 680 + */ 681 + if (is_ata) { 682 + char *buf_data; 683 + int len; 684 + 685 + ret = scsi_mode_sense(sdev, 0x08, 0x0a, 0xf2, buf, sizeof(buf), 686 + 5 * HZ, 3, &data, NULL); 687 + if (ret) 688 + return -EINVAL; 689 + 690 + /* Enable CDL using the ATA feature page */ 691 + len = min_t(size_t, sizeof(buf), 692 + data.length - data.header_length - 693 + data.block_descriptor_length); 694 + buf_data = buf + data.header_length + 695 + data.block_descriptor_length; 696 + if (enable) 697 + buf_data[4] = 0x02; 698 + else 699 + buf_data[4] = 0; 700 + 701 + ret = scsi_mode_select(sdev, 1, 0, buf_data, len, 5 * HZ, 3, 702 + &data, &sshdr); 703 + if (ret) { 704 + if (scsi_sense_valid(&sshdr)) 705 + scsi_print_sense_hdr(sdev, 706 + dev_name(&sdev->sdev_gendev), &sshdr); 707 + return ret; 708 + } 709 + } 710 + 711 + sdev->cdl_enable = enable; 712 + 713 + return 0; 714 + } 715 + 716 + /** 655 717 * scsi_device_get - get an additional reference to a scsi_device 656 718 * @sdev: device to get a reference to 657 719 *
+28
drivers/scsi/scsi_sysfs.c
··· 1222 1222 sdev_show_queue_ramp_up_period, 1223 1223 sdev_store_queue_ramp_up_period); 1224 1224 1225 + static ssize_t sdev_show_cdl_enable(struct device *dev, 1226 + struct device_attribute *attr, char *buf) 1227 + { 1228 + struct scsi_device *sdev = to_scsi_device(dev); 1229 + 1230 + return sysfs_emit(buf, "%d\n", (int)sdev->cdl_enable); 1231 + } 1232 + 1233 + static ssize_t sdev_store_cdl_enable(struct device *dev, 1234 + struct device_attribute *attr, 1235 + const char *buf, size_t count) 1236 + { 1237 + int ret; 1238 + bool v; 1239 + 1240 + if (kstrtobool(buf, &v)) 1241 + return -EINVAL; 1242 + 1243 + ret = scsi_cdl_enable(to_scsi_device(dev), v); 1244 + if (ret) 1245 + return ret; 1246 + 1247 + return count; 1248 + } 1249 + static DEVICE_ATTR(cdl_enable, S_IRUGO | S_IWUSR, 1250 + sdev_show_cdl_enable, sdev_store_cdl_enable); 1251 + 1225 1252 static umode_t scsi_sdev_attr_is_visible(struct kobject *kobj, 1226 1253 struct attribute *attr, int i) 1227 1254 { ··· 1329 1302 #endif 1330 1303 &dev_attr_queue_ramp_up_period.attr, 1331 1304 &dev_attr_cdl_supported.attr, 1305 + &dev_attr_cdl_enable.attr, 1332 1306 REF_EVT(media_change), 1333 1307 REF_EVT(inquiry_change_reported), 1334 1308 REF_EVT(capacity_change_reported),
+2
include/scsi/scsi_device.h
··· 219 219 unsigned no_vpd_size:1; /* No VPD size reported in header */ 220 220 221 221 unsigned cdl_supported:1; /* Command duration limits supported */ 222 + unsigned cdl_enable:1; /* Enable/disable Command duration limits */ 222 223 223 224 unsigned int queue_stopped; /* request queue is quiesced */ 224 225 bool offline_already; /* Device offline message logged */ ··· 368 367 extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh); 369 368 void scsi_attach_vpd(struct scsi_device *sdev); 370 369 void scsi_cdl_check(struct scsi_device *sdev); 370 + int scsi_cdl_enable(struct scsi_device *sdev, bool enable); 371 371 372 372 extern struct scsi_device *scsi_device_from_queue(struct request_queue *q); 373 373 extern int __must_check scsi_device_get(struct scsi_device *);