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));
memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY;
scsi_cmd[4] = 36; /* issue conservative alloc_length */
sreq->sr_cmd_len = 0;
sreq->sr_data_direction = DMA_FROM_DEVICE;
memset(inq_result, 0, 36); /* Perform up to 3 passes. The first pass uses a conservative
scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, 36, * transfer length of 36 unless sdev->inquiry_len specifies a
HZ/2 + HZ*scsi_inq_timeout, 3); * 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);
scsi_cmd[0] = INQUIRY;
scsi_cmd[4] = (unsigned char) try_inquiry_len;
sreq->sr_cmd_len = 0;
sreq->sr_data_direction = DMA_FROM_DEVICE;
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 1st INQUIRY %s with" memset(inq_result, 0, try_inquiry_len);
" code 0x%x\n", sreq->sr_result ? scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result,
"failed" : "successful", sreq->sr_result)); try_inquiry_len,
++count; HZ/2 + HZ*scsi_inq_timeout, 3);
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
"with code 0x%x\n",
sreq->sr_result ? "failed" : "successful",
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) {
* Get any flags for this device. response_len = (unsigned char) inq_result[4] + 5;
* if (response_len > 255)
* XXX add a bflags to Scsi_Device, and replace the corresponding response_len = first_inquiry_len; /* sanity */
* bit fields in Scsi_Device, so bflags need not be passed as an
* argument.
*/
*bflags |= scsi_get_device_flags(sdev, &inq_result[8], &inq_result[16]);
possible_inq_resp_len = (unsigned char) inq_result[4] + 5;
if (BLIST_INQUIRY_36 & *bflags)
possible_inq_resp_len = 36;
else if (BLIST_INQUIRY_58 & *bflags)
possible_inq_resp_len = 58;
else if (possible_inq_resp_len > 255)
possible_inq_resp_len = 36; /* sanity */
if (possible_inq_resp_len > 36) { /* do additional INQUIRY */
memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY;
scsi_cmd[4] = (unsigned char) possible_inq_resp_len;
sreq->sr_cmd_len = 0;
sreq->sr_data_direction = DMA_FROM_DEVICE;
/* /*
* re-zero inq_result just to be safe. * Get any flags for this device.
*
* XXX add a bflags to Scsi_Device, and replace the
* corresponding bit fields in Scsi_Device, so bflags
* need not be passed as an argument.
*/ */
memset(inq_result, 0, possible_inq_resp_len); *bflags = scsi_get_device_flags(sdev, &inq_result[8],
scsi_wait_req(sreq, (void *) scsi_cmd, &inq_result[16]);
(void *) inq_result,
possible_inq_resp_len, (1+scsi_inq_timeout)*(HZ/2), 3); /* When the first pass succeeds we gain information about
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 2nd INQUIRY" * what larger transfer lengths might work. */
" %s with code 0x%x\n", sreq->sr_result ? if (pass == 1) {
"failed" : "successful", sreq->sr_result)); if (BLIST_INQUIRY_36 & *bflags)
if (sreq->sr_result) { next_inquiry_len = 36;
/* if the longer inquiry has failed, flag the device else if (BLIST_INQUIRY_58 & *bflags)
* as only accepting 36 byte inquiries and retry the next_inquiry_len = 58;
* 36 byte inquiry */ else if (sdev->inquiry_len)
printk(KERN_INFO "scsi scan: %d byte inquiry failed" next_inquiry_len = sdev->inquiry_len;
" with code %d. Consider BLIST_INQUIRY_36 for" else
" this device\n", possible_inq_resp_len, next_inquiry_len = response_len;
sreq->sr_result);
*bflags = BLIST_INQUIRY_36; /* If more data is available perform the second pass */
goto repeat_inquiry; if (next_inquiry_len > try_inquiry_len) {
try_inquiry_len = next_inquiry_len;
pass = 2;
goto next_pass;
}
} }
/* } 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