X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi_scan.c;h=84b4879cff114f8a6e709deed6e18c67966d9140;hb=63237eeb5ac92d618a0a6055f4b1f65c5d14682b;hp=a43b9ec3aefd66f2953841c964cb2fd2c5746106;hpb=81b7bbd1932a04869d4c8635a75222dfc6089f96;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a43b9ec..84b4879 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -54,7 +54,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' @@ -85,7 +85,7 @@ 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)"); @@ -109,18 +109,19 @@ MODULE_PARM_DESC(scan, "sync, async or none"); */ 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; -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."); +/* This lock protects only this list */ static DEFINE_SPINLOCK(async_scan_lock); static LIST_HEAD(scanning_hosts); @@ -181,9 +182,16 @@ int scsi_complete_async_scans(void) return 0; } -#ifdef MODULE /* Only exported for the benefit of scsi_wait_scan */ EXPORT_SYMBOL_GPL(scsi_complete_async_scans); + +#ifndef MODULE +/* + * For async scanning we need to wait for all the scans to complete before + * trying to mount the root fs. Otherwise non-modular drivers may not be ready + * yet. + */ +late_initcall(scsi_complete_async_scans); #endif /** @@ -213,6 +221,9 @@ static void scsi_unlock_floptical(struct scsi_device *sdev, /** * 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. @@ -228,6 +239,7 @@ 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); sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size, GFP_ATOMIC); @@ -246,7 +258,9 @@ 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); sdev->sdev_gendev.parent = get_device(&starget->dev); sdev->sdev_target = starget; @@ -304,10 +318,25 @@ out_device_destroy: put_device(&sdev->sdev_gendev); 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; + + 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; @@ -317,9 +346,14 @@ static void scsi_target_dev_release(struct device *dev) 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); @@ -370,21 +404,25 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, 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; } 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); +#ifndef CONFIG_SYSFS_DEPRECATED + dev->bus = &scsi_bus_type; +#endif + dev->type = &scsi_target_type; starget->id = id; starget->channel = channel; INIT_LIST_HEAD(&starget->siblings); INIT_LIST_HEAD(&starget->devices); - starget->state = STARGET_RUNNING; + starget->state = STARGET_CREATED; + starget->scsi_level = SCSI_2; retry: spin_lock_irqsave(shost->host_lock, flags); @@ -396,18 +434,6 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, spin_unlock_irqrestore(shost->host_lock, flags); /* 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); @@ -415,9 +441,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, 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; } } @@ -444,23 +468,14 @@ static void scsi_target_reap_usercontext(struct work_struct *work) { struct scsi_target *starget = container_of(work, struct scsi_target, ew.work); - 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_destroy(starget); } /** * 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 @@ -471,21 +486,25 @@ void scsi_target_reap(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; + enum scsi_target_state state; + int empty; spin_lock_irqsave(shost->host_lock, flags); + state = starget->state; + empty = --starget->reap_ref == 0 && + list_empty(&starget->devices) ? 1 : 0; + spin_unlock_irqrestore(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->ew); + if (!empty) return; - } - spin_unlock_irqrestore(shost->host_lock, flags); - - return; + BUG_ON(state == STARGET_DEL); + starget->state = STARGET_DEL; + if (state == STARGET_CREATED) + scsi_target_destroy(starget); + else + execute_in_process_context(scsi_target_reap_usercontext, + &starget->ew); } /** @@ -654,6 +673,19 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned 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, @@ -682,16 +714,14 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned 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 + * @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 @@ -731,25 +761,15 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, sdev->rev = (char *) (sdev->inquiry + 32); if (*bflags & BLIST_ISROM) { - /* - * It would be better to modify sdev->type, and set - * sdev->removable; this can now be done since - * print_inquiry has gone away. - */ - inq_result[0] = TYPE_ROM; - inq_result[1] |= 0x80; /* removable */ - } else if (*bflags & BLIST_NO_ULD_ATTACH) - sdev->no_uld_attach = 1; + 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: - /* RBC 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; - /* fall through */ case TYPE_TAPE: case TYPE_DISK: case TYPE_PRINTER: @@ -763,13 +783,6 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, sdev->writeable = 1; break; case TYPE_ROM: - /* 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; - /* fall through */ case TYPE_WORM: sdev->writeable = 0; break; @@ -777,6 +790,15 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, printk(KERN_INFO "scsi: unknown device type %d\n", sdev->type); } + 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 * spec says: The device server is capable of supporting the @@ -794,12 +816,11 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, */ 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; @@ -812,13 +833,10 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, sdev->inq_periph_qual, inq_result[2] & 0x07, (inq_result[3] & 0x0f) == 1 ? " CCS" : ""); - /* - * End sysfs code. - */ - 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 @@ -827,6 +845,9 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, 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 @@ -849,8 +870,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, 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; @@ -915,8 +935,7 @@ 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 + * 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 @@ -944,9 +963,10 @@ static unsigned char *scsi_inq_str(unsigned char *buf, unsigned char *inq, * 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, @@ -1033,8 +1053,9 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, scsi_inq_str(vend, result, 8, 16), scsi_inq_str(mod, result, 16, 32)); }); + } - + res = SCSI_SCAN_TARGET_PRESENT; goto out_free_result; } @@ -1097,6 +1118,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 + * @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 @@ -1192,7 +1215,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; @@ -1203,10 +1226,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 + * @lun: integer to be reverted * @scsilun: struct scsi_lun to be set. * * Description: @@ -1238,18 +1262,22 @@ 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. * * 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) @@ -1309,7 +1337,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; } @@ -1456,14 +1484,14 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, if (strncmp(scsi_scan_type, "none", 4) == 0) return ERR_PTR(-ENODEV); - if (!shost->async_scan) - scsi_complete_async_scans(); - starget = scsi_alloc_target(parent, channel, id); if (!starget) return ERR_PTR(-ENOMEM); mutex_lock(&shost->scan_mutex); + if (!shost->async_scan) + scsi_complete_async_scans(); + if (scsi_host_scan_allowed(shost)) scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); mutex_unlock(&shost->scan_mutex); @@ -1553,8 +1581,7 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, } /** - * 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 @@ -1576,10 +1603,10 @@ void scsi_scan_target(struct device *parent, unsigned int channel, if (strncmp(scsi_scan_type, "none", 4) == 0) return; + mutex_lock(&shost->scan_mutex); if (!shost->async_scan) scsi_complete_async_scans(); - mutex_lock(&shost->scan_mutex); if (scsi_host_scan_allowed(shost)) __scsi_scan_target(parent, channel, id, lun, rescan); mutex_unlock(&shost->scan_mutex); @@ -1622,10 +1649,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, { SCSI_LOG_SCAN_BUS(3, shost_printk (KERN_INFO, shost, "%s: <%u:%u:%u>\n", - __FUNCTION__, channel, id, lun)); - - if (!shost->async_scan) - scsi_complete_async_scans(); + __func__, channel, id, lun)); if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) || ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) || @@ -1633,6 +1657,9 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, return -EINVAL; mutex_lock(&shost->scan_mutex); + if (!shost->async_scan) + scsi_complete_async_scans(); + if (scsi_host_scan_allowed(shost)) { if (channel == SCAN_WILD_CARD) for (channel = 0; channel <= shost->max_channel; @@ -1651,7 +1678,8 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost) { struct scsi_device *sdev; shost_for_each_device(sdev, shost) { - if (scsi_sysfs_add_sdev(sdev) != 0) + if (!scsi_host_scan_allowed(shost) || + scsi_sysfs_add_sdev(sdev) != 0) scsi_destroy_sdev(sdev); } } @@ -1669,12 +1697,13 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost) static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) { 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", __FUNCTION__, + printk("%s called twice for host %d", __func__, shost->host_no); dump_stack(); return NULL; @@ -1688,8 +1717,13 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) goto err; init_completion(&data->prev_finished); - spin_lock(&async_scan_lock); + 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); @@ -1713,15 +1747,20 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) 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", __FUNCTION__, + printk("%s called twice for host %d", __func__, shost->host_no); dump_stack(); + mutex_unlock(&shost->scan_mutex); return; } @@ -1729,8 +1768,13 @@ static void scsi_finish_async_scan(struct async_scan_data *data) scsi_sysfs_add_devices(shost); - spin_lock(&async_scan_lock); + 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, @@ -1772,6 +1816,7 @@ static int do_scan_async(void *_data) **/ void scsi_scan_host(struct Scsi_Host *shost) { + struct task_struct *p; struct async_scan_data *data; if (strncmp(scsi_scan_type, "none", 4) == 0) @@ -1783,7 +1828,9 @@ void scsi_scan_host(struct Scsi_Host *shost) return; } - kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no); + p = kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no); + if (IS_ERR(p)) + do_scan_async(data); } EXPORT_SYMBOL(scsi_scan_host);