Binary files linux-2.4.21.replun2/drivers/scsi/.scsi_scan.c.swp and linux-2.4.21.replun3/drivers/scsi/.scsi_scan.c.swp differ diff -uNr linux-2.4.21.replun2/drivers/scsi/scsi_scan.c linux-2.4.21.replun3/drivers/scsi/scsi_scan.c --- linux-2.4.21.replun2/drivers/scsi/scsi_scan.c 2003-08-24 21:17:37.000000000 +0200 +++ linux-2.4.21.replun3/drivers/scsi/scsi_scan.c 2003-08-24 21:17:59.000000000 +0200 @@ -47,6 +47,10 @@ #endif #define BLIST_NOREPLUN 0x2000 /* do not try to use REPORT_LUNS when scanning */ +typedef struct scsi_lun { + u8 scsi_lun[8]; +} ScsiLun; + static void print_inquiry(unsigned char *data); static int scan_scsis_single(unsigned int channel, unsigned int dev, unsigned int lun, int lun0_scsi_level, @@ -54,6 +58,9 @@ Scsi_Device ** SDpnt, char *scsi_result); static int find_lun0_scsi_level(unsigned int channel, unsigned int dev, struct Scsi_Host *shpnt); +static int scsi_send_replun(Scsi_Device *SDpnt, unsigned int channel, + unsigned int target_id, + struct scsi_lun* lun_data, int length); struct dev_info { const char *vendor; @@ -249,6 +256,17 @@ static int scsi_sparselun; static int scsi_largelun; static int max_scsi_sparseluns; +static int scsi_noreportlun; +static int scsi_reportlun2; +/* + * max_report_luns: the maximum number of LUNS that will be + * returned from the REPORT LUNS command. 8 times this value must + * be allocated. In theory this could be up to an 8 byte value, but + * in practice, the maximum number of LUNs suppored by any device + * is about 16k. + */ +static unsigned int max_report_luns = 511; + #ifdef MODULE @@ -267,6 +285,16 @@ MODULE_PARM(max_scsi_sparseluns, "i"); MODULE_PARM_DESC(max_scsi_sparseluns, "Limit LUNs for scanning sparse LUN devices"); +MODULE_PARM(scsi_noreportlun, "i"); +MODULE_PARM_DESC(scsi_noreportlun, "Don't use REPORT_LUNs for scanning SCSI-3 devs"); + +MODULE_PARM(scsi_reportlun2, "i"); +MODULE_PARM_DESC(scsi_reportlun, "Use REPORT_LUNs for scanning SCSI-2 devs as well"); + +MODULE_PARM(max_report_luns, "i"); +MODULE_PARM_DESC(max_report_luns, + "REPORT LUNS maximum number of LUNS received (should be" + " between 1 and 16383)"); #else static int __init scsi_luns_setup(char *str) @@ -355,6 +383,52 @@ __setup("max_scsi_sparseluns=", scsi_max_sparseluns_setup); +static int __init scsi_noreplun_setup(char *str) +{ + unsigned int tmp; + + if (get_option(&str, &tmp) == 1) { + scsi_noreportlun = tmp; + return 1; + } else { + scsi_noreportlun = 1; + return 0; + } +} + +__setup("scsi_noreportlun=", scsi_noreplun_setup); + +static int __init scsi_replun2_setup(char *str) +{ + unsigned int tmp; + + if (get_option(&str, &tmp) == 1) { + scsi_reportlun2 = tmp; + return 1; + } else { + scsi_reportlun2 = 1; + return 0; + } +} + +__setup("scsi_reportlun2=", scsi_replun2_setup); + +static int __init scsi_max_reportluns_setup(char *str) +{ + unsigned int tmp; + + if (get_option(&str, &tmp) == 1) { + max_report_luns = tmp; + return 1; + } else { + printk("scsi_max_reportluns_setup : usage max_report_luns=n " + "(n should be between 1 and 16383)\n"); + return 0; + } +} + +__setup("max_report_luns=", scsi_max_reportluns_setup); + #endif static void print_inquiry(unsigned char *data) @@ -598,16 +672,28 @@ kfree(scsi_result); } + +static int scsilun_to_int(struct scsi_lun *scsilun) +{ + int i; + unsigned int lun; + + lun = 0; + for (i = 0; i < sizeof(lun); i += 2) + lun = lun | (((scsilun->scsi_lun[i] << 8) | + scsilun->scsi_lun[i + 1]) << (i * 8)); + return lun; +} + static void scsi_scan_target(Scsi_Device **SDpnt2, unsigned char* scsi_result, unsigned int channel, unsigned int dev) { /* Actual ID. PC ordering is 0->n IBM/spec ordering is n->0 */ int target_id; - int lun0_sl = SCSI_2; + int lun0_sl = SCSI_2, bflags; unsigned int lun; unsigned int sparse_lun = 0; - Scsi_Device *SDpnt = *SDpnt2; - struct Scsi_Host *shpnt = SDpnt->host; + struct Scsi_Host *shpnt = (*SDpnt2)->host; /* The minimum */ unsigned int max_dev_lun = (max_scsi_luns < shpnt->max_lun ? max_scsi_luns : shpnt->max_lun); @@ -622,32 +708,104 @@ if (shpnt->this_id == target_id) return; - /* - * TODO: First scan for LUN 0, then decide what to do next. - * TODO: Handle target present but without device. - * TODO: REPORT_LUNS - */ - for (lun = 0; lun < max_dev_lun; ++lun) { - /* don't probe further for luns > 7 for targets <= SCSI_2 */ - if ((lun0_sl < SCSI_3) && (lun > 7) && !scsi_largelun) - break; + /* OK, first check whether there is a device */ + if (!scan_scsis_single(channel, target_id, 0, SCSI_2, + &max_dev_lun, &sparse_lun, SDpnt2, + scsi_result)) + return; + if (!*SDpnt2) + return; + bflags = get_device_flags(scsi_result); + if (bflags & BLIST_NOLUN) + return; + + lun0_sl = (*SDpnt2)->scsi_level; + if (!(bflags & BLIST_NOREPLUN) + && ( (!scsi_noreportlun && lun0_sl >= SCSI_3) + ||( scsi_reportlun2 && lun0_sl >= SCSI_2 + && shpnt->max_lun > 8))) { + /* Send REPORT_LUNS */ + int replun_buflen = (max_report_luns + 1) * sizeof(struct scsi_lun); + int num_luns; + struct scsi_lun *lunp; + struct scsi_lun *lun_data = kmalloc(replun_buflen, GFP_ATOMIC | + ((*SDpnt2)->host->unchecked_isa_dma ? __GFP_DMA : 0)); + unsigned char* data; + if (!lun_data) { + printk ("scsi: Allocation of REPORT_LEN buffer failed!\n"); + goto serial_scan; + } + memset(lun_data, 0, replun_buflen); + num_luns = scsi_send_replun(*SDpnt2, channel, target_id, + lun_data, replun_buflen); + if (num_luns == -1) + goto serial_scan_freebuf; + /* OK, successful, scan for the LUNs reported */ + SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUN scan of" + " host %d channel %d id %d\n", shpnt->host_no, + channel, target_id)); + + for (lunp = &lun_data[1]; lunp <= &lun_data[num_luns]; ++lunp) { + lun = scsilun_to_int(lunp); + if (memcmp(&lunp->scsi_lun[sizeof(lun)], "\0\0\0\0", 4)) { + int i; + printk(KERN_WARNING "scsi: host %i ch %i id %i lun 0x", + shpnt->host_no, channel, target_id); + data = (unsigned char *)lunp->scsi_lun; + for (i = 0; i < sizeof(struct scsi_lun); ++i) + printk("%02x", data[i]); + printk(" has a LUN larger than currently supported.\n"); + } else if (lun == 0) { + /* LUN 0 has already been scanned. */ + } else if (lun > max_dev_lun) { + printk(KERN_WARNING "scsi: host %i ch %i id %i lun %d has a LUN larger" + " than allowed by %s\n", + shpnt->host_no, channel, target_id, lun, + (shpnt->max_lun <= max_scsi_luns + ? "the host adapter" + : "max_scsi_luns") ); + } else { + int res; + + res = scan_scsis_single(channel, target_id, lun, lun0_sl, + &max_dev_lun, &sparse_lun, SDpnt2, + scsi_result); + if (res == 0) { + /* Got some results, but now none, abort. */ + printk(KERN_ERR "scsi: Unexpected response" + " from host %i ch %i id %i lun %d while scanning, scan" + " aborted\n", shpnt->host_no, channel, target_id, lun); + break; + } + } + } + + + kfree(lun_data); + return; + serial_scan_freebuf: + kfree(lun_data); + } + + serial_scan: + if (lun0_sl < SCSI_3 && + (bflags & BLIST_LARGELUN || + is_on_llun_blklst (shpnt->host_no, channel, target_id))) + lun0_sl = SCSI_3; /* treat as SCSI 3 */ + + if (lun0_sl < SCSI_3 && !scsi_largelun && max_dev_lun > 8) + max_dev_lun = 8; + + for (lun = 1; lun < max_dev_lun; ++lun) { if (!scan_scsis_single(channel, target_id, lun, lun0_sl, - &max_dev_lun, &sparse_lun, &SDpnt, + &max_dev_lun, &sparse_lun, SDpnt2, scsi_result) - && !sparse_lun && (lun == 0 || !scsi_sparselun)) + && !sparse_lun && !scsi_sparselun) + break; + if (!*SDpnt2) break; - - if (SDpnt && (0 == lun)) { - int bflags = get_device_flags (scsi_result); - if (bflags & BLIST_LARGELUN || - is_on_llun_blklst (shpnt->host_no, channel, target_id)) - lun0_sl = SCSI_3; /* treat as SCSI 3 */ - else - lun0_sl = SDpnt->scsi_level; - } } /* for lun ends */ - *SDpnt2 = SDpnt; } @@ -767,9 +922,10 @@ } + /* Send one or two INQUIRIES, get device_flags and return 0 on success */ -int scsi_probe_lun(Scsi_Request *SRpnt, char* scsi_result, - int *bflags, int inq_lun) +static int scsi_probe_lun(Scsi_Request *SRpnt, char* scsi_result, + int *bflags, int inq_lun) { int inq_len; /* @@ -971,6 +1127,86 @@ return SRpnt->sr_result; } +#ifndef REPORT_LUNS +# define REPORT_LUNS 0xa0 +#endif + +/* Returns -1 on failure, num_lun on success */ +static int scsi_send_replun(Scsi_Device *SDpnt, unsigned int channel, + unsigned int target_id, + struct scsi_lun* lun_data, int length) +{ + char scsi_cmd[MAX_COMMAND_SIZE]; + int retries, result; + unsigned int num_luns; + unsigned char *data = (unsigned char*)lun_data->scsi_lun; + + Scsi_Request *SRpnt = Scsi_Device_Init(SDpnt, channel, target_id, 0); + if (!SRpnt) + return -1; + + scsi_cmd[0] = REPORT_LUNS; + /* bytes 1 - 5: reserved, set to zero. */ + memset(scsi_cmd+1, 0, 5); + /* bytes 6 - 9: length of the command. */ + scsi_cmd[6] = (unsigned char) (length >> 24) & 0xff; + scsi_cmd[7] = (unsigned char) (length >> 16) & 0xff; + scsi_cmd[8] = (unsigned char) (length >> 8) & 0xff; + scsi_cmd[9] = (unsigned char) length & 0xff; + + scsi_cmd[10] = 0; /* reserved */ + scsi_cmd[11] = 0; /* control */ + SRpnt->sr_cmd_len = 0; + SRpnt->sr_data_direction = SCSI_DATA_READ; + + /* + * We can get a UNIT ATTENTION, for example a power on/reset, so + * retry a few times (like sd.c does for TEST UNIT READY). + * Experience shows some combinations of adapter/devices get at + * least two power on/resets. + * + * Illegal requests (for devices that do not support REPORT LUNS) + * should come through as a check condition, and will not generate + * a retry. + */ + for (retries = 0; retries < 3; ++retries) { + SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending" + " REPORT LUNS to host %i ch %i id %i (try %d)\n", + SDpnt->host->host_no, SDpnt->channel, SDpnt->id, + retries)); + scsi_wait_req(SRpnt, scsi_cmd, lun_data, length, + SCSI_TIMEOUT + 2*HZ, 3); + SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS" + " %s (try %d) result 0x%x\n", SRpnt->sr_result + ? "failed" : "successful", retries, + SRpnt->sr_result)); + if ((result = SRpnt->sr_result) == 0 || + SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION) + break; + } + + scsi_release_request(SRpnt); + scsi_release_commandblocks(SDpnt); + if (result) + return -1; + + length = ((data[0] << 24) | (data[1] << 16) | + (data[2] << 8) | (data[3] << 0)); + + num_luns = (length / sizeof(struct scsi_lun)); + if (num_luns > max_report_luns) { + printk(KERN_WARNING "scsi: On host %i ch %i id %i only %d (max_report_luns)" + " of %d luns reported, try increasing" + " max_report_luns.\n", + SDpnt->host->host_no, SDpnt->channel, SDpnt->id, + max_report_luns, num_luns); + num_luns = max_report_luns; + } + + return num_luns; +} + + /* * The worker for scan_scsis. * TODO: Returning 0 means No response,