diff mbox series

[v5,08/19] scsi: detect support for command duration limits

Message ID 20230404182428.715140-9-nks@flawful.org (mailing list archive)
State New, archived
Headers show
Series Add Command Duration Limits support | expand

Commit Message

Niklas Cassel April 4, 2023, 6:24 p.m. UTC
From: Damien Le Moal <damien.lemoal@opensource.wdc.com>

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 <damien.lemoal@opensource.wdc.com>
Co-developed-by: Niklas Cassel <niklas.cassel@wdc.com>
Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
---
 Documentation/ABI/testing/sysfs-block-device |  9 +++
 drivers/scsi/scsi.c                          | 81 ++++++++++++++++++++
 drivers/scsi/scsi_scan.c                     |  3 +
 drivers/scsi/scsi_sysfs.c                    |  2 +
 include/scsi/scsi_device.h                   |  3 +
 5 files changed, 98 insertions(+)

Comments

Hannes Reinecke April 4, 2023, 6:48 p.m. UTC | #1
On 4/4/23 20:24, Niklas Cassel wrote:
> From: Damien Le Moal <damien.lemoal@opensource.wdc.com>
> 
> 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 <damien.lemoal@opensource.wdc.com>
> Co-developed-by: Niklas Cassel <niklas.cassel@wdc.com>
> Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
> ---
>   Documentation/ABI/testing/sysfs-block-device |  9 +++
>   drivers/scsi/scsi.c                          | 81 ++++++++++++++++++++
>   drivers/scsi/scsi_scan.c                     |  3 +
>   drivers/scsi/scsi_sysfs.c                    |  2 +
>   include/scsi/scsi_device.h                   |  3 +
>   5 files changed, 98 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-block-device b/Documentation/ABI/testing/sysfs-block-device
> index 7ac7b19b2f72..ee3610a25845 100644
> --- a/Documentation/ABI/testing/sysfs-block-device
> +++ b/Documentation/ABI/testing/sysfs-block-device
> @@ -95,3 +95,12 @@ Description:
>   		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:		Mar, 2023
> +KernelVersion:	v6.4
> +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.
> diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
> index 62d9472e08e9..c03814ce23ca 100644
> --- a/drivers/scsi/scsi.c
> +++ b/drivers/scsi/scsi.c
> @@ -570,6 +570,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;
> +}
> +
Why do we need to check this when writing to sysfs? Shouldn't we detect 
this during startup / revalidate?

> +/**
> + * 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
> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
> index d217be323cc6..aa13feb17c62 100644
> --- a/drivers/scsi/scsi_scan.c
> +++ b/drivers/scsi/scsi_scan.c
> @@ -1087,6 +1087,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;
> @@ -1624,6 +1626,7 @@ void scsi_rescan_device(struct device *dev)
>   	device_lock(dev);
>   
>   	scsi_attach_vpd(sdev);
> +	scsi_cdl_check(sdev);
>   
>   	if (sdev->handler && sdev->handler->rescan)
>   		sdev->handler->rescan(sdev);
> diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
> index ee28f73af4d4..4994148e685b 100644
> --- a/drivers/scsi/scsi_sysfs.c
> +++ b/drivers/scsi/scsi_sysfs.c
> @@ -670,6 +670,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,
> @@ -1300,6 +1301,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),
> diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
> index c93c5aaf637e..6b8df9e253a0 100644
> --- a/include/scsi/scsi_device.h
> +++ b/include/scsi/scsi_device.h
> @@ -218,6 +218,8 @@ struct scsi_device {
>   	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 */
>   
> @@ -364,6 +366,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 *);

Cheers,

Hannes
Damien Le Moal April 4, 2023, 11:27 p.m. UTC | #2
On 4/5/23 03:48, Hannes Reinecke wrote:
> On 4/4/23 20:24, Niklas Cassel wrote:
>> From: Damien Le Moal <damien.lemoal@opensource.wdc.com>
>>
>> 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 <damien.lemoal@opensource.wdc.com>
>> Co-developed-by: Niklas Cassel <niklas.cassel@wdc.com>
>> Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
>> ---
>>   Documentation/ABI/testing/sysfs-block-device |  9 +++
>>   drivers/scsi/scsi.c                          | 81 ++++++++++++++++++++
>>   drivers/scsi/scsi_scan.c                     |  3 +
>>   drivers/scsi/scsi_sysfs.c                    |  2 +
>>   include/scsi/scsi_device.h                   |  3 +
>>   5 files changed, 98 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-block-device b/Documentation/ABI/testing/sysfs-block-device
>> index 7ac7b19b2f72..ee3610a25845 100644
>> --- a/Documentation/ABI/testing/sysfs-block-device
>> +++ b/Documentation/ABI/testing/sysfs-block-device
>> @@ -95,3 +95,12 @@ Description:
>>   		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:		Mar, 2023
>> +KernelVersion:	v6.4
>> +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.
>> diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
>> index 62d9472e08e9..c03814ce23ca 100644
>> --- a/drivers/scsi/scsi.c
>> +++ b/drivers/scsi/scsi.c
>> @@ -570,6 +570,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;
>> +}
>> +
> Why do we need to check this when writing to sysfs? Shouldn't we detect 
> this during startup / revalidate?

Hmm, I think you missed the call chain on this one (the patch starting with the
sys attribute doc changes is a little confusing).

scsi_cdl_check_cmd() is called from scsi_cdl_check(), which is itself called
from scsi_add_lun() (for initial device scan) and scsi_rescan_device() for
revalidate. scsi_cdl_check() will set sdev->cdl_supported to 1 for devices
supporting CDL, which is what sysfs cdl_supported show method uses, and later,
the cdl_enable attribute show/store method use that too. That function is not
called from sysfs attributes methods.

Note that this function was moved from sd.c to scsi.c as CDL is defined is SPC,
not SBC.

> 
>> +/**
>> + * 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
>> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
>> index d217be323cc6..aa13feb17c62 100644
>> --- a/drivers/scsi/scsi_scan.c
>> +++ b/drivers/scsi/scsi_scan.c
>> @@ -1087,6 +1087,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;
>> @@ -1624,6 +1626,7 @@ void scsi_rescan_device(struct device *dev)
>>   	device_lock(dev);
>>   
>>   	scsi_attach_vpd(sdev);
>> +	scsi_cdl_check(sdev);
>>   
>>   	if (sdev->handler && sdev->handler->rescan)
>>   		sdev->handler->rescan(sdev);
>> diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
>> index ee28f73af4d4..4994148e685b 100644
>> --- a/drivers/scsi/scsi_sysfs.c
>> +++ b/drivers/scsi/scsi_sysfs.c
>> @@ -670,6 +670,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,
>> @@ -1300,6 +1301,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),
>> diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
>> index c93c5aaf637e..6b8df9e253a0 100644
>> --- a/include/scsi/scsi_device.h
>> +++ b/include/scsi/scsi_device.h
>> @@ -218,6 +218,8 @@ struct scsi_device {
>>   	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 */
>>   
>> @@ -364,6 +366,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 *);
> 
> Cheers,
> 
> Hannes
>
Hannes Reinecke April 5, 2023, 6:26 a.m. UTC | #3
On 4/5/23 01:27, Damien Le Moal wrote:
> On 4/5/23 03:48, Hannes Reinecke wrote:
>> On 4/4/23 20:24, Niklas Cassel wrote:
>>> From: Damien Le Moal <damien.lemoal@opensource.wdc.com>
>>>
>>> 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 <damien.lemoal@opensource.wdc.com>
>>> Co-developed-by: Niklas Cassel <niklas.cassel@wdc.com>
>>> Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
>>> ---
>>>    Documentation/ABI/testing/sysfs-block-device |  9 +++
>>>    drivers/scsi/scsi.c                          | 81 ++++++++++++++++++++
>>>    drivers/scsi/scsi_scan.c                     |  3 +
>>>    drivers/scsi/scsi_sysfs.c                    |  2 +
>>>    include/scsi/scsi_device.h                   |  3 +
>>>    5 files changed, 98 insertions(+)
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-block-device b/Documentation/ABI/testing/sysfs-block-device
>>> index 7ac7b19b2f72..ee3610a25845 100644
>>> --- a/Documentation/ABI/testing/sysfs-block-device
>>> +++ b/Documentation/ABI/testing/sysfs-block-device
>>> @@ -95,3 +95,12 @@ Description:
>>>    		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:		Mar, 2023
>>> +KernelVersion:	v6.4
>>> +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.
>>> diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
>>> index 62d9472e08e9..c03814ce23ca 100644
>>> --- a/drivers/scsi/scsi.c
>>> +++ b/drivers/scsi/scsi.c
>>> @@ -570,6 +570,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;
>>> +}
>>> +
>> Why do we need to check this when writing to sysfs? Shouldn't we detect
>> this during startup / revalidate?
> 
> Hmm, I think you missed the call chain on this one (the patch starting with the
> sys attribute doc changes is a little confusing).
> 
> scsi_cdl_check_cmd() is called from scsi_cdl_check(), which is itself called
> from scsi_add_lun() (for initial device scan) and scsi_rescan_device() for
> revalidate. scsi_cdl_check() will set sdev->cdl_supported to 1 for devices
> supporting CDL, which is what sysfs cdl_supported show method uses, and later,
> the cdl_enable attribute show/store method use that too. That function is not
> called from sysfs attributes methods.
> 
> Note that this function was moved from sd.c to scsi.c as CDL is defined is SPC,
> not SBC.
> 
Indeed, you are right.

Reviewed-by: Hannes Reinecke <hare@suse.de>

Cheers,

Hannes
diff mbox series

Patch

diff --git a/Documentation/ABI/testing/sysfs-block-device b/Documentation/ABI/testing/sysfs-block-device
index 7ac7b19b2f72..ee3610a25845 100644
--- a/Documentation/ABI/testing/sysfs-block-device
+++ b/Documentation/ABI/testing/sysfs-block-device
@@ -95,3 +95,12 @@  Description:
 		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:		Mar, 2023
+KernelVersion:	v6.4
+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.
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 62d9472e08e9..c03814ce23ca 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -570,6 +570,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
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index d217be323cc6..aa13feb17c62 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1087,6 +1087,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;
@@ -1624,6 +1626,7 @@  void scsi_rescan_device(struct device *dev)
 	device_lock(dev);
 
 	scsi_attach_vpd(sdev);
+	scsi_cdl_check(sdev);
 
 	if (sdev->handler && sdev->handler->rescan)
 		sdev->handler->rescan(sdev);
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index ee28f73af4d4..4994148e685b 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -670,6 +670,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,
@@ -1300,6 +1301,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),
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index c93c5aaf637e..6b8df9e253a0 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -218,6 +218,8 @@  struct scsi_device {
 	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 */
 
@@ -364,6 +366,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 *);