diff options
Diffstat (limited to 'drivers/scsi/scsi_scan.c')
| -rw-r--r-- | drivers/scsi/scsi_scan.c | 1038 |
1 files changed, 742 insertions, 296 deletions
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 327c5d7e5bd..e02b3aab56c 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, @@ -25,19 +25,22 @@ * or a LUN is seen that cannot have a device attached to it. */ -#include <linux/config.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/blkdev.h> -#include <asm/semaphore.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/spinlock.h> +#include <linux/async.h> +#include <linux/slab.h> #include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_driver.h> #include <scsi/scsi_devinfo.h> #include <scsi/scsi_host.h> -#include <scsi/scsi_request.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_eh.h> @@ -53,7 +56,7 @@ #define SCSI_TIMEOUT (2*HZ) /* - * Prefix values for the SCSI id's (stored in driverfs name field) + * Prefix values for the SCSI id's (stored in sysfs name field) */ #define SCSI_UID_SER_NUM 'S' #define SCSI_UID_UNKNOWN 'Z' @@ -74,7 +77,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 @@ -84,10 +87,21 @@ static unsigned int max_scsi_luns = MAX_SCSI_LUNS; static unsigned int max_scsi_luns = 1; #endif -module_param_named(max_luns, max_scsi_luns, int, S_IRUGO|S_IWUSR); +module_param_named(max_luns, max_scsi_luns, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(max_luns, "last scsi LUN (should be between 1 and 2^32-1)"); +#ifdef CONFIG_SCSI_SCAN_ASYNC +#define SCSI_SCAN_TYPE_DEFAULT "async" +#else +#define SCSI_SCAN_TYPE_DEFAULT "sync" +#endif + +char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT; + +module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO); +MODULE_PARM_DESC(scan, "sync, async or none"); + /* * max_scsi_report_luns: the maximum number of LUNS that will be * returned from the REPORT LUNS command. 8 times this value must @@ -97,17 +111,78 @@ MODULE_PARM_DESC(max_luns, */ static unsigned int max_scsi_report_luns = 511; -module_param_named(max_report_luns, max_scsi_report_luns, int, S_IRUGO|S_IWUSR); +module_param_named(max_report_luns, max_scsi_report_luns, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(max_report_luns, "REPORT LUNS maximum number of LUNS received (should be" " between 1 and 16384)"); -static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ+3; +static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ + 18; -module_param_named(inq_timeout, scsi_inq_timeout, int, S_IRUGO|S_IWUSR); +module_param_named(inq_timeout, scsi_inq_timeout, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(inq_timeout, "Timeout (in seconds) waiting for devices to answer INQUIRY." - " Default is 5. Some non-compliant devices need more."); + " Default is 20. Some devices may need more; most need less."); + +/* This lock protects only this list */ +static DEFINE_SPINLOCK(async_scan_lock); +static LIST_HEAD(scanning_hosts); + +struct async_scan_data { + struct list_head list; + struct Scsi_Host *shost; + struct completion prev_finished; +}; + +/** + * scsi_complete_async_scans - Wait for asynchronous scans to complete + * + * When this function returns, any host which started scanning before + * this function was called will have finished its scan. Hosts which + * started scanning after this function was called may or may not have + * finished. + */ +int scsi_complete_async_scans(void) +{ + struct async_scan_data *data; + + do { + if (list_empty(&scanning_hosts)) + return 0; + /* If we can't get memory immediately, that's OK. Just + * sleep a little. Even if we never get memory, the async + * scans will finish eventually. + */ + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + msleep(1); + } while (!data); + + data->shost = NULL; + init_completion(&data->prev_finished); + + spin_lock(&async_scan_lock); + /* Check that there's still somebody else on the list */ + if (list_empty(&scanning_hosts)) + goto done; + list_add_tail(&data->list, &scanning_hosts); + spin_unlock(&async_scan_lock); + + printk(KERN_INFO "scsi: waiting for bus probes to complete ...\n"); + wait_for_completion(&data->prev_finished); + + spin_lock(&async_scan_lock); + list_del(&data->list); + if (!list_empty(&scanning_hosts)) { + struct async_scan_data *next = list_entry(scanning_hosts.next, + struct async_scan_data, list); + complete(&next->prev_finished); + } + done: + spin_unlock(&async_scan_lock); + + kfree(data); + return 0; +} /** * scsi_unlock_floptical - unlock device via a special MODE SENSE command @@ -131,64 +206,14 @@ static void scsi_unlock_floptical(struct scsi_device *sdev, scsi_cmd[4] = 0x2a; /* size */ scsi_cmd[5] = 0; scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL, - SCSI_TIMEOUT, 3); -} - -/** - * print_inquiry - printk the inquiry information - * @inq_result: printk this SCSI INQUIRY - * - * Description: - * printk the vendor, model, and other information found in the - * INQUIRY data in @inq_result. - * - * Notes: - * Remove this, and replace with a hotplug event that logs any - * relevant information. - **/ -static void print_inquiry(unsigned char *inq_result) -{ - int i; - - printk(KERN_NOTICE " Vendor: "); - for (i = 8; i < 16; i++) - if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) - printk("%c", inq_result[i]); - else - printk(" "); - - printk(" Model: "); - for (i = 16; i < 32; i++) - if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) - printk("%c", inq_result[i]); - else - printk(" "); - - printk(" Rev: "); - for (i = 32; i < 36; i++) - if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) - printk("%c", inq_result[i]); - else - printk(" "); - - printk("\n"); - - i = inq_result[0] & 0x1f; - - printk(KERN_NOTICE " Type: %s ", - i < - MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : - "Unknown "); - printk(" ANSI SCSI revision: %02x", - inq_result[2] & 0x07); - if ((inq_result[2] & 0x07) == 1 && (inq_result[3] & 0x0f) == 1) - printk(" CCS\n"); - else - printk("\n"); + SCSI_TIMEOUT, 3, NULL); } /** * scsi_alloc_sdev - allocate and setup a scsi_Device + * @starget: which target to allocate a &scsi_device for + * @lun: which lun + * @hostdata: usually NULL and set by ->slave_alloc instead * * Description: * Allocate, initialize for io, and return a pointer to a scsi_Device. @@ -204,17 +229,19 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, struct scsi_device *sdev; int display_failure_msg = 1, ret; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + extern void scsi_evt_thread(struct work_struct *work); + extern void scsi_requeue_run_queue(struct work_struct *work); - sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size, + 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->queue_ramp_up_period = SCSI_DEFAULT_RAMP_UP_PERIOD; sdev->id = starget->id; sdev->lun = lun; sdev->channel = starget->channel; @@ -223,7 +250,10 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, INIT_LIST_HEAD(&sdev->same_target_siblings); INIT_LIST_HEAD(&sdev->cmd_list); INIT_LIST_HEAD(&sdev->starved_entry); + INIT_LIST_HEAD(&sdev->event_list); spin_lock_init(&sdev->list_lock); + INIT_WORK(&sdev->event_work, scsi_evt_thread); + INIT_WORK(&sdev->requeue_work, scsi_requeue_run_queue); sdev->sdev_gendev.parent = get_device(&starget->dev); sdev->sdev_target = starget; @@ -252,9 +282,10 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, /* 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; } - + WARN_ON_ONCE(!blk_get_queue(sdev->request_queue)); sdev->request_queue->queuedata = sdev; scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); @@ -266,8 +297,6 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, /* * if LLDD reports slave not present, don't clutter * console with alloc failure messages - - */ if (ret == -ENXIO) display_failure_msg = 0; @@ -278,30 +307,46 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, return sdev; out_device_destroy: - transport_destroy_device(&sdev->sdev_gendev); - scsi_free_queue(sdev->request_queue); - put_device(&sdev->sdev_gendev); + __scsi_remove_device(sdev); out: if (display_failure_msg) - printk(ALLOC_FAILURE_MSG, __FUNCTION__); + printk(ALLOC_FAILURE_MSG, __func__); return NULL; } +static void scsi_target_destroy(struct scsi_target *starget) +{ + struct device *dev = &starget->dev; + struct Scsi_Host *shost = dev_to_shost(dev->parent); + unsigned long flags; + + starget->state = STARGET_DEL; + transport_destroy_device(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(dev); +} + static void scsi_target_dev_release(struct device *dev) { struct device *parent = dev->parent; struct scsi_target *starget = to_scsi_target(dev); - struct Scsi_Host *shost = dev_to_shost(parent); - if (shost->hostt->target_destroy) - shost->hostt->target_destroy(starget); kfree(starget); put_device(parent); } +static struct device_type scsi_target_type = { + .name = "scsi_target", + .release = scsi_target_dev_release, +}; + int scsi_is_target_device(const struct device *dev) { - return dev->release == scsi_target_dev_release; + return dev->type == &scsi_target_type; } EXPORT_SYMBOL(scsi_is_target_device); @@ -326,6 +371,49 @@ static struct scsi_target *__scsi_find_target(struct device *parent, return found_starget; } +/** + * scsi_target_reap_ref_release - remove target from visibility + * @kref: the reap_ref in the target being released + * + * Called on last put of reap_ref, which is the indication that no device + * under this target is visible anymore, so render the target invisible in + * sysfs. Note: we have to be in user context here because the target reaps + * should be done in places where the scsi device visibility is being removed. + */ +static void scsi_target_reap_ref_release(struct kref *kref) +{ + struct scsi_target *starget + = container_of(kref, struct scsi_target, reap_ref); + + /* + * if we get here and the target is still in the CREATED state that + * means it was allocated but never made visible (because a scan + * turned up no LUNs), so don't call device_del() on it. + */ + if (starget->state != STARGET_CREATED) { + transport_remove_device(&starget->dev); + device_del(&starget->dev); + } + scsi_target_destroy(starget); +} + +static void scsi_target_reap_ref_put(struct scsi_target *starget) +{ + kref_put(&starget->reap_ref, scsi_target_reap_ref_release); +} + +/** + * scsi_alloc_target - allocate a new or find an existing target + * @parent: parent of the target (need not be a scsi host) + * @channel: target channel number (zero if no channels) + * @id: target id number + * + * Return an existing target if one exists, provided it hasn't already + * gone into STARGET_DEL state, otherwise allocate a new target. + * + * The target is returned with an incremented reference, so the caller + * is responsible for both reaping and doing a last put + */ static struct scsi_target *scsi_alloc_target(struct device *parent, int channel, uint id) { @@ -336,37 +424,29 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, + shost->transportt->target_size; struct scsi_target *starget; struct scsi_target *found_target; + int error, ref_got; - /* - * Obtain the real parent from the transport. The transport - * is allowed to fail (no error) if there is nothing at that - * target id. - */ - if (shost->transportt->target_parent) { - spin_lock_irqsave(shost->host_lock, flags); - parent = shost->transportt->target_parent(shost, channel, id); - spin_unlock_irqrestore(shost->host_lock, flags); - if (!parent) - return NULL; - } - - starget = kmalloc(size, GFP_KERNEL); + starget = kzalloc(size, GFP_KERNEL); if (!starget) { - printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); + printk(KERN_ERR "%s: allocation failure\n", __func__); return NULL; } - memset(starget, 0, size); dev = &starget->dev; device_initialize(dev); - starget->reap_ref = 1; + kref_init(&starget->reap_ref); dev->parent = get_device(parent); - dev->release = scsi_target_dev_release; - sprintf(dev->bus_id, "target%d:%d:%d", - shost->host_no, channel, id); + dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id); + dev->bus = &scsi_bus_type; + dev->type = &scsi_target_type; starget->id = id; starget->channel = channel; + starget->can_queue = 0; INIT_LIST_HEAD(&starget->siblings); INIT_LIST_HEAD(&starget->devices); + starget->state = STARGET_CREATED; + starget->scsi_level = SCSI_2; + starget->max_target_blocked = SCSI_DEFAULT_TARGET_BLOCKED; + retry: spin_lock_irqsave(shost->host_lock, flags); found_target = __scsi_find_target(parent, channel, id); @@ -377,35 +457,54 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, spin_unlock_irqrestore(shost->host_lock, flags); /* allocate and add */ transport_setup_device(dev); - device_add(dev); - transport_add_device(dev); if (shost->hostt->target_alloc) { - int error = shost->hostt->target_alloc(starget); + 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); + scsi_target_destroy(starget); return NULL; } } + get_device(dev); return starget; found: - found_target->reap_ref++; + /* + * release routine already fired if kref is zero, so if we can still + * take the reference, the target must be alive. If we can't, it must + * be dying and we need to wait for a new target + */ + ref_got = kref_get_unless_zero(&found_target->reap_ref); + spin_unlock_irqrestore(shost->host_lock, flags); - put_device(parent); - kfree(starget); - return found_target; + if (ref_got) { + put_device(dev); + return found_target; + } + /* + * Unfortunately, we found a dying target; need to wait until it's + * dead before we can get a new one. There is an anomaly here. We + * *should* call scsi_target_reap() to balance the kref_get() of the + * reap_ref above. However, since the target being released, it's + * already invisible and the reap_ref is irrelevant. If we call + * scsi_target_reap() we might spuriously do another device_del() on + * an already invisible target. + */ + put_device(&found_target->dev); + /* + * length of time is irrelevant here, we just want to yield the CPU + * for a tick to avoid busy waiting for the target to die. + */ + msleep(1); + goto retry; } /** * 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 @@ -414,19 +513,39 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, */ 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); + /* + * serious problem if this triggers: STARGET_DEL is only set in the if + * the reap_ref drops to zero, so we're trying to do another final put + * on an already released kref + */ + BUG_ON(starget->state == STARGET_DEL); + scsi_target_reap_ref_put(starget); +} - if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { - list_del_init(&starget->siblings); - spin_unlock_irqrestore(shost->host_lock, flags); - device_del(&starget->dev); - transport_unregister_device(&starget->dev); - put_device(&starget->dev); - return; +/** + * sanitize_inquiry_string - remove non-graphical chars from an INQUIRY result string + * @s: INQUIRY result string to sanitize + * @len: length of the string + * + * Description: + * The SCSI spec says that INQUIRY vendor, product, and revision + * strings must consist entirely of graphic ASCII characters, + * padded on the right with spaces. Since not all devices obey + * this rule, we will replace non-graphic or non-ASCII characters + * with spaces. Exception: a NUL character is interpreted as a + * string terminator, so all the following characters are set to + * spaces. + **/ +static void sanitize_inquiry_string(unsigned char *s, int len) +{ + int terminated = 0; + + for (; len > 0; (--len, ++s)) { + if (*s == 0) + terminated = 1; + if (terminated || *s < 0x20 || *s > 0x7e) + *s = ' '; } - spin_unlock_irqrestore(shost->host_lock, flags); } /** @@ -441,9 +560,9 @@ void scsi_target_reap(struct scsi_target *starget) * * 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 any flags value is stored in *@bflags. + * are copied to the scsi_device any flags value is stored in *@bflags. **/ -static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, +static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, int result_len, int *bflags) { unsigned char scsi_cmd[MAX_COMMAND_SIZE]; @@ -462,13 +581,14 @@ static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, 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)); + SCSI_LOG_SCAN_BUS(3, sdev_printk(KERN_INFO, sdev, + "scsi scan: INQUIRY pass %d length %d\n", + pass, try_inquiry_len)); /* Each pass gets up to three chances to ignore Unit Attention */ for (count = 0; count < 3; ++count) { + int resid; + memset(scsi_cmd, 0, 6); scsi_cmd[0] = INQUIRY; scsi_cmd[4] = (unsigned char) try_inquiry_len; @@ -477,7 +597,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, inq_result, try_inquiry_len, &sshdr, - HZ / 2 + HZ * scsi_inq_timeout, 3); + HZ / 2 + HZ * scsi_inq_timeout, 3, + &resid); SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s " "with code 0x%x\n", @@ -498,20 +619,32 @@ static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, (sshdr.ascq == 0)) continue; } + } else { + /* + * if nothing was transferred, we try + * again. It's a workaround for some USB + * devices. + */ + if (resid == try_inquiry_len) + continue; } break; } if (result == 0) { - response_len = (unsigned char) inq_result[4] + 5; + sanitize_inquiry_string(&inq_result[8], 8); + sanitize_inquiry_string(&inq_result[16], 16); + sanitize_inquiry_string(&inq_result[32], 4); + + response_len = inq_result[4] + 5; if (response_len > 255) response_len = first_inquiry_len; /* sanity */ /* * Get any flags for this device. * - * XXX add a bflags to Scsi_Device, and replace the - * corresponding bit fields in Scsi_Device, so bflags + * 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], @@ -566,6 +699,19 @@ static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, * short INQUIRY), an abort here prevents any further use of the * device, including spin up. * + * On the whole, the best approach seems to be to assume the first + * 36 bytes are valid no matter what the device says. That's + * better than copying < 36 bytes to the inquiry-result buffer + * and displaying garbage for the Vendor, Product, or Revision + * strings. + */ + if (sdev->inquiry_len < 36) { + printk(KERN_INFO "scsi scan: INQUIRY result too short (%d)," + " using 36\n", sdev->inquiry_len); + sdev->inquiry_len = 36; + } + + /* * Related to the above issue: * * XXX Devices (disk or all?) should be sent a TEST UNIT READY, @@ -593,24 +739,25 @@ static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, } /** - * 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 + * @sdev: holds information to be stored in the new scsi_device * @inq_result: holds the result of a previous INQUIRY to the LUN * @bflags: black/white list flag + * @async: 1 if this device is being scanned asynchronously * * Description: - * 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 - * when scanning a particular LUN). + * Initialize the scsi_device @sdev. Optionally set fields based + * on values in *@bflags. * * 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) +static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, + int *bflags, int async) { + int ret; + /* * XXX do not save the inquiry, since it can change underneath us, * save just vendor/model/rev. @@ -621,29 +768,46 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) * scanning run at their own risk, or supply a user level program * that can correctly scan. */ - sdev->inquiry = kmalloc(sdev->inquiry_len, GFP_ATOMIC); - if (sdev->inquiry == NULL) { + + /* + * Copy at least 36 bytes of INQUIRY data, so that we don't + * dereference unallocated memory when accessing the Vendor, + * Product, and Revision strings. Badly behaved devices may set + * the INQUIRY Additional Length byte to a small value, indicating + * these strings are invalid, but often they contain plausible data + * nonetheless. It doesn't matter if the device sent < 36 bytes + * total, since scsi_probe_lun() initializes inq_result with 0s. + */ + sdev->inquiry = kmemdup(inq_result, + max_t(size_t, sdev->inquiry_len, 36), + GFP_ATOMIC); + if (sdev->inquiry == NULL) return SCSI_SCAN_NO_RESPONSE; - } - memcpy(sdev->inquiry, inq_result, sdev->inquiry_len); sdev->vendor = (char *) (sdev->inquiry + 8); sdev->model = (char *) (sdev->inquiry + 16); sdev->rev = (char *) (sdev->inquiry + 32); - if (*bflags & BLIST_ISROM) { + if (strncmp(sdev->vendor, "ATA ", 8) == 0) { /* - * It would be better to modify sdev->type, and set - * sdev->removable, but then the print_inquiry() output - * would not show TYPE_ROM; if print_inquiry() is removed - * the issue goes away. + * sata emulation layer device. This is a hack to work around + * the SATL power management specifications which state that + * when the SATL detects the device has gone into standby + * mode, it shall respond with NOT READY. */ - inq_result[0] = TYPE_ROM; - inq_result[1] |= 0x80; /* removable */ - } else if (*bflags & BLIST_NO_ULD_ATTACH) - sdev->no_uld_attach = 1; + sdev->allow_restart = 1; + } + + if (*bflags & BLIST_ISROM) { + sdev->type = TYPE_ROM; + sdev->removable = 1; + } else { + sdev->type = (inq_result[0] & 0x1f); + sdev->removable = (inq_result[1] & 0x80) >> 7; + } - switch (sdev->type = (inq_result[0] & 0x1f)) { + switch (sdev->type) { + case TYPE_RBC: case TYPE_TAPE: case TYPE_DISK: case TYPE_PRINTER: @@ -653,18 +817,26 @@ 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_RBC: + case TYPE_RAID: + case TYPE_OSD: sdev->writeable = 1; break; - case TYPE_WORM: case TYPE_ROM: + case TYPE_WORM: sdev->writeable = 0; break; default: printk(KERN_INFO "scsi: unknown device type %d\n", sdev->type); } - print_inquiry(inq_result); + if (sdev->type == TYPE_RBC || sdev->type == TYPE_ROM) { + /* RBC and MMC devices can return SCSI-3 compliance and yet + * still not support REPORT LUNS, so make them act as + * BLIST_NOREPORTLUN unless BLIST_REPORTLUN2 is + * specifically set */ + if ((*bflags & BLIST_REPORTLUN2) == 0) + *bflags |= BLIST_NOREPORTLUN; + } /* * For a peripheral qualifier (PQ) value of 1 (001b), the SCSI @@ -675,7 +847,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 @@ -683,29 +855,27 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) */ sdev->inq_periph_qual = (inq_result[0] >> 5) & 7; - sdev->removable = (0x80 & inq_result[1]) >> 7; sdev->lockable = sdev->removable; sdev->soft_reset = (inq_result[7] & 1) && ((inq_result[3] & 7) == 2); - if (sdev->scsi_level >= SCSI_3 || (sdev->inquiry_len > 56 && - inq_result[56] & 0x04)) + if (sdev->scsi_level >= SCSI_3 || + (sdev->inquiry_len > 56 && inq_result[56] & 0x04)) sdev->ppr = 1; if (inq_result[7] & 0x60) sdev->wdtr = 1; 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. - */ + sdev_printk(KERN_NOTICE, sdev, "%s %.8s %.16s %.4s PQ: %d " + "ANSI: %d%s\n", scsi_device_type(sdev->type), + sdev->vendor, sdev->model, sdev->rev, + sdev->inq_periph_qual, inq_result[2] & 0x07, + (inq_result[3] & 0x0f) == 1 ? " CCS" : ""); if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) && !(*bflags & BLIST_NOTQ)) sdev->tagged_supported = 1; + /* * Some devices (Texel CD ROM drives) have handshaking problems * when used with the Seagate controllers. borken is initialized @@ -714,6 +884,9 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if ((*bflags & BLIST_BORKEN) == 0) sdev->borken = 0; + if (*bflags & BLIST_NO_ULD_ATTACH) + sdev->no_uld_attach = 1; + /* * Apparently some really broken devices (contrary to the SCSI * standards) need to be selected without asserting ATN @@ -722,6 +895,13 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) sdev->select_no_atn = 1; /* + * Maximum 512 sector transfer length + * broken RA4x00 Compaq Disk Array + */ + if (*bflags & BLIST_MAX_512) + blk_queue_max_hw_sectors(sdev->request_queue, 512); + + /* * Some devices may not want to have a start command automatically * issued when a device is added. */ @@ -729,8 +909,7 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) sdev->no_start_on_add = 1; if (*bflags & BLIST_SINGLELUN) - sdev->single_lun = 1; - + scsi_target(sdev)->single_lun = 1; sdev->use_10_for_rw = 1; @@ -745,7 +924,17 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) /* set the device running here so that slave configure * may do I/O */ - scsi_device_set_state(sdev, SDEV_RUNNING); + ret = scsi_device_set_state(sdev, SDEV_RUNNING); + if (ret) { + ret = scsi_device_set_state(sdev, SDEV_BLOCK); + + if (ret) { + sdev_printk(KERN_ERR, sdev, + "in wrong state %s to complete scan\n", + scsi_device_state_name(sdev->sdev_state)); + return SCSI_SCAN_NO_RESPONSE; + } + } if (*bflags & BLIST_MS_192_BYTES_FOR_3F) sdev->use_192_bytes_for_3f = 1; @@ -756,48 +945,91 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) if (*bflags & BLIST_RETRY_HWERROR) sdev->retry_hwerror = 1; + if (*bflags & BLIST_NO_DIF) + sdev->no_dif = 1; + + sdev->eh_timeout = SCSI_DEFAULT_EH_TIMEOUT; + + if (*bflags & BLIST_SKIP_VPD_PAGES) + sdev->skip_vpd_pages = 1; + transport_configure_device(&sdev->sdev_gendev); - if (sdev->host->hostt->slave_configure) - sdev->host->hostt->slave_configure(sdev); + if (sdev->host->hostt->slave_configure) { + 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; + } + } + + if (sdev->scsi_level >= SCSI_3) + scsi_attach_vpd(sdev); + + sdev->max_queue_depth = sdev->queue_depth; /* * Ok, the device is now all set up, we can * register it and tell the rest of the kernel * about it. */ - if (scsi_sysfs_add_sdev(sdev) != 0) + if (!async && 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) +#ifdef CONFIG_SCSI_LOGGING +/** + * scsi_inq_str - print INQUIRY data from min to max index, strip trailing whitespace + * @buf: Output buffer with at least end-first+1 bytes of space + * @inq: Inquiry buffer (input) + * @first: Offset of string into inq + * @end: Index after last character in inq + */ +static unsigned char *scsi_inq_str(unsigned char *buf, unsigned char *inq, + unsigned first, unsigned end) { - if (sdev->host->hostt->slave_destroy) - sdev->host->hostt->slave_destroy(sdev); - transport_destroy_device(&sdev->sdev_gendev); - put_device(&sdev->sdev_gendev); -} + unsigned term = 0, idx; + for (idx = 0; idx + first < end && idx + first < inq[4] + 5; idx++) { + if (inq[idx+first] > ' ') { + buf[idx] = inq[idx+first]; + term = idx+1; + } else { + buf[idx] = ' '; + } + } + buf[term] = 0; + return buf; +} +#endif /** * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it * @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 + * @sdevp: probe the LUN corresponding to this scsi_device + * @rescan: if nonzero skip some code only needed on first scan + * @hostdata: passed to scsi_alloc_sdev() * * Description: * Call scsi_probe_lun, if a LUN with an attached device is found, * 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_target *starget, uint lun, int *bflagsp, @@ -815,10 +1047,10 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, */ sdev = scsi_device_lookup_by_target(starget, lun); if (sdev) { - if (rescan || sdev->sdev_state != SDEV_CREATED) { + if (rescan || !scsi_device_created(sdev)) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: device exists on %s\n", - sdev->sdev_gendev.bus_id)); + dev_name(&sdev->sdev_gendev))); if (sdevp) *sdevp = sdev; else @@ -844,10 +1076,12 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, if (scsi_probe_lun(sdev, result, result_len, &bflags)) goto out_free_result; + if (bflagsp) + *bflagsp = bflags; /* * result contains valid SCSI INQUIRY data. */ - if ((result[0] >> 5) == 3) { + if (((result[0] >> 5) == 3) && !(bflags & BLIST_ATTACH_PQ3)) { /* * For a Peripheral qualifier 3 (011b), the SCSI * spec says: The device server is not capable of @@ -858,21 +1092,62 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, * logical disk configured at sdev->lun, but there * is a target id responding. */ + SCSI_LOG_SCAN_BUS(2, sdev_printk(KERN_INFO, sdev, "scsi scan:" + " peripheral qualifier of 3, device not" + " added\n")) + if (lun == 0) { + SCSI_LOG_SCAN_BUS(1, { + unsigned char vend[9]; + unsigned char mod[17]; + + sdev_printk(KERN_INFO, sdev, + "scsi scan: consider passing scsi_mod." + "dev_flags=%s:%s:0x240 or 0x1000240\n", + scsi_inq_str(vend, result, 8, 16), + scsi_inq_str(mod, result, 16, 32)); + }); + + } + + res = SCSI_SCAN_TARGET_PRESENT; + goto out_free_result; + } + + /* + * Some targets may set slight variations of PQ and PDT to signal + * that no LUN is present, so don't add sdev in these cases. + * Two specific examples are: + * 1) NetApp targets: return PQ=1, PDT=0x1f + * 2) USB UFI: returns PDT=0x1f, with the PQ bits being "reserved" + * in the UFI 1.0 spec (we cannot rely on reserved bits). + * + * References: + * 1) SCSI SPC-3, pp. 145-146 + * PQ=1: "A peripheral device having the specified peripheral + * device type is not connected to this logical unit. However, the + * device server is capable of supporting the specified peripheral + * device type on this logical unit." + * PDT=0x1f: "Unknown or no device type" + * 2) USB UFI 1.0, p. 20 + * PDT=00h Direct-access device (floppy) + * PDT=1Fh none (no FDD connected to the requested logical unit) + */ + if (((result[0] >> 5) == 1 || starget->pdt_1f_for_no_lun) && + (result[0] & 0x1f) == 0x1f && + !scsi_is_wlun(lun)) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO - "scsi scan: peripheral qualifier of 3," - " no device added\n")); + "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); + res = scsi_add_lun(sdev, result, &bflags, shost->async_scan); if (res == SCSI_SCAN_LUN_PRESENT) { if (bflags & BLIST_KEY) { sdev->lockable = 0; scsi_unlock_floptical(sdev, result); } - if (bflagsp) - *bflagsp = bflags; } out_free_result: @@ -888,7 +1163,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, } } } else - scsi_destroy_sdev(sdev); + __scsi_remove_device(sdev); out: return res; } @@ -897,7 +1172,8 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, * scsi_sequential_lun_scan - sequentially scan a SCSI target * @starget: pointer to target structure to scan * @bflags: black/white list flag for LUN 0 - * @lun0_res: result of scanning LUN 0 + * @scsi_level: Which version of the standard does this device adhere to + * @rescan: passed to scsi_probe_add_lun() * * Description: * Generally, scan from LUN 1 (LUN 0 is assumed to already have been @@ -907,14 +1183,13 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, * Modifies sdevscan->lun. **/ static void scsi_sequential_lun_scan(struct scsi_target *starget, - int bflags, int lun0_res, int scsi_level, - int rescan) + int bflags, 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" - "%s\n", starget->dev.bus_id)); + "%s\n", dev_name(&starget->dev))); max_dev_lun = min(max_scsi_luns, shost->max_lun); /* @@ -929,13 +1204,6 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, sparse_lun = 0; /* - * If not sparse lun and no device attached at LUN 0 do not scan - * any further. - */ - if (!sparse_lun && (lun0_res != SCSI_SCAN_LUN_PRESENT)) - return; - - /* * If less than SCSI_1_CSS, and no special lun scaning, stop * scanning; this matches 2.4 behaviour, but could just be a bug * (to continue scanning a SCSI_1_CSS device). @@ -983,7 +1251,7 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, } /** - * scsilun_to_int: convert a scsi_lun to an int + * scsilun_to_int - convert a scsi_lun to an int * @scsilun: struct scsi_lun to be converted. * * Description: @@ -1001,7 +1269,7 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, * Given a struct scsi_lun of: 0a 04 0b 03 00 00 00 00, this function returns * the integer: 0x0b030a04 **/ -static int scsilun_to_int(struct scsi_lun *scsilun) +int scsilun_to_int(struct scsi_lun *scsilun) { int i; unsigned int lun; @@ -1012,10 +1280,11 @@ static int scsilun_to_int(struct scsi_lun *scsilun) scsilun->scsi_lun[i + 1]) << (i * 8)); return lun; } +EXPORT_SYMBOL(scsilun_to_int); /** - * int_to_scsilun: reverts an int into a scsi_lun - * @int: integer to be reverted + * int_to_scsilun - reverts an int into a scsi_lun + * @lun: integer to be reverted * @scsilun: struct scsi_lun to be set. * * Description: @@ -1047,18 +1316,23 @@ 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 + * @starget: which target + * @bflags: Zero or a mix of BLIST_NOLUN, BLIST_REPORTLUN2, or BLIST_NOREPORTLUN + * @rescan: nonzero if we can skip code only needed on first scan * * Description: - * If @sdevscan is for a SCSI-3 or up device, send a REPORT LUN - * command, and scan the resulting list of LUNs by calling - * scsi_probe_and_add_lun. + * Fast scanning for modern (SCSI-3) devices by sending a REPORT LUN command. + * Scan the resulting list of LUNs by calling scsi_probe_and_add_lun. * - * Modifies sdevscan->lun. + * If BLINK_REPORTLUN2 is set, scan a target that supports more than 8 + * LUNs even if it's older than SCSI-3. + * If BLIST_NOREPORTLUN is set, return 1 always. + * If BLIST_NOLUN is set, return 0 always. + * If starget->no_report_luns is set, return 1 always. * * Return: * 0: scan completed (or no memory, so further scanning is futile) - * 1: no report lun scan, or not configured + * 1: could not scan with REPORT LUN **/ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, int rescan) @@ -1075,26 +1349,35 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, 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. + * Don't attempt if the target doesn't support REPORT LUNS. */ - if ((bflags & BLIST_NOREPORTLUN) || - starget->scsi_level < SCSI_2 || - (starget->scsi_level < SCSI_3 && - (!(bflags & BLIST_REPORTLUN2) || shost->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; + if (starget->no_report_luns) + return 1; 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)) + if (scsi_device_get(sdev)) { + __scsi_remove_device(sdev); return 0; + } } sprintf(devname, "host %d channel %d id %d", @@ -1114,7 +1397,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, lun_data = kmalloc(length, GFP_ATOMIC | (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0)); if (!lun_data) { - printk(ALLOC_FAILURE_MSG, __FUNCTION__); + printk(ALLOC_FAILURE_MSG, __func__); goto out; } @@ -1153,7 +1436,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, lun_data, length, &sshdr, - SCSI_TIMEOUT + 4 * HZ, 3); + SCSI_TIMEOUT + 4 * HZ, 3, NULL); SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS" " %s (try %d) result 0x%x\n", result @@ -1170,8 +1453,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, /* * The device probably does not support a REPORT LUN command */ - kfree(lun_data); - return 1; + ret = 1; + goto out_err; } /* @@ -1190,9 +1473,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, 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 @@ -1231,45 +1513,56 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, /* * 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); out: scsi_device_put(sdev); - if (sdev->sdev_state == SDEV_CREATED) + if (scsi_device_created(sdev)) /* * the sdev we used didn't appear in the report luns scan */ - scsi_destroy_sdev(sdev); - return 0; + __scsi_remove_device(sdev); + return ret; } struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, uint id, uint lun, void *hostdata) { - struct scsi_device *sdev; + struct scsi_device *sdev = ERR_PTR(-ENODEV); struct device *parent = &shost->shost_gendev; - int res; - struct scsi_target *starget = scsi_alloc_target(parent, channel, id); + struct scsi_target *starget; + + if (strncmp(scsi_scan_type, "none", 4) == 0) + return ERR_PTR(-ENODEV); + starget = scsi_alloc_target(parent, channel, id); if (!starget) return ERR_PTR(-ENOMEM); + scsi_autopm_get_target(starget); + + mutex_lock(&shost->scan_mutex); + if (!shost->async_scan) + scsi_complete_async_scans(); - get_device(&starget->dev); - down(&shost->scan_mutex); - if (scsi_host_scan_allowed(shost)) { - res = scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, - hostdata); - if (res != SCSI_SCAN_LUN_PRESENT) - sdev = ERR_PTR(-ENODEV); + if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) { + scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); + scsi_autopm_put_host(shost); } - up(&shost->scan_mutex); + mutex_unlock(&shost->scan_mutex); + scsi_autopm_put_target(starget); + /* + * paired with scsi_alloc_target(). Target will be destroyed unless + * scsi_probe_and_add_lun made an underlying device visible + */ scsi_target_reap(starget); put_device(&starget->dev); @@ -1323,8 +1616,8 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, starget = scsi_alloc_target(parent, channel, id); if (!starget) return; + scsi_autopm_get_target(starget); - get_device(&starget->dev); if (lun != SCAN_WILD_CARD) { /* * Scan for a specific host/chan/id/lun. @@ -1345,20 +1638,22 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, * do a sequential scan. */ scsi_sequential_lun_scan(starget, bflags, - res, starget->scsi_level, rescan); + starget->scsi_level, rescan); } out_reap: - /* now determine if the target has any children at all - * and if not, nuke it */ + scsi_autopm_put_target(starget); + /* + * paired with scsi_alloc_target(): 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. + * 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 @@ -1377,10 +1672,18 @@ void scsi_scan_target(struct device *parent, unsigned int channel, { struct Scsi_Host *shost = dev_to_shost(parent); - down(&shost->scan_mutex); - if (scsi_host_scan_allowed(shost)) + if (strncmp(scsi_scan_type, "none", 4) == 0) + return; + + mutex_lock(&shost->scan_mutex); + if (!shost->async_scan) + scsi_complete_async_scans(); + + if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) { __scsi_scan_target(parent, channel, id, lun, rescan); - up(&shost->scan_mutex); + scsi_autopm_put_host(shost); + } + mutex_unlock(&shost->scan_mutex); } EXPORT_SYMBOL(scsi_scan_target); @@ -1418,16 +1721,20 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, 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", + __func__, channel, id, lun)); if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) || - ((id != SCAN_WILD_CARD) && (id > shost->max_id)) || + ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) || ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun))) return -EINVAL; - down(&shost->scan_mutex); - if (scsi_host_scan_allowed(shost)) { + mutex_lock(&shost->scan_mutex); + if (!shost->async_scan) + scsi_complete_async_scans(); + + if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) { if (channel == SCAN_WILD_CARD) for (channel = 0; channel <= shost->max_channel; channel++) @@ -1435,35 +1742,182 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, rescan); else scsi_scan_channel(shost, channel, id, lun, rescan); + scsi_autopm_put_host(shost); } - up(&shost->scan_mutex); + mutex_unlock(&shost->scan_mutex); return 0; } +static void scsi_sysfs_add_devices(struct Scsi_Host *shost) +{ + struct scsi_device *sdev; + shost_for_each_device(sdev, shost) { + /* target removed before the device could be added */ + if (sdev->sdev_state == SDEV_DEL) + continue; + if (!scsi_host_scan_allowed(shost) || + scsi_sysfs_add_sdev(sdev) != 0) + __scsi_remove_device(sdev); + } +} + /** - * scsi_scan_host - scan the given adapter - * @shost: adapter to scan - **/ -void scsi_scan_host(struct Scsi_Host *shost) + * scsi_prep_async_scan - prepare for an async scan + * @shost: the host which will be scanned + * Returns: a cookie to be passed to scsi_finish_async_scan() + * + * Tells the midlayer this host is going to do an asynchronous scan. + * It reserves the host's position in the scanning list and ensures + * that other asynchronous scans started after this one won't affect the + * ordering of the discovered devices. + */ +static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) { - scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD, + struct async_scan_data *data; + unsigned long flags; + + if (strncmp(scsi_scan_type, "sync", 4) == 0) + return NULL; + + if (shost->async_scan) { + printk("%s called twice for host %d", __func__, + shost->host_no); + dump_stack(); + return NULL; + } + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto err; + data->shost = scsi_host_get(shost); + if (!data->shost) + goto err; + init_completion(&data->prev_finished); + + mutex_lock(&shost->scan_mutex); + spin_lock_irqsave(shost->host_lock, flags); + shost->async_scan = 1; + spin_unlock_irqrestore(shost->host_lock, flags); + mutex_unlock(&shost->scan_mutex); + + spin_lock(&async_scan_lock); + if (list_empty(&scanning_hosts)) + complete(&data->prev_finished); + list_add_tail(&data->list, &scanning_hosts); + spin_unlock(&async_scan_lock); + + return data; + + err: + kfree(data); + return NULL; +} + +/** + * scsi_finish_async_scan - asynchronous scan has finished + * @data: cookie returned from earlier call to scsi_prep_async_scan() + * + * All the devices currently attached to this host have been found. + * This function announces all the devices it has found to the rest + * of the system. + */ +static void scsi_finish_async_scan(struct async_scan_data *data) +{ + struct Scsi_Host *shost; + unsigned long flags; + + if (!data) + return; + + shost = data->shost; + + mutex_lock(&shost->scan_mutex); + + if (!shost->async_scan) { + printk("%s called twice for host %d", __func__, + shost->host_no); + dump_stack(); + mutex_unlock(&shost->scan_mutex); + return; + } + + wait_for_completion(&data->prev_finished); + + scsi_sysfs_add_devices(shost); + + spin_lock_irqsave(shost->host_lock, flags); + shost->async_scan = 0; + spin_unlock_irqrestore(shost->host_lock, flags); + + mutex_unlock(&shost->scan_mutex); + + spin_lock(&async_scan_lock); + list_del(&data->list); + if (!list_empty(&scanning_hosts)) { + struct async_scan_data *next = list_entry(scanning_hosts.next, + struct async_scan_data, list); + complete(&next->prev_finished); + } + spin_unlock(&async_scan_lock); + + scsi_autopm_put_host(shost); + scsi_host_put(shost); + kfree(data); +} + +static void do_scsi_scan_host(struct Scsi_Host *shost) +{ + if (shost->hostt->scan_finished) { + unsigned long start = jiffies; + if (shost->hostt->scan_start) + shost->hostt->scan_start(shost); + + while (!shost->hostt->scan_finished(shost, jiffies - start)) + msleep(10); + } else { + scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD, SCAN_WILD_CARD, 0); + } +} + +static void do_scan_async(void *_data, async_cookie_t c) +{ + struct async_scan_data *data = _data; + struct Scsi_Host *shost = data->shost; + + do_scsi_scan_host(shost); + scsi_finish_async_scan(data); } -EXPORT_SYMBOL(scsi_scan_host); /** - * scsi_scan_single_target - scan the given SCSI target - * @shost: adapter to scan - * @chan: channel to scan - * @id: target id to scan + * scsi_scan_host - scan the given adapter + * @shost: adapter to scan **/ -void scsi_scan_single_target(struct Scsi_Host *shost, - unsigned int chan, unsigned int id) +void scsi_scan_host(struct Scsi_Host *shost) { - scsi_scan_host_selected(shost, chan, id, SCAN_WILD_CARD, 1); + struct async_scan_data *data; + + if (strncmp(scsi_scan_type, "none", 4) == 0) + return; + if (scsi_autopm_get_host(shost) < 0) + return; + + data = scsi_prep_async_scan(shost); + if (!data) { + do_scsi_scan_host(shost); + scsi_autopm_put_host(shost); + return; + } + + /* register with the async subsystem so wait_for_device_probe() + * will flush this work + */ + async_schedule(do_scan_async, data); + + /* scsi_autopm_put_host(shost) is called in scsi_finish_async_scan() */ } -EXPORT_SYMBOL(scsi_scan_single_target); +EXPORT_SYMBOL(scsi_scan_host); void scsi_forget_host(struct Scsi_Host *shost) { @@ -1482,32 +1936,29 @@ void scsi_forget_host(struct Scsi_Host *shost) spin_unlock_irqrestore(shost->host_lock, flags); } -/* - * Function: scsi_get_host_dev() - * - * Purpose: Create a Scsi_Device that points to the host adapter itself. - * - * Arguments: SHpnt - Host that needs a Scsi_Device +/** + * scsi_get_host_dev - Create a scsi_device that points to the host adapter itself + * @shost: 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. * * Note - this device is not accessible from any high-level * drivers (including generics), which is probably not - * optimal. We can add hooks later to attach + * optimal. We can add hooks later to attach. */ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) { struct scsi_device *sdev = NULL; struct scsi_target *starget; - down(&shost->scan_mutex); + mutex_lock(&shost->scan_mutex); if (!scsi_host_scan_allowed(shost)) goto out; starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->this_id); @@ -1515,35 +1966,30 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) goto out; sdev = scsi_alloc_sdev(starget, 0, NULL); - if (sdev) { - sdev->sdev_gendev.parent = get_device(&starget->dev); + if (sdev) sdev->borken = 0; - } + else + scsi_target_reap(starget); put_device(&starget->dev); out: - up(&shost->scan_mutex); + 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 +/** + * scsi_free_host_dev - Free a scsi_device that points to the host adapter itself + * @sdev: Host device to be freed * * Lock status: None assumed. * * Returns: Nothing - * - * Notes: */ void scsi_free_host_dev(struct scsi_device *sdev) { BUG_ON(sdev->id != sdev->host->this_id); - scsi_destroy_sdev(sdev); + __scsi_remove_device(sdev); } EXPORT_SYMBOL(scsi_free_host_dev); |
