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
next prev parent 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