From 6928192f580e8346816b3ac3bd6870315ae3b991 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 15 Oct 2004 04:20:00 -0500 Subject: [PATCH] [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: Alan Stern Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 171 ++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 81 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 205d1633fdc32..d8685cb6591a7 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -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 -- 2.53.0