This file does not exist if the HBA driver does not implement
support for the SATA NCQ priority feature, regardless of the
device support for this feature.
+
+
+What: /sys/block/*/device/cdl_supported
+Date: May, 2023
+KernelVersion: v6.5
+Contact: linux-scsi@vger.kernel.org
+Description:
+ (RO) Indicates if the device supports the command duration
+ limits feature found in some ATA and SCSI devices.
}
EXPORT_SYMBOL(scsi_report_opcode);
+#define SCSI_CDL_CHECK_BUF_LEN 64
+
+static bool scsi_cdl_check_cmd(struct scsi_device *sdev, u8 opcode, u16 sa,
+ unsigned char *buf)
+{
+ int ret;
+ u8 cdlp;
+
+ /* Check operation code */
+ ret = scsi_report_opcode(sdev, buf, SCSI_CDL_CHECK_BUF_LEN, opcode, sa);
+ if (ret <= 0)
+ return false;
+
+ if ((buf[1] & 0x03) != 0x03)
+ return false;
+
+ /* See SPC-6, one command format of REPORT SUPPORTED OPERATION CODES */
+ cdlp = (buf[1] & 0x18) >> 3;
+ if (buf[0] & 0x01) {
+ /* rwcdlp == 1 */
+ switch (cdlp) {
+ case 0x01:
+ /* T2A page */
+ return true;
+ case 0x02:
+ /* T2B page */
+ return true;
+ }
+ } else {
+ /* rwcdlp == 0 */
+ switch (cdlp) {
+ case 0x01:
+ /* A page */
+ return true;
+ case 0x02:
+ /* B page */
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * scsi_cdl_check - Check if a SCSI device supports Command Duration Limits
+ * @sdev: The device to check
+ */
+void scsi_cdl_check(struct scsi_device *sdev)
+{
+ bool cdl_supported;
+ unsigned char *buf;
+
+ buf = kmalloc(SCSI_CDL_CHECK_BUF_LEN, GFP_KERNEL);
+ if (!buf) {
+ sdev->cdl_supported = 0;
+ return;
+ }
+
+ /* Check support for READ_16, WRITE_16, READ_32 and WRITE_32 commands */
+ cdl_supported =
+ scsi_cdl_check_cmd(sdev, READ_16, 0, buf) ||
+ scsi_cdl_check_cmd(sdev, WRITE_16, 0, buf) ||
+ scsi_cdl_check_cmd(sdev, VARIABLE_LENGTH_CMD, READ_32, buf) ||
+ scsi_cdl_check_cmd(sdev, VARIABLE_LENGTH_CMD, WRITE_32, buf);
+ if (cdl_supported) {
+ /*
+ * We have CDL support: force the use of READ16/WRITE16.
+ * READ32 and WRITE32 will be used for devices that support
+ * the T10_PI_TYPE2_PROTECTION protection type.
+ */
+ sdev->use_16_for_rw = 1;
+ sdev->use_10_for_rw = 0;
+
+ sdev->cdl_supported = 1;
+ } else {
+ sdev->cdl_supported = 0;
+ }
+
+ kfree(buf);
+}
+
/**
* scsi_device_get - get an additional reference to a scsi_device
* @sdev: device to get a reference to
sdev_rd_attr (vendor, "%.8s\n");
sdev_rd_attr (model, "%.16s\n");
sdev_rd_attr (rev, "%.4s\n");
+sdev_rd_attr (cdl_supported, "%d\n");
static ssize_t
sdev_show_device_busy(struct device *dev, struct device_attribute *attr,
&dev_attr_preferred_path.attr,
#endif
&dev_attr_queue_ramp_up_period.attr,
+ &dev_attr_cdl_supported.attr,
REF_EVT(media_change),
REF_EVT(inquiry_change_reported),
REF_EVT(capacity_change_reported),
unsigned silence_suspend:1; /* Do not print runtime PM related messages */
unsigned no_vpd_size:1; /* No VPD size reported in header */
+ unsigned cdl_supported:1; /* Command duration limits supported */
+
unsigned int queue_stopped; /* request queue is quiesced */
bool offline_already; /* Device offline message logged */
extern void scsi_remove_device(struct scsi_device *);
extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh);
void scsi_attach_vpd(struct scsi_device *sdev);
+void scsi_cdl_check(struct scsi_device *sdev);
extern struct scsi_device *scsi_device_from_queue(struct request_queue *q);
extern int __must_check scsi_device_get(struct scsi_device *);