Skip to contentSkip to conversation

Improve ata_scsi_report_supported_opcodes()

PATCH In review Damien Le Moal 3 participants 8 messages Jul 3 libata

lore
Share & tools
lore

Damien Le Moal

author maintainer
#0 lore raw

v4 cover Improve ata_scsi_report_supported_opcodes()

The implementation of ata_scsi_report_supported_opcodes() mostly satisfies the internal use when an ATA device is probed from the SCSI layer, but it is very incomplete. The code is also not very flexible for extending support for new commands.

This patch series address these issues, cleaning up the implementation and making REPORT SUPPORTED OPERATION CODES fully functional. E.g., with these changes, sg_opcodes can be used for listing all SCSI commands that are supported by libata-scsi and a device.

Example:

  # sg_opcodes /dev/sda
    ATA       WDC  WUH722626AL  WZ41
    Peripheral device type: disk
  Opcode  Service    CDB  RWCDLP,  Name
  (hex)   action(h)  size   CDLP
  -----------------------------------------------
   00                  6    0,0    Test Unit Ready
   01                  6    0,0    Rezero Unit
   03                  6    0,0    Request Sense
   08                  6    0,0    Read(6)
   0a                  6    0,0    Write(6)
   0b                  6    0,0    Seek(6)
   12                  6    0,0    Inquiry
   15                  6    0,0    Mode select(6)
   1a                  6    0,0    Mode sense(6)
   1b                  6    0,0    Start stop unit
   1d                  6    0,0    Send diagnostic
   25                 10    0,0    Read capacity(10)
   28                 10    0,0    Read(10)
   2a                 10    0,0    Write(10)
   2b                 10    0,0    Seek(10)
   2f                 10    0,0    Verify(10)
   35                 10    0,0    Synchronize cache(10)
   55                 10    0,0    Mode select(10)
   5a                 10    0,0    Mode sense(10)
   7f     1ff0        32    0,0    ATA pass-through(32)
   85                 16    0,0    ATA pass-through(16)
   88                 16    1,1    Read(16)
   8a                 16    1,2    Write(16)
   8f                 16    0,0    Verify(16)
   91                 16    0,0    Synchronize cache(16)
   9e       10        16    0,0    Read capacity(16)
   a0                 12    0,0    Report luns
   a1                 12    0,0    ATA pass-through(12)
   a3        c        12    0,0    Report supported operation codes

Changes from v2:
- Patch 2: cleaned up the switch-case in ata_scsi_cmd_is_supported()

Changes from v2:
- Patch 2: reformatted the array of supported commands and removed WRITE_SAME_16 command from it.
- Applied review tags.

Changes from v1:
- Patch 2: Added an sa_valid field to the array of supported command to correctly handle sa == 0 being a valid service action (report zones) in patch 3
- Dropped v1 patch 4
- Patch 5: correctly set the servact field based on the sa_valid field of the array of supported commands.

Damien Le Moal (5):
ata: libata: rename ata_dev_is_zac()
ata: libata-scsi: refactor ata_scsi_report_supported_opcodes()
ata: libata-scsi: improve service action support in
ata_scsi_report_supported_opcodes()
ata: libata-scsi: support reporting options 2 in REPORT SUPPORTED
OPERATION CODES
ata: libata-scsi: support the all command format for reporting
supported commands

 drivers/ata/libata-core.c |   6 +-
 drivers/ata/libata-scsi.c | 279 ++++++++++++++++++++++++++++++--------
 drivers/ata/libata.h      |   2 +-
 3 files changed, 226 insertions(+), 61 deletions(-)
Signature · 1 line
2.54.0
author maintainer
#1 lore raw

PATCH v4 1/5 ata: libata: rename ata_dev_is_zac()

The helper function ata_dev_is_zac() checks if a device is a ZAC class device (host managed zoned disk) or if it is a host aware zoned disk, that is, a regular ATA disk that supports the zoned capabilities. So the name of this helper function is confusing as it hints at the first case only.

Rename this helper function to ata_dev_is_zoned() to avoid confusions and better reflect the two cases tested. Use this helper in ata_scsiop_inq_std(), ata_scsiop_read_cap() and ata_scsi_report_supported_opcodes() instead of having the same tests open coded.

While at it, to stay consistent with this renaming, also rename ata_dev_config_zac() to ata_dev_config_zoned().

---
 drivers/ata/libata-core.c |  6 +++---
 drivers/ata/libata-scsi.c | 11 +++++------
 drivers/ata/libata.h      |  2 +-
 3 files changed, 9 insertions(+), 10 deletions(-)
drivers/ata/libata-core.c +3 -3
index bdc88cf74709..5121faf9738e 100644
@@ -2488,7 +2488,7 @@ static void ata_dev_config_sense_reporting(struct ata_device *dev)
 	}
 }
 
-static void ata_dev_config_zac(struct ata_device *dev)
+static void ata_dev_config_zoned(struct ata_device *dev)
 {
 	unsigned int err_mask;
 	u8 *identify_buf = dev->sector_buf;
@@ -2497,7 +2497,7 @@ static void ata_dev_config_zac(struct ata_device *dev)
 	dev->zac_zones_optimal_nonseq = U32_MAX;
 	dev->zac_zones_max_open = U32_MAX;
 
-	if (!ata_dev_is_zac(dev))
+	if (!ata_dev_is_zoned(dev))
 		return;
 
 	if (!ata_identify_page_supported(dev, ATA_LOG_ZONED_INFORMATION)) {
@@ -3111,7 +3111,7 @@ int ata_dev_configure(struct ata_device *dev)
 		ata_dev_config_fua(dev);
 		ata_dev_config_devslp(dev);
 		ata_dev_config_sense_reporting(dev);
-		ata_dev_config_zac(dev);
+		ata_dev_config_zoned(dev);
 		ata_dev_config_trusted(dev);
 		ata_dev_config_cpr(dev);
 		ata_dev_config_cdl(dev);
drivers/ata/libata-scsi.c +5 -6
index 5868526301a2..be7143df6a01 100644
@@ -2023,7 +2023,7 @@ static unsigned int ata_scsiop_inq_std(struct ata_device *dev,
 	if (rbuf[32] == 0 || rbuf[32] == ' ')
 		memcpy(&rbuf[32], "n/a ", 4);
 
-	if (ata_id_zoned_cap(dev->id) || dev->class == ATA_DEV_ZAC)
+	if (ata_dev_is_zoned(dev))
 		memcpy(rbuf + 58, versions_zbc, sizeof(versions_zbc));
 	else
 		memcpy(rbuf + 58, versions, sizeof(versions));
@@ -2063,7 +2063,7 @@ static unsigned int ata_scsiop_inq_00(struct ata_device *dev,
 	};
 
 	for (i = 0; i < sizeof(pages); i++) {
-		if (pages[i] == 0xb6 && !ata_dev_is_zac(dev))
+		if (pages[i] == 0xb6 && !ata_dev_is_zoned(dev))
 			continue;
 		rbuf[num_pages + 4] = pages[i];
 		num_pages++;
@@ -2320,7 +2320,7 @@ static unsigned int ata_scsiop_inq_b2(struct ata_device *dev,
 static unsigned int ata_scsiop_inq_b6(struct ata_device *dev,
 				      struct scsi_cmnd *cmd, u8 *rbuf)
 {
-	if (!ata_dev_is_zac(dev)) {
+	if (!ata_dev_is_zoned(dev)) {
 		ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
 		return 0;
 	}
@@ -2851,7 +2851,7 @@ static unsigned int ata_scsiop_read_cap(struct ata_device *dev,
 	rbuf[10] = sector_size >> (8 * 1);
 	rbuf[11] = sector_size;
 
-	if (ata_id_zoned_cap(dev->id) || dev->class == ATA_DEV_ZAC)
+	if (ata_dev_is_zoned(dev))
 		rbuf[12] = (1 << 4); /* RC_BASIS */
 	rbuf[13] = log2_per_phys;
 	rbuf[14] = (lowest_aligned >> 8) & 0x3f;
@@ -3659,8 +3659,7 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
 		break;
 	case ZBC_IN:
 	case ZBC_OUT:
-		if (ata_id_zoned_cap(dev->id) ||
-		    dev->class == ATA_DEV_ZAC)
+		if (ata_dev_is_zoned(dev))
 			supported = 3;
 		break;
 	case SECURITY_PROTOCOL_IN:
drivers/ata/libata.h +1 -1
index 700627596ce1..39494ab206a2 100644
@@ -44,7 +44,7 @@ static inline bool ata_sstatus_online(u32 sstatus)
 	return (sstatus & 0xf) == 0x3;
 }
 
-static inline bool ata_dev_is_zac(struct ata_device *dev)
+static inline bool ata_dev_is_zoned(struct ata_device *dev)
 {
 	/* Host managed device or host aware device */
 	return dev->class == ATA_DEV_ZAC ||
Signature · 1 line
2.54.0
author maintainer
#2 lore raw

PATCH v4 2/5 ata: libata-scsi: refactor ata_scsi_report_supported_opcodes()

In reply to Damien Le Moal

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.

---
 drivers/ata/libata-scsi.c | 197 ++++++++++++++++++++++++++++----------
 1 file changed, 145 insertions(+), 52 deletions(-)
drivers/ata/libata-scsi.c +145 -52
index be7143df6a01..196adb684433 100644
@@ -3593,87 +3593,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;
 	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;
 	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;
 }
Signature · 1 line
2.54.0
Reviewed-by on v4 2/5
#3 lore raw

Re: [PATCH v4 2/5] ata: libata-scsi: refactor ata_scsi_report_supported_opcodes()

Quoted 33 lines · Damien Le Moal

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.

Quoted 5 lines
---
  drivers/ata/libata-scsi.c | 197 ++++++++++++++++++++++++++++----------
  1 file changed, 145 insertions(+), 52 deletions(-)

Cheers,

Hannes

Signature · 4 lines
Dr. Hannes Reinecke                  Kernel Storage Architect
[email protected]                                +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
author maintainer
#4 lore raw

PATCH v4 3/5 ata: libata-scsi: improve service action support in ata_scsi_report_supported_opcodes()

In reply to Damien Le Moal

ata_scsi_report_supported_opcodes() is ignoring the service action specified in the SCSI command CDB, but the one command format must take this field into consideration. With the reporting options field set to 1, the REPORT SUPPORTED OPERATION CODES command must be failed if the specified opcode to check has service actions, while reporting option 3 must match supported opcodes together with the specified service action.

Stop ignoring the service action by passing it to ata_scsi_cmd_is_supported() and searching for commands in the array of supported commands (ata_supported_cmds) using both the command opcode and service action.

Introduce the helper function ata_scsi_supported_cmd_use_sa() to determine if a particular command has service actions and use this function to fail a REPORT SUPPORTED OPERATION CODES command if such command is specified with reporting options 1.

---
 drivers/ata/libata-scsi.c | 41 ++++++++++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 7 deletions(-)
drivers/ata/libata-scsi.c +34 -7
index 196adb684433..39323d39fd75 100644
@@ -3682,32 +3682,46 @@ static const struct ata_scsi_cmd ata_supported_cmds[] = {
 	{	.op = SECURITY_PROTOCOL_OUT,	.cdb_len = 12	},
 };
 
-static const struct ata_scsi_cmd *ata_scsi_get_supported_cmd(u8 op)
+static const struct ata_scsi_cmd *ata_scsi_get_supported_cmd(u8 op, u16 sa)
 {
 	const struct ata_scsi_cmd *cmd;
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) {
 		cmd = &ata_supported_cmds[i];
-		if (cmd->op == op)
+		if (cmd->op == op && cmd->sa == sa)
 			return cmd;
 	}
 
 	return NULL;
 }
 
+static bool ata_scsi_supported_cmd_use_sa(u8 op)
+{
+	const struct ata_scsi_cmd *cmd;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) {
+		cmd = &ata_supported_cmds[i];
+		if (cmd->op == op)
+			return cmd->sa_valid;
+	}
+
+	return false;
+}
+
 struct ata_scsi_cmd_support {
 	u8 cdlp;
 	u8 rwcdlp;
 };
 
-static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op,
+static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, u16 sa,
 				      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);
+	cmd = ata_scsi_get_supported_cmd(op, sa);
 	if (!cmd)
 		return false;
 
@@ -3753,15 +3767,28 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
 {
 	struct ata_scsi_cmd_support sup;
 	u8 *cdb = cmd->cmnd;
-
-	if (cdb[2] != 1 && cdb[2] != 3) {
+	u16 sa = 0;
+
+	switch (cdb[2]) {
+	case 1:
+		/* One command format with command support data, ignore sa. */
+		if (ata_scsi_supported_cmd_use_sa(cdb[3])) {
+			ata_scsi_set_invalid_field(dev, cmd, 3, 0xff);
+			return 0;
+		}
+		break;
+	case 3:
+		/* One command format */
+		sa = get_unaligned_be16(&cdb[4]);
+		break;
+	default:
 		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 */
-	if (ata_scsi_cmd_is_supported(dev, cdb[3], &sup)) {
+	if (ata_scsi_cmd_is_supported(dev, cdb[3], sa, &sup)) {
 		rbuf[0] = sup.rwcdlp;
 		rbuf[1] = (sup.cdlp << 3) | 0x03;
 	} else {
Signature · 1 line
2.54.0
author maintainer
#5 lore raw

PATCH v4 4/5 ata: libata-scsi: support reporting options 2 in REPORT SUPPORTED OPERATION CODES

In reply to Damien Le Moal

Add support for the reporting options value 2 of the in REPORT SUPPORTED OPERATION CODES command in ata_scsi_report_supported_opcodes(). With this reporting option, the specified opcode to check must have a service action. Use ata_scsi_supported_cmd_has_sa() to check for this and fill the command reply buffer using the one command format based on the command support.

---
 drivers/ata/libata-scsi.c | 7 +++++++
 1 file changed, 7 insertions(+)
drivers/ata/libata-scsi.c +7 -0
index 39323d39fd75..265236b0493d 100644
@@ -3777,6 +3777,13 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
 			return 0;
 		}
 		break;
+	case 2:
+		/* One command format, must have sa. */
+		if (!ata_scsi_supported_cmd_use_sa(cdb[3])) {
+			ata_scsi_set_invalid_field(dev, cmd, 3, 0xff);
+			return 0;
+		}
+		fallthrough;
 	case 3:
 		/* One command format */
 		sa = get_unaligned_be16(&cdb[4]);
Signature · 1 line
2.54.0
#6 lore raw

Re: [PATCH v4 4/5] ata: libata-scsi: support reporting options 2 in REPORT SUPPORTED OPERATION CODES

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] Reporting option 3 fails to ignore the REQUESTED SERVICE ACTION field for commands that do not…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Reporting option 3 fails to ignore the REQUESTED SERVICE ACTION field for commands that do not have service actions.
- [Medium] Validation for reporting options 1 and 2 incorrectly handles unsupported commands by conflating 'unsupported' with 'no service action'. --

commit f779ad68951058a9ef94049680f8a5d3f0d0be1e
Author: Damien Le Moal <[email protected]>

ata: libata-scsi: support reporting options 2 in REPORT SUPPORTED OPERATION CODES

Add support for the reporting options value 2 of the in REPORT SUPPORTED OPERATION CODES command in ata_scsi_report_supported_opcodes(). With this reporting option, the specified opcode to check must have a service action. Use ata_scsi_supported_cmd_has_sa() to check for this and fill the command reply buffer using the one command format based on the command support.

drivers/ata/libata-scsi.c static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, · line 3779

Quoted 11 lines
drivers/ata/libata-scsi.c +3 -0
index dca46e7779b64..157a4b6979a89 100644
@@ -3774,6 +3774,13 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
 			return 0;
 		}
 		break;
+	case 2:
+		/* One command format, must have sa. */
+		if (!ata_scsi_supported_cmd_use_sa(cdb[3])) {

[Severity: Medium]
Does ata_scsi_supported_cmd_use_sa() accurately distinguish between an unsupported command and a command that lacks a service action?

If a host queries an unsupported command that architecturally does have a service action (such as opcode 0x5e), it appears the helper would return false because the command is simply not in the supported list.

For option 2, this causes the command to incorrectly fail with a check condition (invalid field in cdb) instead of gracefully returning 'Not supported' in the data phase.

drivers/ata/libata-scsi.c static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, · line 3789

Quoted 7 lines
drivers/ata/libata-scsi.c +4 -0
+			ata_scsi_set_invalid_field(dev, cmd, 3, 0xff);
+			return 0;
+		}
+		fallthrough;
 	case 3:
 		/* One command format */
 		sa = get_unaligned_be16(&cdb[4]);

[Severity: Medium]
Will this unconditionally read and use the service action field for reporting option 3 even when the command doesn't have a service action?

According to the specification, the requested service action field should be ignored for commands without service actions. If a host provides a non-zero garbage value in this field for a command without a service action, passing this value into ata_scsi_cmd_is_supported() might cause the lookup to fail and incorrectly report the command as unsupported.

Quoted 1 line

break;

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
author maintainer
#7 lore raw

PATCH v4 5/5 ata: libata-scsi: support the all command format for reporting supported commands

In reply to Damien Le Moal

Add support to ata_scsi_report_supported_opcodes() for the all command format indicated with a reporting option of 0. The function ata_scsi_report_all_supported_opcodes() is introduced to implement this support. This function operates by testing all commands of the ata_supported_cmds array and testing them using ata_scsi_cmd_is_supported(), filling rbuf as it loops through all the commands that libata can emulate or translate.

With this change, sg_opcodes /dev/sdX is able to list all commands supported by libata and a device. An example below is shown for a SATA disk also supporting CDL:

  # sg_opcodes /dev/sda
    ATA       WDC  WUH722626AL  WZ41
    Peripheral device type: disk
  Opcode  Service    CDB  RWCDLP,  Name
  (hex)   action(h)  size   CDLP
  -----------------------------------------------
   00                  6    0,0    Test Unit Ready
   01                  6    0,0    Rezero Unit
   03                  6    0,0    Request Sense
   08                  6    0,0    Read(6)
   0a                  6    0,0    Write(6)
   0b                  6    0,0    Seek(6)
   12                  6    0,0    Inquiry
   15                  6    0,0    Mode select(6)
   1a                  6    0,0    Mode sense(6)
   1b                  6    0,0    Start stop unit
   1d                  6    0,0    Send diagnostic
   25                 10    0,0    Read capacity(10)
   28                 10    0,0    Read(10)
   2a                 10    0,0    Write(10)
   2b                 10    0,0    Seek(10)
   2f                 10    0,0    Verify(10)
   35                 10    0,0    Synchronize cache(10)
   55                 10    0,0    Mode select(10)
   5a                 10    0,0    Mode sense(10)
   7f     1ff0        32    0,0    ATA pass-through(32)
   85                 16    0,0    ATA pass-through(16)
   88                 16    1,1    Read(16)
   8a                 16    1,2    Write(16)
   8f                 16    0,0    Verify(16)
   91                 16    0,0    Synchronize cache(16)
   9e       10        16    0,0    Read capacity(16)
   a0                 12    0,0    Report luns
   a1                 12    0,0    ATA pass-through(12)
   a3        c        12    0,0    Report supported operation codes
---
 drivers/ata/libata-scsi.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)
drivers/ata/libata-scsi.c +39 -0
index 265236b0493d..e48915a9f81b 100644
@@ -3761,6 +3761,42 @@ static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, u16 sa,
 	return true;
 }
 
+static unsigned int
+ata_scsi_report_all_supported_opcodes(struct ata_device *dev, u8 *rbuf)
+{
+	struct ata_scsi_cmd_support sup;
+	const struct ata_scsi_cmd *cmd;
+	unsigned int len = 4;
+	u8 *buf = &rbuf[len];
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) {
+		if (len > ATA_SCSI_RBUF_SIZE - 8)
+			break;
+
+		cmd = &ata_supported_cmds[i];
+
+		/* All command format */
+		if (!ata_scsi_cmd_is_supported(dev, cmd->op, cmd->sa, &sup))
+			continue;
+
+		buf[0] = cmd->op;
+		put_unaligned_be16(cmd->sa, &buf[2]);
+		buf[5] = (sup.rwcdlp << 6) | (sup.cdlp << 2);
+		if (cmd->sa_valid)
+			buf[5] |= 0x01;
+		put_unaligned_be16(cmd->cdb_len, &buf[6]);
+
+		/* CTDP == 0 */
+		len += 8;
+		buf += 8;
+	}
+
+	put_unaligned_be32(len - 4, &rbuf[0]);
+
+	return len;
+}
+
 static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
 						      struct scsi_cmnd *cmd,
 						      u8 *rbuf)
@@ -3770,6 +3806,9 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
 	u16 sa = 0;
 
 	switch (cdb[2]) {
+	case 0:
+		/* All command format */
+		return ata_scsi_report_all_supported_opcodes(dev, rbuf);
 	case 1:
 		/* One command format with command support data, ignore sa. */
 		if (ata_scsi_supported_cmd_use_sa(cdb[3])) {
Signature · 1 line
2.54.0