scsi: core: Detect support for command duration limits
JIRA: https://issues.redhat.com/browse/RHEL-33543 Upstream Status: From upstream linux mainline Conflicts: Merge differences, KABI changes Introduce the function scsi_cdl_check() to detect if a device supports command duration limits (CDL). Support for the READ 16, WRITE 16, READ 32 and WRITE 32 commands are checked using the function scsi_report_opcode() to probe the rwcdlp and cdlp bits as they indicate the mode page defining the command duration limits descriptors that apply to the command being tested. If any of these commands support CDL, the field cdl_supported of struct scsi_device is set to 1 to indicate that the device supports CDL. Support for CDL for a device is advertizes through sysfs using the new cdl_supported device attribute. This attribute value is 1 for a device supporting CDL and 0 otherwise. 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-9-nks@flawful.org Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> (cherry picked from commit 624885209f31eb9985bf51abe204ecbffe2fdeea) Signed-off-by: Ewan D. Milne <emilne@redhat.com>
This commit is contained in:
parent
2fee68b6a6
commit
0c72065b3d
|
@ -58,3 +58,12 @@ Description:
|
|||
(RW) Write to the file to turn on or off the SATA ncq (native
|
||||
command queueing) support. By default this feature is turned
|
||||
off.
|
||||
|
||||
|
||||
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.
|
||||
|
|
|
@ -588,6 +588,87 @@ int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
|
|||
}
|
||||
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
|
||||
|
|
|
@ -1101,6 +1101,8 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
|
|||
if (sdev->scsi_level >= SCSI_3)
|
||||
scsi_attach_vpd(sdev);
|
||||
|
||||
scsi_cdl_check(sdev);
|
||||
|
||||
sdev->max_queue_depth = sdev->queue_depth;
|
||||
WARN_ON_ONCE(sdev->max_queue_depth > sdev->budget_map.depth);
|
||||
sdev->sdev_bflags = *bflags;
|
||||
|
@ -1660,6 +1662,7 @@ int scsi_rescan_device_rh(struct device *dev)
|
|||
}
|
||||
|
||||
scsi_attach_vpd(sdev);
|
||||
scsi_cdl_check(sdev);
|
||||
|
||||
if (sdev->handler && sdev->handler->rescan)
|
||||
sdev->handler->rescan(sdev);
|
||||
|
|
|
@ -687,6 +687,7 @@ sdev_rd_attr (scsi_level, "%d\n");
|
|||
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,
|
||||
|
@ -1317,6 +1318,7 @@ static struct attribute *scsi_sdev_attrs[] = {
|
|||
&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),
|
||||
|
|
|
@ -216,6 +216,8 @@ struct scsi_device {
|
|||
RH_KABI_FILL_HOLE(unsigned use_16_for_sync:1) /* Use sync (16) over sync (10) */
|
||||
RH_KABI_FILL_HOLE(unsigned no_vpd_size:1) /* No VPD size reported in header */
|
||||
|
||||
RH_KABI_FILL_HOLE(unsigned cdl_supported:1) /* Command duration limits supported */
|
||||
|
||||
unsigned int queue_stopped; /* request queue is quiesced */
|
||||
bool offline_already; /* Device offline message logged */
|
||||
|
||||
|
@ -387,6 +389,7 @@ extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh);
|
|||
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 *);
|
||||
|
|
Loading…
Reference in New Issue