From: Damien Le Moal <dlemoal@kernel.org>
To: linux-ide@vger.kernel.org, Niklas Cassel <cassel@kernel.org>
Subject: [PATCH v3 2/5] ata: libata-scsi: refactor ata_scsi_report_supported_opcodes()
Date: Fri, 3 Jul 2026 13:05:29 +0900 [thread overview]
Message-ID: <20260703040532.2152614-3-dlemoal@kernel.org> (raw)
In-Reply-To: <20260703040532.2152614-1-dlemoal@kernel.org>
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 },
+};
+
+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;
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;
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;
}
--
2.54.0
next prev parent reply other threads:[~2026-07-03 4:05 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 ` Damien Le Moal [this message]
2026-07-03 6:44 ` [PATCH v3 2/5] ata: libata-scsi: refactor ata_scsi_report_supported_opcodes() Hannes Reinecke
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=20260703040532.2152614-3-dlemoal@kernel.org \
--to=dlemoal@kernel.org \
--cc=cassel@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