Commit 6928192f authored by Alan Stern's avatar Alan Stern Committed by James Bottomley

[PATCH] Let LLD specify INQUIRY length

That sounds like a good suggestion.  Even better, instead of adding a new
field we can simply use the existing inquiry_length.

This patch changes scsi_probe_lun() to use the value in
sdev->inquiry_length for the first INQUIRY attempt, if that value is
nonzero.  Subsequent attempts are based, as before, on the blacklist flags
and the Additional Length field in the INQUIRY data.

The patch also contains a fairly extensive reorganization of the
subroutine.  All the code that was duplicated for sending the INQUIRY
command twice has been consolidated.  The routine now makes up to three
passes:

	In the first pass, the transfer length is the value initially
	found in sdev->inquiry_length if that has been set, otherwise
	it is the current conservative 36 bytes.

	If the first pass succeeds, the routine retrieves the blist flags
	for the device and checks the Additional Length field.  The blist
	flags take precedence over sdev->inquiry_length, which in turn
	takes precedence over the Additional Length.  If it turns out
	there is more data available than we transferred the first time,
	a second pass tries to get it.

	If the second pass succeeds the INQUIRY data may have changed,
	so the blist flags are looked up again and the Additional Length
	is checked again.  If not, a third pass tries to get the data
	back, using the same transfer length as the first pass.

Finally, the value stored in sdev->inquiry_length is set to the amount
actually transferred or the size computed from the Additional Length,
whichever is smaller.

Although the net change in the source file size is small, the new routine
has more comments and less code.  Overall I think it's an improvement.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent c7014083
...@@ -358,104 +358,113 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, ...@@ -358,104 +358,113 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
{ {
struct scsi_device *sdev = sreq->sr_device; /* a bit ugly */ struct scsi_device *sdev = sreq->sr_device; /* a bit ugly */
unsigned char scsi_cmd[MAX_COMMAND_SIZE]; unsigned char scsi_cmd[MAX_COMMAND_SIZE];
int possible_inq_resp_len; int first_inquiry_len, try_inquiry_len, next_inquiry_len;
int count = 0; int response_len = 0;
int pass, count;
*bflags = 0; *bflags = 0;
repeat_inquiry:
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY to host %d"
" channel %d id %d lun %d\n", sdev->host->host_no,
sdev->channel, sdev->id, sdev->lun));
/* Perform up to 3 passes. The first pass uses a conservative
* transfer length of 36 unless sdev->inquiry_len specifies a
* different value. */
first_inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36;
try_inquiry_len = first_inquiry_len;
pass = 1;
next_pass:
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY pass %d "
"to host %d channel %d id %d lun %d, length %d\n",
pass, sdev->host->host_no, sdev->channel,
sdev->id, sdev->lun, try_inquiry_len));
/* Each pass gets up to three chances to ignore Unit Attention */
for (count = 0; count < 3; ++count) {
memset(scsi_cmd, 0, 6); memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY; scsi_cmd[0] = INQUIRY;
scsi_cmd[4] = 36; /* issue conservative alloc_length */ scsi_cmd[4] = (unsigned char) try_inquiry_len;
sreq->sr_cmd_len = 0; sreq->sr_cmd_len = 0;
sreq->sr_data_direction = DMA_FROM_DEVICE; sreq->sr_data_direction = DMA_FROM_DEVICE;
memset(inq_result, 0, 36); memset(inq_result, 0, try_inquiry_len);
scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, 36, scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result,
try_inquiry_len,
HZ/2 + HZ*scsi_inq_timeout, 3); HZ/2 + HZ*scsi_inq_timeout, 3);
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 1st INQUIRY %s with" SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
" code 0x%x\n", sreq->sr_result ? "with code 0x%x\n",
"failed" : "successful", sreq->sr_result)); sreq->sr_result ? "failed" : "successful",
++count; sreq->sr_result));
if (sreq->sr_result) { if (sreq->sr_result) {
if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) != 0 &&
(sreq->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION &&
(sreq->sr_sense_buffer[12] == 0x28 ||
sreq->sr_sense_buffer[12] == 0x29) &&
sreq->sr_sense_buffer[13] == 0) {
/* not-ready to ready transition or power-on - good */ /* not-ready to ready transition or power-on - good */
/* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */ /* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */
/* Supposedly, but many buggy devices do so anyway */ /* Supposedly, but many buggy devices do so anyway. */
if (count < 3) if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
goto repeat_inquiry; (sreq->sr_sense_buffer[2] & 0xf) ==
UNIT_ATTENTION &&
(sreq->sr_sense_buffer[12] == 0x28 ||
sreq->sr_sense_buffer[12] == 0x29) &&
sreq->sr_sense_buffer[13] == 0)
continue;
} }
/* break;
* assume no peripheral if any other sort of error
*/
return;
} }
if (sreq->sr_result == 0) {
response_len = (unsigned char) inq_result[4] + 5;
if (response_len > 255)
response_len = first_inquiry_len; /* sanity */
/* /*
* Get any flags for this device. * Get any flags for this device.
* *
* XXX add a bflags to Scsi_Device, and replace the corresponding * XXX add a bflags to Scsi_Device, and replace the
* bit fields in Scsi_Device, so bflags need not be passed as an * corresponding bit fields in Scsi_Device, so bflags
* argument. * need not be passed as an argument.
*/ */
*bflags |= scsi_get_device_flags(sdev, &inq_result[8], &inq_result[16]); *bflags = scsi_get_device_flags(sdev, &inq_result[8],
&inq_result[16]);
possible_inq_resp_len = (unsigned char) inq_result[4] + 5; /* When the first pass succeeds we gain information about
* what larger transfer lengths might work. */
if (pass == 1) {
if (BLIST_INQUIRY_36 & *bflags) if (BLIST_INQUIRY_36 & *bflags)
possible_inq_resp_len = 36; next_inquiry_len = 36;
else if (BLIST_INQUIRY_58 & *bflags) else if (BLIST_INQUIRY_58 & *bflags)
possible_inq_resp_len = 58; next_inquiry_len = 58;
else if (possible_inq_resp_len > 255) else if (sdev->inquiry_len)
possible_inq_resp_len = 36; /* sanity */ next_inquiry_len = sdev->inquiry_len;
else
next_inquiry_len = response_len;
if (possible_inq_resp_len > 36) { /* do additional INQUIRY */ /* If more data is available perform the second pass */
memset(scsi_cmd, 0, 6); if (next_inquiry_len > try_inquiry_len) {
scsi_cmd[0] = INQUIRY; try_inquiry_len = next_inquiry_len;
scsi_cmd[4] = (unsigned char) possible_inq_resp_len; pass = 2;
sreq->sr_cmd_len = 0; goto next_pass;
sreq->sr_data_direction = DMA_FROM_DEVICE; }
/*
* re-zero inq_result just to be safe.
*/
memset(inq_result, 0, possible_inq_resp_len);
scsi_wait_req(sreq, (void *) scsi_cmd,
(void *) inq_result,
possible_inq_resp_len, (1+scsi_inq_timeout)*(HZ/2), 3);
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 2nd INQUIRY"
" %s with code 0x%x\n", sreq->sr_result ?
"failed" : "successful", sreq->sr_result));
if (sreq->sr_result) {
/* if the longer inquiry has failed, flag the device
* as only accepting 36 byte inquiries and retry the
* 36 byte inquiry */
printk(KERN_INFO "scsi scan: %d byte inquiry failed"
" with code %d. Consider BLIST_INQUIRY_36 for"
" this device\n", possible_inq_resp_len,
sreq->sr_result);
*bflags = BLIST_INQUIRY_36;
goto repeat_inquiry;
} }
/* } else if (pass == 2) {
* The INQUIRY can change, this means the length can change. printk(KERN_INFO "scsi scan: %d byte inquiry failed. "
*/ "Consider BLIST_INQUIRY_36 for this device\n",
possible_inq_resp_len = (unsigned char) inq_result[4] + 5; try_inquiry_len);
if (BLIST_INQUIRY_58 & *bflags)
possible_inq_resp_len = 58; /* If this pass failed, the third pass goes back and transfers
else if (possible_inq_resp_len > 255) * the same amount as we successfully got in the first pass. */
possible_inq_resp_len = 36; /* sanity */ try_inquiry_len = first_inquiry_len;
pass = 3;
goto next_pass;
} }
sdev->inquiry_len = possible_inq_resp_len; /* If the last transfer attempt got an error, assume the
* peripheral doesn't exist or is dead. */
if (sreq->sr_result)
return;
/* Don't report any more data than the device says is valid */
sdev->inquiry_len = min(try_inquiry_len, response_len);
/* /*
* XXX Abort if the response length is less than 36? If less than * XXX Abort if the response length is less than 36? If less than
......
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