]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Let LLD specify INQUIRY length
authorAlan Stern <stern@rowland.harvard.edu>
Fri, 15 Oct 2004 09:20:00 +0000 (04:20 -0500)
committerJames Bottomley <jejb@titanic.il.steeleye.com>
Fri, 15 Oct 2004 09:20:00 +0000 (04:20 -0500)
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: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
drivers/scsi/scsi_scan.c

index 205d1633fdc32f42c81a35947786afc8105790e9..d8685cb6591a7dd0d9f5ae5389f1abdf5f347282 100644 (file)
@@ -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 */
        unsigned char scsi_cmd[MAX_COMMAND_SIZE];
-       int possible_inq_resp_len;
-       int count = 0;
+       int first_inquiry_len, try_inquiry_len, next_inquiry_len;
+       int response_len = 0;
+       int pass, count;
 
        *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);
-       scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, 36,
-                     HZ/2 + HZ*scsi_inq_timeout, 3);
+       /* 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);
+               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"
-                       " code 0x%x\n", sreq->sr_result ?
-                       "failed" : "successful", sreq->sr_result));
-       ++count;
+               memset(inq_result, 0, try_inquiry_len);
+               scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result,
+                               try_inquiry_len,
+                               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 */
                        /* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */
-                       /* Supposedly, but many buggy devices do so anyway */
-                       if (count < 3)
-                               goto repeat_inquiry;
+                       /* Supposedly, but many buggy devices do so anyway. */
+                       if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
+                                       (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;
                }
-               /*
-                * assume no peripheral if any other sort of error
-                */
-               return;
+               break;
        }
 
-       /*
-        * 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.
-        */
-       *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 (sreq->sr_result == 0) {
+               response_len = (unsigned char) inq_result[4] + 5;
+               if (response_len > 255)
+                       response_len = first_inquiry_len;       /* 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);
-               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;
+               *bflags = scsi_get_device_flags(sdev, &inq_result[8],
+                               &inq_result[16]);
+
+               /* When the first pass succeeds we gain information about
+                * what larger transfer lengths might work. */
+               if (pass == 1) {
+                       if (BLIST_INQUIRY_36 & *bflags)
+                               next_inquiry_len = 36;
+                       else if (BLIST_INQUIRY_58 & *bflags)
+                               next_inquiry_len = 58;
+                       else if (sdev->inquiry_len)
+                               next_inquiry_len = sdev->inquiry_len;
+                       else
+                               next_inquiry_len = response_len;
+
+                       /* If more data is available perform the second pass */
+                       if (next_inquiry_len > try_inquiry_len) {
+                               try_inquiry_len = next_inquiry_len;
+                               pass = 2;
+                               goto next_pass;
+                       }
                }
 
-               /*
-                * The INQUIRY can change, this means the length can change.
-                */
-               possible_inq_resp_len = (unsigned char) inq_result[4] + 5;
-               if (BLIST_INQUIRY_58 & *bflags)
-                       possible_inq_resp_len = 58;
-               else if (possible_inq_resp_len > 255)
-                       possible_inq_resp_len = 36;     /* sanity */
+       } else if (pass == 2) {
+               printk(KERN_INFO "scsi scan: %d byte inquiry failed.  "
+                               "Consider BLIST_INQUIRY_36 for this device\n",
+                               try_inquiry_len);
+
+               /* If this pass failed, the third pass goes back and transfers
+                * the same amount as we successfully got in the first pass. */
+               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