X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi_scan.c;h=1a5474bd11a19d0eb32f434d2556ee64b1bb3c52;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=9d4d3ce9e92e86c54b020e67a1d497d198d12631;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 9d4d3ce9e..1a5474bd1 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -9,7 +9,7 @@ * global variable (boot or module load time) settings. * * A specific LUN is scanned via an INQUIRY command; if the LUN has a - * device attached, a Scsi_Device is allocated and setup for it. + * device attached, a scsi_device is allocated and setup for it. * * For every id of every channel on the given host: * @@ -17,7 +17,7 @@ * device or storage attached to LUN 0): * * If LUN 0 has a device attached, allocate and setup a - * Scsi_Device for it. + * scsi_device for it. * * If target is SCSI-3 or up, issue a REPORT LUN, and scan * all of the LUNs returned by the REPORT LUN; else, @@ -39,6 +39,7 @@ #include #include #include +#include #include "scsi_priv.h" #include "scsi_logging.h" @@ -73,7 +74,7 @@ #define SCSI_SCAN_TARGET_PRESENT 1 #define SCSI_SCAN_LUN_PRESENT 2 -static char *scsi_null_device_strs = "nullnullnullnull"; +static const char *scsi_null_device_strs = "nullnullnullnull"; #define MAX_SCSI_LUNS 512 @@ -110,15 +111,14 @@ MODULE_PARM_DESC(inq_timeout, /** * scsi_unlock_floptical - unlock device via a special MODE SENSE command - * @sreq: used to send the command + * @sdev: scsi device to send command to * @result: area to store the result of the MODE SENSE * * Description: - * Send a vendor specific MODE SENSE (not a MODE SELECT) command using - * @sreq to unlock a device, storing the (unused) results into result. + * Send a vendor specific MODE SENSE (not a MODE SELECT) command. * Called for BLIST_KEY devices. **/ -static void scsi_unlock_floptical(struct scsi_request *sreq, +static void scsi_unlock_floptical(struct scsi_device *sdev, unsigned char *result) { unsigned char scsi_cmd[MAX_COMMAND_SIZE]; @@ -128,11 +128,10 @@ static void scsi_unlock_floptical(struct scsi_request *sreq, scsi_cmd[1] = 0; scsi_cmd[2] = 0x2e; scsi_cmd[3] = 0; - scsi_cmd[4] = 0x2a; /* size */ + scsi_cmd[4] = 0x2a; /* size */ scsi_cmd[5] = 0; - sreq->sr_cmd_len = 0; - sreq->sr_data_direction = DMA_FROM_DEVICE; - scsi_wait_req(sreq, scsi_cmd, result, 0x2a /* size */, SCSI_TIMEOUT, 3); + scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL, + SCSI_TIMEOUT, 3); } /** @@ -199,24 +198,25 @@ static void print_inquiry(unsigned char *inq_result) * Return value: * scsi_Device pointer, or NULL on failure. **/ -static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, - uint channel, uint id, uint lun, void *hostdata) +static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, + unsigned int lun, void *hostdata) { - struct scsi_device *sdev, *device; - unsigned long flags; + struct scsi_device *sdev; + int display_failure_msg = 1, ret; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); - sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC); + sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size, + GFP_ATOMIC); if (!sdev) goto out; - memset(sdev, 0, sizeof(*sdev)); sdev->vendor = scsi_null_device_strs; sdev->model = scsi_null_device_strs; sdev->rev = scsi_null_device_strs; sdev->host = shost; - sdev->id = id; + sdev->id = starget->id; sdev->lun = lun; - sdev->channel = channel; + sdev->channel = starget->channel; sdev->sdev_state = SDEV_CREATED; INIT_LIST_HEAD(&sdev->siblings); INIT_LIST_HEAD(&sdev->same_target_siblings); @@ -224,6 +224,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, INIT_LIST_HEAD(&sdev->starved_entry); spin_lock_init(&sdev->list_lock); + sdev->sdev_gendev.parent = get_device(&starget->dev); + sdev->sdev_target = starget; + /* usually NULL and set by ->slave_alloc instead */ sdev->hostdata = hostdata; @@ -243,209 +246,336 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, */ sdev->borken = 1; - spin_lock_init(&sdev->sdev_lock); sdev->request_queue = scsi_alloc_queue(sdev); - if (!sdev->request_queue) - goto out_free_dev; + if (!sdev->request_queue) { + /* release fn is set up in scsi_sysfs_device_initialise, so + * have to free and put manually here */ + put_device(&starget->dev); + kfree(sdev); + goto out; + } sdev->request_queue->queuedata = sdev; scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + scsi_sysfs_device_initialize(sdev); + if (shost->hostt->slave_alloc) { - if (shost->hostt->slave_alloc(sdev)) - goto out_free_queue; + ret = shost->hostt->slave_alloc(sdev); + if (ret) { + /* + * if LLDD reports slave not present, don't clutter + * console with alloc failure messages + */ + if (ret == -ENXIO) + display_failure_msg = 0; + goto out_device_destroy; + } } - if (shost->transportt->setup) { - if (shost->transportt->setup(sdev)) - goto out_cleanup_slave; - } + return sdev; - if (get_device(&sdev->host->shost_gendev)) { - - device_initialize(&sdev->sdev_gendev); - sdev->sdev_gendev.parent = &sdev->host->shost_gendev; - sdev->sdev_gendev.bus = &scsi_bus_type; - sdev->sdev_gendev.release = scsi_device_dev_release; - sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d", - sdev->host->host_no, sdev->channel, sdev->id, - sdev->lun); - - class_device_initialize(&sdev->sdev_classdev); - sdev->sdev_classdev.dev = &sdev->sdev_gendev; - sdev->sdev_classdev.class = &sdev_class; - snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE, - "%d:%d:%d:%d", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); - - class_device_initialize(&sdev->transport_classdev); - sdev->transport_classdev.dev = &sdev->sdev_gendev; - sdev->transport_classdev.class = sdev->host->transportt->class; - snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE, - "%d:%d:%d:%d", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); - } else - goto out_cleanup_transport; +out_device_destroy: + transport_destroy_device(&sdev->sdev_gendev); + put_device(&sdev->sdev_gendev); +out: + if (display_failure_msg) + printk(ALLOC_FAILURE_MSG, __FUNCTION__); + return NULL; +} +static void scsi_target_dev_release(struct device *dev) +{ + struct device *parent = dev->parent; + struct scsi_target *starget = to_scsi_target(dev); + + kfree(starget); + put_device(parent); +} + +int scsi_is_target_device(const struct device *dev) +{ + return dev->release == scsi_target_dev_release; +} +EXPORT_SYMBOL(scsi_is_target_device); + +static struct scsi_target *__scsi_find_target(struct device *parent, + int channel, uint id) +{ + struct scsi_target *starget, *found_starget = NULL; + struct Scsi_Host *shost = dev_to_shost(parent); /* - * If there are any same target siblings, add this to the - * sibling list + * Search for an existing target for this sdev. */ - spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry(device, &shost->__devices, siblings) { - if (device->id == sdev->id && - device->channel == sdev->channel) { - list_add_tail(&sdev->same_target_siblings, - &device->same_target_siblings); - sdev->scsi_level = device->scsi_level; + list_for_each_entry(starget, &shost->__targets, siblings) { + if (starget->id == id && + starget->channel == channel) { + found_starget = starget; break; } } + if (found_starget) + get_device(&found_starget->dev); - /* - * If there wasn't another lun already configured at this - * target, then default this device to SCSI_2 until we - * know better - */ - if (!sdev->scsi_level) - sdev->scsi_level = SCSI_2; + return found_starget; +} + +static struct scsi_target *scsi_alloc_target(struct device *parent, + int channel, uint id) +{ + struct Scsi_Host *shost = dev_to_shost(parent); + struct device *dev = NULL; + unsigned long flags; + const int size = sizeof(struct scsi_target) + + shost->transportt->target_size; + struct scsi_target *starget; + struct scsi_target *found_target; + int error; - list_add_tail(&sdev->siblings, &shost->__devices); + starget = kzalloc(size, GFP_KERNEL); + if (!starget) { + printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); + return NULL; + } + dev = &starget->dev; + device_initialize(dev); + starget->reap_ref = 1; + dev->parent = get_device(parent); + dev->release = scsi_target_dev_release; + sprintf(dev->bus_id, "target%d:%d:%d", + shost->host_no, channel, id); + starget->id = id; + starget->channel = channel; + INIT_LIST_HEAD(&starget->siblings); + INIT_LIST_HEAD(&starget->devices); + starget->state = STARGET_RUNNING; + retry: + spin_lock_irqsave(shost->host_lock, flags); + + found_target = __scsi_find_target(parent, channel, id); + if (found_target) + goto found; + + list_add_tail(&starget->siblings, &shost->__targets); spin_unlock_irqrestore(shost->host_lock, flags); - return sdev; + /* allocate and add */ + transport_setup_device(dev); + error = device_add(dev); + if (error) { + dev_err(dev, "target device_add failed, error %d\n", error); + spin_lock_irqsave(shost->host_lock, flags); + list_del_init(&starget->siblings); + spin_unlock_irqrestore(shost->host_lock, flags); + transport_destroy_device(dev); + put_device(parent); + kfree(starget); + return NULL; + } + transport_add_device(dev); + if (shost->hostt->target_alloc) { + error = shost->hostt->target_alloc(starget); + + if(error) { + dev_printk(KERN_ERR, dev, "target allocation failed, error %d\n", error); + /* don't want scsi_target_reap to do the final + * put because it will be under the host lock */ + get_device(dev); + scsi_target_reap(starget); + put_device(dev); + return NULL; + } + } -out_cleanup_transport: - if (shost->transportt->cleanup) - shost->transportt->cleanup(sdev); -out_cleanup_slave: - if (shost->hostt->slave_destroy) - shost->hostt->slave_destroy(sdev); -out_free_queue: - scsi_free_queue(sdev->request_queue); -out_free_dev: - kfree(sdev); -out: - printk(ALLOC_FAILURE_MSG, __FUNCTION__); - return NULL; + return starget; + + found: + found_target->reap_ref++; + spin_unlock_irqrestore(shost->host_lock, flags); + put_device(parent); + if (found_target->state != STARGET_DEL) { + kfree(starget); + return found_target; + } + /* Unfortunately, we found a dying target; need to + * wait until it's dead before we can get a new one */ + put_device(&found_target->dev); + flush_scheduled_work(); + goto retry; +} + +static void scsi_target_reap_usercontext(void *data) +{ + struct scsi_target *starget = data; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + unsigned long flags; + + transport_remove_device(&starget->dev); + device_del(&starget->dev); + transport_destroy_device(&starget->dev); + spin_lock_irqsave(shost->host_lock, flags); + if (shost->hostt->target_destroy) + shost->hostt->target_destroy(starget); + list_del_init(&starget->siblings); + spin_unlock_irqrestore(shost->host_lock, flags); + put_device(&starget->dev); +} + +/** + * scsi_target_reap - check to see if target is in use and destroy if not + * + * @starget: target to be checked + * + * This is used after removing a LUN or doing a last put of the target + * it checks atomically that nothing is using the target and removes + * it if so. + */ +void scsi_target_reap(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + unsigned long flags; + + spin_lock_irqsave(shost->host_lock, flags); + + if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { + BUG_ON(starget->state == STARGET_DEL); + starget->state = STARGET_DEL; + spin_unlock_irqrestore(shost->host_lock, flags); + execute_in_process_context(scsi_target_reap_usercontext, + starget, &starget->ew); + return; + + } + spin_unlock_irqrestore(shost->host_lock, flags); + + return; } /** * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY - * @sreq: used to send the INQUIRY + * @sdev: scsi_device to probe * @inq_result: area to store the INQUIRY result + * @result_len: len of inq_result * @bflags: store any bflags found here * * Description: - * Probe the lun associated with @sreq using a standard SCSI INQUIRY; + * Probe the lun associated with @req using a standard SCSI INQUIRY; * - * If the INQUIRY is successful, sreq->sr_result is zero and: the + * If the INQUIRY is successful, zero is returned and the * INQUIRY data is in @inq_result; the scsi_level and INQUIRY length - * are copied to the Scsi_Device at @sreq->sr_device (sdev); - * any flags value is stored in *@bflags. + * are copied to the scsi_device any flags value is stored in *@bflags. **/ -static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, - int *bflags) +static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, + int result_len, int *bflags) { - 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, result; + struct scsi_sense_hdr sshdr; *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); - - 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; - - 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; - } - /* - * assume no peripheral if any other sort of error - */ - return; - } - /* - * 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]); + /* 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; - 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 */ + next_pass: + SCSI_LOG_SCAN_BUS(3, sdev_printk(KERN_INFO, sdev, + "scsi scan: INQUIRY pass %d length %d\n", + pass, try_inquiry_len)); - if (possible_inq_resp_len > 36) { /* do additional INQUIRY */ + /* 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) possible_inq_resp_len; - sreq->sr_cmd_len = 0; - 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; + scsi_cmd[4] = (unsigned char) try_inquiry_len; + + memset(inq_result, 0, try_inquiry_len); + + result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, + inq_result, try_inquiry_len, &sshdr, + HZ / 2 + HZ * scsi_inq_timeout, 3); + + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s " + "with code 0x%x\n", + result ? "failed" : "successful", result)); + + if (result) { + /* + * not-ready to ready transition [asc/ascq=0x28/0x0] + * or power-on, reset [asc/ascq=0x29/0x0], continue. + * INQUIRY should not yield UNIT_ATTENTION + * but many buggy devices do so anyway. + */ + if ((driver_byte(result) & DRIVER_SENSE) && + scsi_sense_valid(&sshdr)) { + if ((sshdr.sense_key == UNIT_ATTENTION) && + ((sshdr.asc == 0x28) || + (sshdr.asc == 0x29)) && + (sshdr.ascq == 0)) + continue; + } } + break; + } + + if (result == 0) { + response_len = (unsigned char) inq_result[4] + 5; + if (response_len > 255) + response_len = first_inquiry_len; /* sanity */ /* - * The INQUIRY can change, this means the length can change. + * 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. */ - 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 */ + *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; + } + } + + } 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 (result) + return -EIO; + + /* 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 @@ -477,33 +607,30 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, if (sdev->scsi_level >= 2 || (sdev->scsi_level == 1 && (inq_result[3] & 0x0f) == 1)) sdev->scsi_level++; + sdev->sdev_target->scsi_level = sdev->scsi_level; - return; + return 0; } /** - * scsi_add_lun - allocate and fully initialze a Scsi_Device - * @sdevscan: holds information to be stored in the new Scsi_Device - * @sdevnew: store the address of the newly allocated Scsi_Device + * scsi_add_lun - allocate and fully initialze a scsi_device + * @sdevscan: holds information to be stored in the new scsi_device + * @sdevnew: store the address of the newly allocated scsi_device * @inq_result: holds the result of a previous INQUIRY to the LUN * @bflags: black/white list flag * * Description: - * Allocate and initialize a Scsi_Device matching sdevscan. Optionally + * Allocate and initialize a scsi_device matching sdevscan. Optionally * set fields based on values in *@bflags. If @sdevnew is not - * NULL, store the address of the new Scsi_Device in *@sdevnew (needed + * NULL, store the address of the new scsi_device in *@sdevnew (needed * when scanning a particular LUN). * * Return: - * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device - * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized + * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a scsi_device + * SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized **/ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) { - struct scsi_device *sdev_sibling; - struct scsi_target *starget; - unsigned long flags; - /* * XXX do not save the inquiry, since it can change underneath us, * save just vendor/model/rev. @@ -533,7 +660,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) */ inq_result[0] = TYPE_ROM; inq_result[1] |= 0x80; /* removable */ - } + } else if (*bflags & BLIST_NO_ULD_ATTACH) + sdev->no_uld_attach = 1; switch (sdev->type = (inq_result[0] & 0x1f)) { case TYPE_TAPE: @@ -545,6 +673,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) case TYPE_MEDIUM_CHANGER: case TYPE_ENCLOSURE: case TYPE_COMM: + case TYPE_RAID: + case TYPE_RBC: sdev->writeable = 1; break; case TYPE_WORM: @@ -566,7 +696,7 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) * * The above is vague, as it implies that we could treat 001 and * 011 the same. Stay compatible with previous code, and create a - * Scsi_Device for a PQ of 1 + * scsi_device for a PQ of 1 * * Don't set the device offline here; rather let the upper * level drivers eval the PQ to decide whether they should @@ -586,12 +716,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if (inq_result[7] & 0x10) sdev->sdtr = 1; - sprintf(sdev->devfs_name, "scsi/host%d/bus%d/target%d/lun%d", - sdev->host->host_no, sdev->channel, - sdev->id, sdev->lun); - /* - * End driverfs/devfs code. + * End sysfs code. */ if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) && @@ -605,6 +731,20 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if ((*bflags & BLIST_BORKEN) == 0) sdev->borken = 0; + /* + * Apparently some really broken devices (contrary to the SCSI + * standards) need to be selected without asserting ATN + */ + if (*bflags & BLIST_SELECT_NO_ATN) + sdev->select_no_atn = 1; + + /* + * Maximum 512 sector transfer length + * broken RA4x00 Compaq Disk Array + */ + if (*bflags & BLIST_MAX_512) + blk_queue_max_sectors(sdev->request_queue, 512); + /* * Some devices may not want to have a start command automatically * issued when a device is added. @@ -612,40 +752,9 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if (*bflags & BLIST_NOSTARTONADD) sdev->no_start_on_add = 1; - /* - * If we need to allow I/O to only one of the luns attached to - * this target id at a time set single_lun, and allocate or modify - * sdev_target. - */ - if (*bflags & BLIST_SINGLELUN) { + if (*bflags & BLIST_SINGLELUN) sdev->single_lun = 1; - spin_lock_irqsave(sdev->host->host_lock, flags); - starget = NULL; - /* - * Search for an existing target for this sdev. - */ - list_for_each_entry(sdev_sibling, &sdev->same_target_siblings, - same_target_siblings) { - if (sdev_sibling->sdev_target != NULL) { - starget = sdev_sibling->sdev_target; - break; - } - } - if (!starget) { - starget = kmalloc(sizeof(*starget), GFP_ATOMIC); - if (!starget) { - printk(ALLOC_FAILURE_MSG, __FUNCTION__); - spin_unlock_irqrestore(sdev->host->host_lock, - flags); - return SCSI_SCAN_NO_RESPONSE; - } - starget->starget_refcnt = 0; - starget->starget_sdev_user = NULL; - } - starget->starget_refcnt++; - sdev->sdev_target = starget; - spin_unlock_irqrestore(sdev->host->host_lock, flags); - } + sdev->use_10_for_rw = 1; @@ -668,23 +777,52 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if (*bflags & BLIST_NOT_LOCKABLE) sdev->lockable = 0; - if(sdev->host->hostt->slave_configure) - sdev->host->hostt->slave_configure(sdev); + if (*bflags & BLIST_RETRY_HWERROR) + sdev->retry_hwerror = 1; + + transport_configure_device(&sdev->sdev_gendev); + + if (sdev->host->hostt->slave_configure) { + int ret = sdev->host->hostt->slave_configure(sdev); + if (ret) { + /* + * if LLDD reports slave not present, don't clutter + * console with alloc failure messages + */ + if (ret != -ENXIO) { + sdev_printk(KERN_ERR, sdev, + "failed to configure device\n"); + } + return SCSI_SCAN_NO_RESPONSE; + } + } /* * Ok, the device is now all set up, we can * register it and tell the rest of the kernel * about it. */ - scsi_sysfs_add_sdev(sdev); + if (scsi_sysfs_add_sdev(sdev) != 0) + return SCSI_SCAN_NO_RESPONSE; return SCSI_SCAN_LUN_PRESENT; } +static inline void scsi_destroy_sdev(struct scsi_device *sdev) +{ + if (sdev->host->hostt->slave_destroy) + sdev->host->hostt->slave_destroy(sdev); + transport_destroy_device(&sdev->sdev_gendev); + put_device(&sdev->sdev_gendev); +} + + /** * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it - * @sdevscan: probe the LUN corresponding to this Scsi_Device - * @sdevnew: store the value of any new Scsi_Device allocated + * @starget: pointer to target device structure + * @lun: LUN of target device + * @sdevscan: probe the LUN corresponding to this scsi_device + * @sdevnew: store the value of any new scsi_device allocated * @bflagsp: store bflags here if not NULL * * Description: @@ -692,55 +830,54 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) * allocate and set it up by calling scsi_add_lun. * * Return: - * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device + * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a scsi_device * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is * attached at the LUN - * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized + * SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized **/ -static int scsi_probe_and_add_lun(struct Scsi_Host *host, - uint channel, uint id, uint lun, int *bflagsp, - struct scsi_device **sdevp, int rescan, void *hostdata) +static int scsi_probe_and_add_lun(struct scsi_target *starget, + uint lun, int *bflagsp, + struct scsi_device **sdevp, int rescan, + void *hostdata) { struct scsi_device *sdev; - struct scsi_request *sreq; unsigned char *result; - int bflags, res = SCSI_SCAN_NO_RESPONSE; + int bflags, res = SCSI_SCAN_NO_RESPONSE, result_len = 256; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); /* * The rescan flag is used as an optimization, the first scan of a * host adapter calls into here with rescan == 0. */ - if (rescan) { - sdev = scsi_device_lookup(host, channel, id, lun); - if (sdev) { + sdev = scsi_device_lookup_by_target(starget, lun); + if (sdev) { + if (rescan || sdev->sdev_state != SDEV_CREATED) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO - "scsi scan: device exists on <%d:%d:%d:%d>\n", - host->host_no, channel, id, lun)); + "scsi scan: device exists on %s\n", + sdev->sdev_gendev.bus_id)); if (sdevp) *sdevp = sdev; + else + scsi_device_put(sdev); + if (bflagsp) *bflagsp = scsi_get_device_flags(sdev, sdev->vendor, sdev->model); - /* XXX: bandaid until callers do refcounting */ - scsi_device_put(sdev); return SCSI_SCAN_LUN_PRESENT; } - } - - sdev = scsi_alloc_sdev(host, channel, id, lun, hostdata); + scsi_device_put(sdev); + } else + sdev = scsi_alloc_sdev(starget, lun, hostdata); if (!sdev) goto out; - sreq = scsi_allocate_request(sdev, GFP_ATOMIC); - if (!sreq) - goto out_free_sdev; - result = kmalloc(256, GFP_ATOMIC | - (host->unchecked_isa_dma) ? __GFP_DMA : 0); + + result = kmalloc(result_len, GFP_ATOMIC | + ((shost->unchecked_isa_dma) ? __GFP_DMA : 0)); if (!result) - goto out_free_sreq; + goto out_free_sdev; - scsi_probe_lun(sreq, result, &bflags); - if (sreq->sr_result) + if (scsi_probe_lun(sdev, result, result_len, &bflags)) goto out_free_result; /* @@ -764,11 +901,24 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, goto out_free_result; } + /* + * Non-standard SCSI targets may set the PDT to 0x1f (unknown or + * no device type) instead of using the Peripheral Qualifier to + * indicate that no LUN is present. For example, USB UFI does this. + */ + if (starget->pdt_1f_for_no_lun && (result[0] & 0x1f) == 0x1f) { + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO + "scsi scan: peripheral device type" + " of 31, no device added\n")); + res = SCSI_SCAN_TARGET_PRESENT; + goto out_free_result; + } + res = scsi_add_lun(sdev, result, &bflags); if (res == SCSI_SCAN_LUN_PRESENT) { if (bflags & BLIST_KEY) { sdev->lockable = 0; - scsi_unlock_floptical(sreq, result); + scsi_unlock_floptical(sdev, result); } if (bflagsp) *bflagsp = bflags; @@ -776,26 +926,25 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, out_free_result: kfree(result); - out_free_sreq: - scsi_release_request(sreq); out_free_sdev: if (res == SCSI_SCAN_LUN_PRESENT) { - if (sdevp) - *sdevp = sdev; - } else { - if (sdev->host->hostt->slave_destroy) - sdev->host->hostt->slave_destroy(sdev); - if (sdev->host->transportt->cleanup) - sdev->host->transportt->cleanup(sdev); - put_device(&sdev->sdev_gendev); - } + if (sdevp) { + if (scsi_device_get(sdev) == 0) { + *sdevp = sdev; + } else { + __scsi_remove_device(sdev); + res = SCSI_SCAN_NO_RESPONSE; + } + } + } else + scsi_destroy_sdev(sdev); out: return res; } /** * scsi_sequential_lun_scan - sequentially scan a SCSI target - * @sdevscan: scan the host, channel, and id of this Scsi_Device + * @starget: pointer to target structure to scan * @bflags: black/white list flag for LUN 0 * @lun0_res: result of scanning LUN 0 * @@ -806,14 +955,15 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, * * Modifies sdevscan->lun. **/ -static void scsi_sequential_lun_scan(struct Scsi_Host *shost, uint channel, - uint id, int bflags, int lun0_res, int scsi_level, int rescan) +static void scsi_sequential_lun_scan(struct scsi_target *starget, + int bflags, int lun0_res, int scsi_level, + int rescan) { unsigned int sparse_lun, lun, max_dev_lun; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of" - " host %d channel %d id %d\n", shost->host_no, - channel, id)); + "%s\n", starget->dev.bus_id)); max_dev_lun = min(max_scsi_luns, shost->max_lun); /* @@ -875,8 +1025,8 @@ static void scsi_sequential_lun_scan(struct Scsi_Host *shost, uint channel, * sparse_lun. */ for (lun = 1; lun < max_dev_lun; ++lun) - if ((scsi_probe_and_add_lun(shost, channel, id, lun, - NULL, NULL, rescan, NULL) != SCSI_SCAN_LUN_PRESENT) && + if ((scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, + NULL) != SCSI_SCAN_LUN_PRESENT) && !sparse_lun) return; } @@ -912,9 +1062,41 @@ static int scsilun_to_int(struct scsi_lun *scsilun) return lun; } +/** + * int_to_scsilun: reverts an int into a scsi_lun + * @int: integer to be reverted + * @scsilun: struct scsi_lun to be set. + * + * Description: + * Reverts the functionality of the scsilun_to_int, which packed + * an 8-byte lun value into an int. This routine unpacks the int + * back into the lun value. + * Note: the scsilun_to_int() routine does not truly handle all + * 8bytes of the lun value. This functions restores only as much + * as was set by the routine. + * + * Notes: + * Given an integer : 0x0b030a04, this function returns a + * scsi_lun of : struct scsi_lun of: 0a 04 0b 03 00 00 00 00 + * + **/ +void int_to_scsilun(unsigned int lun, struct scsi_lun *scsilun) +{ + int i; + + memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun)); + + for (i = 0; i < sizeof(lun); i += 2) { + scsilun->scsi_lun[i] = (lun >> 8) & 0xFF; + scsilun->scsi_lun[i+1] = lun & 0xFF; + lun = lun >> 16; + } +} +EXPORT_SYMBOL(int_to_scsilun); + /** * scsi_report_lun_scan - Scan using SCSI REPORT LUN results - * @sdevscan: scan the host, channel, and id of this Scsi_Device + * @sdevscan: scan the host, channel, and id of this scsi_device * * Description: * If @sdevscan is for a SCSI-3 or up device, send a REPORT LUN @@ -927,7 +1109,7 @@ static int scsilun_to_int(struct scsi_lun *scsilun) * 0: scan completed (or no memory, so further scanning is futile) * 1: no report lun scan, or not configured **/ -static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, +static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, int rescan) { char devname[64]; @@ -936,29 +1118,40 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, unsigned int lun; unsigned int num_luns; unsigned int retries; + int result; struct scsi_lun *lunp, *lun_data; - struct scsi_request *sreq; u8 *data; + struct scsi_sense_hdr sshdr; + struct scsi_device *sdev; + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + int ret = 0; /* * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set. * Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does * support more than 8 LUNs. */ - if ((bflags & BLIST_NOREPORTLUN) || - sdev->scsi_level < SCSI_2 || - (sdev->scsi_level < SCSI_3 && - (!(bflags & BLIST_REPORTLUN2) || sdev->host->max_lun <= 8)) ) + if (bflags & BLIST_NOREPORTLUN) + return 1; + if (starget->scsi_level < SCSI_2 && + starget->scsi_level != SCSI_UNKNOWN) + return 1; + if (starget->scsi_level < SCSI_3 && + (!(bflags & BLIST_REPORTLUN2) || shost->max_lun <= 8)) return 1; if (bflags & BLIST_NOLUN) return 0; - sreq = scsi_allocate_request(sdev, GFP_ATOMIC); - if (!sreq) - goto out; + if (!(sdev = scsi_device_lookup_by_target(starget, 0))) { + sdev = scsi_alloc_sdev(starget, 0, NULL); + if (!sdev) + return 0; + if (scsi_device_get(sdev)) + return 0; + } sprintf(devname, "host %d channel %d id %d", - sdev->host->host_no, sdev->channel, sdev->id); + shost->host_no, sdev->channel, sdev->id); /* * Allocate enough to hold the header (the same size as one scsi_lun) @@ -973,8 +1166,10 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, length = (max_scsi_report_luns + 1) * sizeof(struct scsi_lun); lun_data = kmalloc(length, GFP_ATOMIC | (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0)); - if (!lun_data) - goto out_release_request; + if (!lun_data) { + printk(ALLOC_FAILURE_MSG, __FUNCTION__); + goto out; + } scsi_cmd[0] = REPORT_LUNS; @@ -993,8 +1188,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, scsi_cmd[10] = 0; /* reserved */ scsi_cmd[11] = 0; /* control */ - sreq->sr_cmd_len = 0; - sreq->sr_data_direction = DMA_FROM_DEVICE; /* * We can get a UNIT ATTENTION, for example a power on/reset, so @@ -1010,26 +1203,29 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending" " REPORT LUNS to %s (try %d)\n", devname, retries)); - scsi_wait_req(sreq, scsi_cmd, lun_data, length, - SCSI_TIMEOUT + 4*HZ, 3); + + result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, + lun_data, length, &sshdr, + SCSI_TIMEOUT + 4 * HZ, 3); + SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS" - " %s (try %d) result 0x%x\n", sreq->sr_result - ? "failed" : "successful", retries, - sreq->sr_result)); - if (sreq->sr_result == 0 || - sreq->sr_sense_buffer[2] != UNIT_ATTENTION) + " %s (try %d) result 0x%x\n", result + ? "failed" : "successful", retries, result)); + if (result == 0) break; + else if (scsi_sense_valid(&sshdr)) { + if (sshdr.sense_key != UNIT_ATTENTION) + break; + } } - if (sreq->sr_result) { + if (result) { /* * The device probably does not support a REPORT LUN command */ - kfree(lun_data); - scsi_release_request(sreq); - return 1; + ret = 1; + goto out_err; } - scsi_release_request(sreq); /* * Get the length from the first four bytes of lun_data. @@ -1047,9 +1243,8 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, num_luns = max_scsi_report_luns; } - SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUN scan of" - " host %d channel %d id %d\n", sdev->host->host_no, - sdev->channel, sdev->id)); + SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev, + "scsi scan: REPORT LUN scan\n")); /* * Scan the luns in lun_data. The entry at offset 0 is really @@ -1075,10 +1270,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, 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 > sdev->host->max_lun) { printk(KERN_WARNING "scsi: %s lun%d has a LUN larger" " than allowed by the host adapter\n", @@ -1086,48 +1277,68 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, } else { int res; - res = scsi_probe_and_add_lun(sdev->host, sdev->channel, - sdev->id, lun, NULL, NULL, rescan, NULL); + res = scsi_probe_and_add_lun(starget, + lun, NULL, NULL, rescan, NULL); if (res == SCSI_SCAN_NO_RESPONSE) { /* * Got some results, but now none, abort. */ - printk(KERN_ERR "scsi: Unexpected response" - " from %s lun %d while scanning, scan" - " aborted\n", devname, lun); + sdev_printk(KERN_ERR, sdev, + "Unexpected response" + " from lun %d while scanning, scan" + " aborted\n", lun); break; } } } + out_err: kfree(lun_data); - return 0; - - out_release_request: - scsi_release_request(sreq); out: - /* - * We are out of memory, don't try scanning any further. - */ - printk(ALLOC_FAILURE_MSG, __FUNCTION__); - return 0; + scsi_device_put(sdev); + if (sdev->sdev_state == SDEV_CREATED) + /* + * the sdev we used didn't appear in the report luns scan + */ + scsi_destroy_sdev(sdev); + return ret; } struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, - uint id, uint lun, void *hostdata) + uint id, uint lun, void *hostdata) { - struct scsi_device *sdev; - int res; + struct scsi_device *sdev = ERR_PTR(-ENODEV); + struct device *parent = &shost->shost_gendev; + struct scsi_target *starget; - down(&shost->scan_mutex); - res = scsi_probe_and_add_lun(shost, channel, id, lun, NULL, - &sdev, 1, hostdata); - if (res != SCSI_SCAN_LUN_PRESENT) - sdev = ERR_PTR(-ENODEV); - up(&shost->scan_mutex); + starget = scsi_alloc_target(parent, channel, id); + if (!starget) + return ERR_PTR(-ENOMEM); + + get_device(&starget->dev); + mutex_lock(&shost->scan_mutex); + if (scsi_host_scan_allowed(shost)) + scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); + mutex_unlock(&shost->scan_mutex); + scsi_target_reap(starget); + put_device(&starget->dev); return sdev; } +EXPORT_SYMBOL(__scsi_add_device); + +int scsi_add_device(struct Scsi_Host *host, uint channel, + uint target, uint lun) +{ + struct scsi_device *sdev = + __scsi_add_device(host, channel, target, lun, NULL); + if (IS_ERR(sdev)) + return PTR_ERR(sdev); + + scsi_device_put(sdev); + return 0; +} +EXPORT_SYMBOL(scsi_add_device); void scsi_rescan_device(struct device *dev) { @@ -1143,32 +1354,15 @@ void scsi_rescan_device(struct device *dev) module_put(drv->owner); } } +EXPORT_SYMBOL(scsi_rescan_device); -/** - * scsi_scan_target - scan a target id, possibly including all LUNs on the - * target. - * @sdevsca: Scsi_Device handle for scanning - * @shost: host to scan - * @channel: channel to scan - * @id: target id to scan - * - * Description: - * Scan the target id on @shost, @channel, and @id. Scan at least LUN - * 0, and possibly all LUNs on the target id. - * - * Use the pre-allocated @sdevscan as a handle for the scanning. This - * function sets sdevscan->host, sdevscan->id and sdevscan->lun; the - * scanning functions modify sdevscan->lun. - * - * First try a REPORT LUN scan, if that does not scan the target, do a - * sequential scan of LUNs on the target id. - **/ -static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun, int rescan) +static void __scsi_scan_target(struct device *parent, unsigned int channel, + unsigned int id, unsigned int lun, int rescan) { + struct Scsi_Host *shost = dev_to_shost(parent); int bflags = 0; int res; - struct scsi_device *sdev; + struct scsi_target *starget; if (shost->this_id == id) /* @@ -1176,41 +1370,70 @@ static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel, */ return; + starget = scsi_alloc_target(parent, channel, id); + if (!starget) + return; + + get_device(&starget->dev); if (lun != SCAN_WILD_CARD) { /* * Scan for a specific host/chan/id/lun. */ - scsi_probe_and_add_lun(shost, channel, id, lun, NULL, NULL, - rescan, NULL); - return; + scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL); + goto out_reap; } /* * Scan LUN 0, if there is some response, scan further. Ideally, we * would not configure LUN 0 until all LUNs are scanned. */ - res = scsi_probe_and_add_lun(shost, channel, id, 0, &bflags, &sdev, - rescan, NULL); - if (res == SCSI_SCAN_LUN_PRESENT) { - if (scsi_report_lun_scan(sdev, bflags, rescan) != 0) + res = scsi_probe_and_add_lun(starget, 0, &bflags, NULL, rescan, NULL); + if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT) { + if (scsi_report_lun_scan(starget, bflags, rescan) != 0) /* * The REPORT LUN did not scan the target, * do a sequential scan. */ - scsi_sequential_lun_scan(shost, channel, id, bflags, - res, sdev->scsi_level, rescan); - } else if (res == SCSI_SCAN_TARGET_PRESENT) { - /* - * There's a target here, but lun 0 is offline so we - * can't use the report_lun scan. Fall back to a - * sequential lun scan with a bflags of SPARSELUN and - * a default scsi level of SCSI_2 - */ - scsi_sequential_lun_scan(shost, channel, id, BLIST_SPARSELUN, - SCSI_SCAN_TARGET_PRESENT, SCSI_2, rescan); + scsi_sequential_lun_scan(starget, bflags, + res, starget->scsi_level, rescan); } + + out_reap: + /* now determine if the target has any children at all + * and if not, nuke it */ + scsi_target_reap(starget); + + put_device(&starget->dev); } +/** + * scsi_scan_target - scan a target id, possibly including all LUNs on the + * target. + * @parent: host to scan + * @channel: channel to scan + * @id: target id to scan + * @lun: Specific LUN to scan or SCAN_WILD_CARD + * @rescan: passed to LUN scanning routines + * + * Description: + * Scan the target id on @parent, @channel, and @id. Scan at least LUN 0, + * and possibly all LUNs on the target id. + * + * First try a REPORT LUN scan, if that does not scan the target, do a + * sequential scan of LUNs on the target id. + **/ +void scsi_scan_target(struct device *parent, unsigned int channel, + unsigned int id, unsigned int lun, int rescan) +{ + struct Scsi_Host *shost = dev_to_shost(parent); + + mutex_lock(&shost->scan_mutex); + if (scsi_host_scan_allowed(shost)) + __scsi_scan_target(parent, channel, id, lun, rescan); + mutex_unlock(&shost->scan_mutex); +} +EXPORT_SYMBOL(scsi_scan_target); + static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, unsigned int id, unsigned int lun, int rescan) { @@ -1234,30 +1457,37 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, order_id = shost->max_id - id - 1; else order_id = id; - scsi_scan_target(shost, channel, order_id, lun, rescan); + __scsi_scan_target(&shost->shost_gendev, channel, + order_id, lun, rescan); } else - scsi_scan_target(shost, channel, id, lun, rescan); + __scsi_scan_target(&shost->shost_gendev, channel, + id, lun, rescan); } int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, unsigned int id, unsigned int lun, int rescan) { - SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "%s: <%u:%u:%u:%u>\n", - __FUNCTION__, shost->host_no, channel, id, lun)); + SCSI_LOG_SCAN_BUS(3, shost_printk (KERN_INFO, shost, + "%s: <%u:%u:%u>\n", + __FUNCTION__, channel, id, lun)); if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) || ((id != SCAN_WILD_CARD) && (id > shost->max_id)) || ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun))) return -EINVAL; - down(&shost->scan_mutex); - if (channel == SCAN_WILD_CARD) - for (channel = 0; channel <= shost->max_channel; channel++) + mutex_lock(&shost->scan_mutex); + if (scsi_host_scan_allowed(shost)) { + if (channel == SCAN_WILD_CARD) + for (channel = 0; channel <= shost->max_channel; + channel++) + scsi_scan_channel(shost, channel, id, lun, + rescan); + else scsi_scan_channel(shost, channel, id, lun, rescan); - else - scsi_scan_channel(shost, channel, id, lun, rescan); - up(&shost->scan_mutex); + } + mutex_unlock(&shost->scan_mutex); return 0; } @@ -1271,26 +1501,21 @@ void scsi_scan_host(struct Scsi_Host *shost) scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD, SCAN_WILD_CARD, 0); } +EXPORT_SYMBOL(scsi_scan_host); void scsi_forget_host(struct Scsi_Host *shost) { - struct scsi_device *sdev, *tmp; + struct scsi_device *sdev; unsigned long flags; - /* - * Ok, this look a bit strange. We always look for the first device - * on the list as scsi_remove_device removes them from it - thus we - * also have to release the lock. - * We don't need to get another reference to the device before - * releasing the lock as we already own the reference from - * scsi_register_device that's release in scsi_remove_device. And - * after that we don't look at sdev anymore. - */ + restart: spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) { + list_for_each_entry(sdev, &shost->__devices, siblings) { + if (sdev->sdev_state == SDEV_DEL) + continue; spin_unlock_irqrestore(shost->host_lock, flags); - scsi_remove_device(sdev); - spin_lock_irqsave(shost->host_lock, flags); + __scsi_remove_device(sdev); + goto restart; } spin_unlock_irqrestore(shost->host_lock, flags); } @@ -1298,16 +1523,16 @@ void scsi_forget_host(struct Scsi_Host *shost) /* * Function: scsi_get_host_dev() * - * Purpose: Create a Scsi_Device that points to the host adapter itself. + * Purpose: Create a scsi_device that points to the host adapter itself. * - * Arguments: SHpnt - Host that needs a Scsi_Device + * Arguments: SHpnt - Host that needs a scsi_device * * Lock status: None assumed. * - * Returns: The Scsi_Device or NULL + * Returns: The scsi_device or NULL * * Notes: - * Attach a single Scsi_Device to the Scsi_Host - this should + * Attach a single scsi_device to the Scsi_Host - this should * be made to look like a "pseudo-device" that points to the * HA itself. * @@ -1317,21 +1542,34 @@ void scsi_forget_host(struct Scsi_Host *shost) */ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) { - struct scsi_device *sdev; + struct scsi_device *sdev = NULL; + struct scsi_target *starget; - sdev = scsi_alloc_sdev(shost, 0, shost->this_id, 0, NULL); + mutex_lock(&shost->scan_mutex); + if (!scsi_host_scan_allowed(shost)) + goto out; + starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->this_id); + if (!starget) + goto out; + + sdev = scsi_alloc_sdev(starget, 0, NULL); if (sdev) { + sdev->sdev_gendev.parent = get_device(&starget->dev); sdev->borken = 0; } + put_device(&starget->dev); + out: + mutex_unlock(&shost->scan_mutex); return sdev; } +EXPORT_SYMBOL(scsi_get_host_dev); /* * Function: scsi_free_host_dev() * * Purpose: Free a scsi_device that points to the host adapter itself. * - * Arguments: SHpnt - Host that needs a Scsi_Device + * Arguments: SHpnt - Host that needs a scsi_device * * Lock status: None assumed. * @@ -1343,9 +1581,7 @@ void scsi_free_host_dev(struct scsi_device *sdev) { BUG_ON(sdev->id != sdev->host->this_id); - if (sdev->host->hostt->slave_destroy) - sdev->host->hostt->slave_destroy(sdev); - if (sdev->host->transportt->cleanup) - sdev->host->transportt->cleanup(sdev); - put_device(&sdev->sdev_gendev); + scsi_destroy_sdev(sdev); } +EXPORT_SYMBOL(scsi_free_host_dev); +