Commit e17d6340 authored by Martin K. Petersen's avatar Martin K. Petersen

scsi: core: Pick suitable allocation length in scsi_report_opcode()

Some devices hang when a buffer size larger than expected is passed in the
ALLOCATION LENGTH field. For REPORT SUPPORTED OPERATION CODES we currently
only request a single command descriptor at a time and therefore the actual
size of the command is known ahead of time. Limit the ALLOCATION LENGTH to
the header size plus the command length of the opcode we are asking about.

Link: https://lore.kernel.org/r/20220302053559.32147-5-martin.petersen@oracle.comReviewed-by: default avatarHannes Reinecke <hare@suse.de>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarJohannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: default avatarBart Van Assche <bvanassche@acm.org>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent c92a6b5d
...@@ -503,21 +503,30 @@ int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer, ...@@ -503,21 +503,30 @@ int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
{ {
unsigned char cmd[16]; unsigned char cmd[16];
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
int result; int result, request_len;
if (sdev->no_report_opcodes || sdev->scsi_level < SCSI_SPC_3) if (sdev->no_report_opcodes || sdev->scsi_level < SCSI_SPC_3)
return -EINVAL; return -EINVAL;
/* RSOC header + size of command we are asking about */
request_len = 4 + COMMAND_SIZE(opcode);
if (request_len > len) {
dev_warn_once(&sdev->sdev_gendev,
"%s: len %u bytes, opcode 0x%02x needs %u\n",
__func__, len, opcode, request_len);
return -EINVAL;
}
memset(cmd, 0, 16); memset(cmd, 0, 16);
cmd[0] = MAINTENANCE_IN; cmd[0] = MAINTENANCE_IN;
cmd[1] = MI_REPORT_SUPPORTED_OPERATION_CODES; cmd[1] = MI_REPORT_SUPPORTED_OPERATION_CODES;
cmd[2] = 1; /* One command format */ cmd[2] = 1; /* One command format */
cmd[3] = opcode; cmd[3] = opcode;
put_unaligned_be32(len, &cmd[6]); put_unaligned_be32(request_len, &cmd[6]);
memset(buffer, 0, len); memset(buffer, 0, len);
result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, len, result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer,
&sshdr, 30 * HZ, 3, NULL); request_len, &sshdr, 30 * HZ, 3, NULL);
if (result < 0) if (result < 0)
return result; return result;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment