mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
ata: libata-scsi: refactor ata_scsi_report_supported_opcodes()
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> Reviewed-by: Hannes Reinecke <hare@kernel.org>
This commit is contained in:
parent
13d5478d9b
commit
1128f29f99
|
|
@ -3593,12 +3593,166 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
|
|||
return 1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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:
|
||||
if (dev->flags & ATA_DFLAG_CDL) {
|
||||
/*
|
||||
* CDL read descriptors map to the T2A page, that is,
|
||||
* rwcdlp = 0x01 and cdlp = 0x01
|
||||
*/
|
||||
sup->rwcdlp = 0x01;
|
||||
sup->cdlp = 0x01;
|
||||
}
|
||||
break;
|
||||
case WRITE_16:
|
||||
if (dev->flags & ATA_DFLAG_CDL) {
|
||||
/*
|
||||
* CDL write descriptors map to the T2B page, that is,
|
||||
* rwcdlp = 0x01 and cdlp = 0x02
|
||||
*/
|
||||
sup->rwcdlp = 0x01;
|
||||
sup->cdlp = 0x02;
|
||||
}
|
||||
break;
|
||||
case ZBC_IN:
|
||||
case ZBC_OUT:
|
||||
return ata_dev_is_zoned(dev);
|
||||
case SECURITY_PROTOCOL_IN:
|
||||
case SECURITY_PROTOCOL_OUT:
|
||||
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;
|
||||
u8 supported = 0, cdlp = 0, rwcdlp = 0;
|
||||
|
||||
if (cdb[2] != 1 && cdb[2] != 3) {
|
||||
ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
|
||||
|
|
@ -3606,74 +3760,13 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case ZBC_IN:
|
||||
case ZBC_OUT:
|
||||
if (ata_dev_is_zoned(dev))
|
||||
supported = 3;
|
||||
break;
|
||||
case SECURITY_PROTOCOL_IN:
|
||||
case SECURITY_PROTOCOL_OUT:
|
||||
if (dev->flags & ATA_DFLAG_TRUSTED)
|
||||
supported = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user