Skip to content

Commit 3c6bdae

Browse files
martinkpetersenJames Bottomley
authored andcommitted
[SCSI] Add a report opcode helper
The REPORT SUPPORTED OPERATION CODES command can be used to query whether a given opcode is supported by a device. Add a helper function that allows us to look up commands. We only issue RSOC if the device reports compliance with SPC-3 or later. But to err on the side of caution we disable the command for ATA, FireWire and USB. Signed-off-by: Martin K. Petersen <[email protected]> Acked-by: Mike Snitzer <[email protected]> Signed-off-by: James Bottomley <[email protected]>
1 parent 49bd665 commit 3c6bdae

File tree

5 files changed

+53
-0
lines changed

5 files changed

+53
-0
lines changed

drivers/ata/libata-scsi.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,7 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
10521052
{
10531053
sdev->use_10_for_rw = 1;
10541054
sdev->use_10_for_ms = 1;
1055+
sdev->no_report_opcodes = 1;
10551056

10561057
/* Schedule policy is determined by ->qc_defer() callback and
10571058
* it needs to see every deferred qc. Set dev_blocked to 1 to

drivers/firewire/sbp2.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,7 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
15461546
struct sbp2_logical_unit *lu = sdev->hostdata;
15471547

15481548
sdev->use_10_for_rw = 1;
1549+
sdev->no_report_opcodes = 1;
15491550

15501551
if (sbp2_param_exclusive_login)
15511552
sdev->manage_start_stop = 1;

drivers/scsi/scsi.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include <linux/cpu.h>
5656
#include <linux/mutex.h>
5757
#include <linux/async.h>
58+
#include <asm/unaligned.h>
5859

5960
#include <scsi/scsi.h>
6061
#include <scsi/scsi_cmnd.h>
@@ -1061,6 +1062,50 @@ int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf,
10611062
}
10621063
EXPORT_SYMBOL_GPL(scsi_get_vpd_page);
10631064

1065+
/**
1066+
* scsi_report_opcode - Find out if a given command opcode is supported
1067+
* @sdev: scsi device to query
1068+
* @buffer: scratch buffer (must be at least 20 bytes long)
1069+
* @len: length of buffer
1070+
* @opcode: opcode for command to look up
1071+
*
1072+
* Uses the REPORT SUPPORTED OPERATION CODES to look up the given
1073+
* opcode. Returns 0 if RSOC fails or if the command opcode is
1074+
* unsupported. Returns 1 if the device claims to support the command.
1075+
*/
1076+
int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
1077+
unsigned int len, unsigned char opcode)
1078+
{
1079+
unsigned char cmd[16];
1080+
struct scsi_sense_hdr sshdr;
1081+
int result;
1082+
1083+
if (sdev->no_report_opcodes || sdev->scsi_level < SCSI_SPC_3)
1084+
return 0;
1085+
1086+
memset(cmd, 0, 16);
1087+
cmd[0] = MAINTENANCE_IN;
1088+
cmd[1] = MI_REPORT_SUPPORTED_OPERATION_CODES;
1089+
cmd[2] = 1; /* One command format */
1090+
cmd[3] = opcode;
1091+
put_unaligned_be32(len, &cmd[6]);
1092+
memset(buffer, 0, len);
1093+
1094+
result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, len,
1095+
&sshdr, 30 * HZ, 3, NULL);
1096+
1097+
if (result && scsi_sense_valid(&sshdr) &&
1098+
sshdr.sense_key == ILLEGAL_REQUEST &&
1099+
(sshdr.asc == 0x20 || sshdr.asc == 0x24) && sshdr.ascq == 0x00)
1100+
return 0;
1101+
1102+
if ((buffer[1] & 3) == 3) /* Command supported */
1103+
return 1;
1104+
1105+
return 0;
1106+
}
1107+
EXPORT_SYMBOL(scsi_report_opcode);
1108+
10641109
/**
10651110
* scsi_device_get - get an additional reference to a scsi_device
10661111
* @sdev: device to get a reference to

drivers/usb/storage/scsiglue.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ static int slave_configure(struct scsi_device *sdev)
186186
/* Some devices don't handle VPD pages correctly */
187187
sdev->skip_vpd_pages = 1;
188188

189+
/* Do not attempt to use REPORT SUPPORTED OPERATION CODES */
190+
sdev->no_report_opcodes = 1;
191+
189192
/* Some disks return the total number of blocks in response
190193
* to READ CAPACITY rather than the highest block number.
191194
* If this device makes that mistake, tell the sd driver. */

include/scsi/scsi_device.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ struct scsi_device {
135135
* because we did a bus reset. */
136136
unsigned use_10_for_rw:1; /* first try 10-byte read / write */
137137
unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */
138+
unsigned no_report_opcodes:1; /* no REPORT SUPPORTED OPERATION CODES */
138139
unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */
139140
unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */
140141
unsigned skip_vpd_pages:1; /* do not read VPD pages */
@@ -362,6 +363,8 @@ extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout,
362363
int retries, struct scsi_sense_hdr *sshdr);
363364
extern int scsi_get_vpd_page(struct scsi_device *, u8 page, unsigned char *buf,
364365
int buf_len);
366+
extern int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
367+
unsigned int len, unsigned char opcode);
365368
extern int scsi_device_set_state(struct scsi_device *sdev,
366369
enum scsi_device_state state);
367370
extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,

0 commit comments

Comments
 (0)