Linux ATA/IDE development
 help / color / mirror / Atom feed
From: Hannes Reinecke <hare@suse.de>
To: Damien Le Moal <dlemoal@kernel.org>,
	linux-ide@vger.kernel.org, Niklas Cassel <cassel@kernel.org>
Subject: Re: [PATCH v3 2/5] ata: libata-scsi: refactor ata_scsi_report_supported_opcodes()
Date: Fri, 3 Jul 2026 08:44:02 +0200	[thread overview]
Message-ID: <63c22af2-09df-47ff-a5cd-a9165bf265fc@suse.de> (raw)
In-Reply-To: <20260703040532.2152614-3-dlemoal@kernel.org>

On 7/3/26 6:05 AM, Damien Le Moal wrote:
> ata_scsi_report_supported_opcodes() is very limited in functionality as it
> lacks support for the all command format and also does not handle
> correctly commands that have a service action.
> 
> In preparation for adding these missing features, refactor how
> ata_scsi_report_supported_opcodes() operates to make modifications and
> extensions easier. To do so, introduce the array of supported commands
> ata_supported_cmds. This array entries are of type struct ata_scsi_cmd.
> This structure stores the operation code, CDB length, and the service
> action of a supported SCSI command that libata SAT can translate or
> emulate. Since some service actions (e.g. ZI_REPORT_ZONES) can have a
> value of 0, the field sa_valid of struct ata_scsi_cmd is used to indicate
> if the sa field is valid, or if it should be ignored.
> 
> The helper function ata_scsi_get_supported_cmd() is implemented to search
> for a particular command by opcode in this array. This function is used in
> ata_scsi_cmd_is_supported() together with a struct ata_scsi_cmd_support to
> check based on the target device features if the specified command is
> supported.
> 
> ata_scsi_cmd_is_supported() is used as the main function in
> ata_scsi_report_supported_opcodes() to determine if a particular command
> is supported and fill the command reply rbuf as needed. In the case of a
> command that is not supported, the support field is set to 1 as specified
> in SPC, indicating that the command is not supported.
> 
> Of note is that the old ata_scsi_report_supported_opcodes() code did not
> handle the VARIABLE_LENGTH_CMD/ATA_32 command which is supported and
> translated by libata-scsi. The ata_supported_cmds array includes this
> command. As commented in the code, WRITE_SAME_16 is left out of the array
> of supported command ata_scsi_write_same_xlat() prevents the use of this
> command as a passthrough command.
> 
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
>   drivers/ata/libata-scsi.c | 201 ++++++++++++++++++++++++++++----------
>   1 file changed, 147 insertions(+), 54 deletions(-)
> 
> diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
> index a86721b5df7f..e502b8702f98 100644
> --- a/drivers/ata/libata-scsi.c
> +++ b/drivers/ata/libata-scsi.c
> @@ -3590,87 +3590,180 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
>   	return 1;
>   }
>   
> -static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
> -						      struct scsi_cmnd *cmd,
> -						      u8 *rbuf)
> +struct ata_scsi_cmd {
> +	u8 op;
> +	u8 cdb_len;
> +	bool sa_valid;
> +	u16 sa;
> +};
> +
> +/*
> + * Array of commands supported with translation or emulation, sorted in
> + * ascending opcode and service action order. All of these commands are
> + * processed either in ata_xlat_func() or in ata_scsi_simulate();
> + *
> + * Note: commands that are not fully supported may be left out of this array
> + * so that they are not reported as supported for passthrough but still
> + * available through the block layer. For now, this includes the following
> + * commands:
> + *  - WRITE_SAME_16: ata_scsi_write_same_xlat() forbids passthrough commands
> + */
> +static const struct ata_scsi_cmd ata_supported_cmds[] = {
> +	{	.op = TEST_UNIT_READY,		.cdb_len = 6	},
> +	{	.op = REZERO_UNIT,		.cdb_len = 6	},
> +	{	.op = REQUEST_SENSE,		.cdb_len = 6	},
> +	{	.op = READ_6,			.cdb_len = 6	},
> +	{	.op = WRITE_6,			.cdb_len = 6	},
> +	{	.op = SEEK_6,			.cdb_len = 6	},
> +	{	.op = INQUIRY,			.cdb_len = 6	},
> +	{	.op = MODE_SELECT,		.cdb_len = 6	},
> +	{	.op = MODE_SENSE,		.cdb_len = 6	},
> +	{	.op = START_STOP,		.cdb_len = 6	},
> +	{	.op = SEND_DIAGNOSTIC,		.cdb_len = 6	},
> +	{	.op = READ_CAPACITY,		.cdb_len = 10	},
> +	{	.op = READ_10,			.cdb_len = 10	},
> +	{	.op = WRITE_10,			.cdb_len = 10	},
> +	{	.op = SEEK_10,			.cdb_len = 10	},
> +	{	.op = VERIFY,			.cdb_len = 10	},
> +	{	.op = SYNCHRONIZE_CACHE,	.cdb_len = 10	},
> +	{	.op = MODE_SELECT_10,		.cdb_len = 10	},
> +	{	.op = MODE_SENSE_10,		.cdb_len = 10	},
> +	{
> +		.op = VARIABLE_LENGTH_CMD,	.cdb_len = 32,
> +		.sa_valid = true,
> +		.sa = ATA_32
> +	},
> +	{	.op = ATA_16,			.cdb_len = 16	},
> +	{	.op = READ_16,			.cdb_len = 16	},
> +	{	.op = WRITE_16,			.cdb_len = 16	},
> +	{	.op = VERIFY_16,		.cdb_len = 16	},
> +	{	.op = SYNCHRONIZE_CACHE_16,	.cdb_len = 16	},
> +	{
> +		.op = ZBC_OUT,			.cdb_len = 16,
> +		.sa_valid = true,
> +		.sa = ZO_CLOSE_ZONE
> +	},
> +	{
> +		.op = ZBC_OUT,			.cdb_len = 16,
> +		.sa_valid = true,
> +		.sa = ZO_FINISH_ZONE
> +	},
> +	{
> +		.op = ZBC_OUT,			.cdb_len = 16,
> +		.sa_valid = true,
> +		.sa = ZO_OPEN_ZONE
> +	},
> +	{
> +		.op = ZBC_OUT,			.cdb_len = 16,
> +		.sa_valid = true,
> +		.sa = ZO_RESET_WRITE_POINTER
> +	},
> +	{
> +		.op = ZBC_IN,			.cdb_len = 16,
> +		.sa_valid = true,
> +		.sa = ZI_REPORT_ZONES
> +	},
> +	{
> +		.op = SERVICE_ACTION_IN_16,	.cdb_len = 16,
> +		.sa_valid = true,
> +		.sa = SAI_READ_CAPACITY_16
> +	},
> +	{	.op = REPORT_LUNS,		.cdb_len = 12	},
> +	{	.op = ATA_12,			.cdb_len = 12	},
> +	{	.op = SECURITY_PROTOCOL_IN,	.cdb_len = 12	},
> +	{
> +		.op = MAINTENANCE_IN,		.cdb_len = 12,
> +		.sa_valid = true,
> +		.sa = MI_REPORT_SUPPORTED_OPERATION_CODES
> +	},
> +	{	.op = SECURITY_PROTOCOL_OUT,	.cdb_len = 12	},
> +};
> +

What an unusual formatting. I would have gone for multi-line
entries even for the simple ones to keep formatting the same
for all entries.
But hey.

> +static const struct ata_scsi_cmd *ata_scsi_get_supported_cmd(u8 op)
>   {
> -	u8 *cdb = cmd->cmnd;
> -	u8 supported = 0, cdlp = 0, rwcdlp = 0;
> +	const struct ata_scsi_cmd *cmd;
> +	int i;
>   
> -	if (cdb[2] != 1 && cdb[2] != 3) {
> -		ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
> -		ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
> -		return 0;
> +	for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) {
> +		cmd = &ata_supported_cmds[i];
> +		if (cmd->op == op)
> +			return cmd;
>   	}
>   
> -	switch (cdb[3]) {
> -	case INQUIRY:
> -	case MODE_SENSE:
> -	case MODE_SENSE_10:
> -	case READ_CAPACITY:
> -	case SERVICE_ACTION_IN_16:
> -	case REPORT_LUNS:
> -	case REQUEST_SENSE:
> -	case SYNCHRONIZE_CACHE:
> -	case SYNCHRONIZE_CACHE_16:
> -	case REZERO_UNIT:
> -	case SEEK_6:
> -	case SEEK_10:
> -	case TEST_UNIT_READY:
> -	case SEND_DIAGNOSTIC:
> -	case MAINTENANCE_IN:
> -	case READ_6:
> -	case READ_10:
> -	case WRITE_6:
> -	case WRITE_10:
> -	case ATA_12:
> -	case ATA_16:
> -	case VERIFY:
> -	case VERIFY_16:
> -	case MODE_SELECT:
> -	case MODE_SELECT_10:
> -	case START_STOP:
> -		supported = 3;
> -		break;
> +	return NULL;
> +}
> +
> +struct ata_scsi_cmd_support {
> +	u8 cdlp;
> +	u8 rwcdlp;
> +};
> +
> +static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op,
> +				      struct ata_scsi_cmd_support *sup)
> +{
> +	const struct ata_scsi_cmd *cmd;
> +
> +	/* First, see if we support the command. */
> +	cmd = ata_scsi_get_supported_cmd(op);
> +	if (!cmd)
> +		return false;
> +
> +	/* Now refine the support report depending on the device features. */
> +	memset(sup, 0, sizeof(*sup));
> +	switch (op) {
>   	case READ_16:
> -		supported = 3;
>   		if (dev->flags & ATA_DFLAG_CDL) {
>   			/*
>   			 * CDL read descriptors map to the T2A page, that is,
>   			 * rwcdlp = 0x01 and cdlp = 0x01
>   			 */
> -			rwcdlp = 0x01;
> -			cdlp = 0x01 << 3;
> +			sup->rwcdlp = 0x01;
> +			sup->cdlp = 0x01;
>   		}
> -		break;
> +		return true;

That is confusing. You return 'true' here, yet once you break out
of the switch the function will return 'true', too.
Please stay with the 'break' here.

>   	case WRITE_16:
> -		supported = 3;
>   		if (dev->flags & ATA_DFLAG_CDL) {
>   			/*
>   			 * CDL write descriptors map to the T2B page, that is,
>   			 * rwcdlp = 0x01 and cdlp = 0x02
>   			 */
> -			rwcdlp = 0x01;
> -			cdlp = 0x02 << 3;
> +			sup->rwcdlp = 0x01;
> +			sup->cdlp = 0x02;
>   		}
> -		break;
> +		return true;

Same here.

>   	case ZBC_IN:
>   	case ZBC_OUT:
> -		if (ata_dev_is_zoned(dev))
> -			supported = 3;
> -		break;
> +		return ata_dev_is_zoned(dev);
>   	case SECURITY_PROTOCOL_IN:
>   	case SECURITY_PROTOCOL_OUT:
> -		if (dev->flags & ATA_DFLAG_TRUSTED)
> -			supported = 3;
> -		break;
> +		return dev->flags & ATA_DFLAG_TRUSTED;
>   	default:
>   		break;
>   	}
>   
> +	return true;
> +}
> +
> +static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
> +						      struct scsi_cmnd *cmd,
> +						      u8 *rbuf)
> +{
> +	struct ata_scsi_cmd_support sup;
> +	u8 *cdb = cmd->cmnd;
> +
> +	if (cdb[2] != 1 && cdb[2] != 3) {
> +		ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
> +		ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
> +		return 0;
> +	}
> +
>   	/* One command format */
> -	rbuf[0] = rwcdlp;
> -	rbuf[1] = cdlp | supported;
> +	if (ata_scsi_cmd_is_supported(dev, cdb[3], &sup)) {
> +		rbuf[0] = sup.rwcdlp;
> +		rbuf[1] = (sup.cdlp << 3) | 0x03;
> +	} else {
> +		rbuf[1] = 0x01;
> +	}
>   
>   	return 4;
>   }

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                  Kernel Storage Architect
hare@suse.de                                +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich

  reply	other threads:[~2026-07-03  6:44 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-03  4:05 [PATCH v3 0/5] Improve ata_scsi_report_supported_opcodes() Damien Le Moal
2026-07-03  4:05 ` [PATCH v3 1/5] ata: libata: rename ata_dev_is_zac() Damien Le Moal
2026-07-03  4:59   ` sashiko-bot
2026-07-03  6:15     ` Damien Le Moal
2026-07-03  4:05 ` [PATCH v3 2/5] ata: libata-scsi: refactor ata_scsi_report_supported_opcodes() Damien Le Moal
2026-07-03  6:44   ` Hannes Reinecke [this message]
2026-07-03  7:05     ` Damien Le Moal
2026-07-03  7:06       ` Hannes Reinecke
2026-07-03  4:05 ` [PATCH v3 3/5] ata: libata-scsi: improve service action support in ata_scsi_report_supported_opcodes() Damien Le Moal
2026-07-03  4:05 ` [PATCH v3 4/5] ata: libata-scsi: support reporting options 2 in REPORT SUPPORTED OPERATION CODES Damien Le Moal
2026-07-03  4:27   ` sashiko-bot
2026-07-03  4:05 ` [PATCH v3 5/5] ata: libata-scsi: support the all command format for reporting supported commands Damien Le Moal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=63c22af2-09df-47ff-a5cd-a9165bf265fc@suse.de \
    --to=hare@suse.de \
    --cc=cassel@kernel.org \
    --cc=dlemoal@kernel.org \
    --cc=linux-ide@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox