#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/spinlock.h>
+#include <linux/async.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#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'
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)");
-static char scsi_scan_type[6] = "sync";
+#ifdef CONFIG_SCSI_SCAN_ASYNC
+#define SCSI_SCAN_TYPE_DEFAULT "async"
+#else
+#define SCSI_SCAN_TYPE_DEFAULT "sync"
+#endif
+
+static 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");
*/
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);
/**
* scsi_complete_async_scans - Wait for asynchronous scans to complete
*
- * Asynchronous scans add themselves to the scanning_hosts list. Once
- * that list is empty, we know that the scans are complete. Rather than
- * waking up periodically to check the state of the list, we pretend to be
- * a scanning task by adding ourselves at the end of the list and going to
- * sleep. When the task before us wakes us up, we take ourselves off the
- * list and return.
+ * 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)
{
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);
+ /* Synchronize async operations globally */
+ async_synchronize_full();
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
/**
scsi_cmd[4] = 0x2a; /* size */
scsi_cmd[5] = 0;
scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL,
- SCSI_TIMEOUT, 3);
+ 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.
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);
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;
return sdev;
out_device_destroy:
+ scsi_device_set_state(sdev, SDEV_DEL);
transport_destroy_device(&sdev->sdev_gendev);
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;
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);
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);
+ dev_set_name(dev, "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;
+ starget->can_queue = 0;
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);
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);
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;
}
}
goto retry;
}
-static void scsi_target_reap_usercontext(void *data)
+static void scsi_target_reap_usercontext(struct work_struct *work)
{
- struct scsi_target *starget = data;
- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
- unsigned long flags;
+ struct scsi_target *starget =
+ container_of(work, struct scsi_target, ew.work);
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
{
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, &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);
}
/**
/* 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;
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",
(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;
}
* 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,
/**
* 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
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.
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:
case TYPE_TAPE:
case TYPE_DISK:
case TYPE_PRINTER:
case TYPE_ENCLOSURE:
case TYPE_COMM:
case TYPE_RAID:
- case TYPE_RBC:
+ 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);
}
+ 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
*/
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;
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
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
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;
/* 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;
transport_configure_device(&sdev->sdev_gendev);
if (sdev->host->hostt->slave_configure) {
- int ret = sdev->host->hostt->slave_configure(sdev);
+ ret = sdev->host->hostt->slave_configure(sdev);
if (ret) {
/*
* if LLDD reports slave not present, don't clutter
#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
* 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,
*/
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
sdev_printk(KERN_INFO, sdev,
"scsi scan: consider passing scsi_mod."
- "dev_flags=%s:%s:0x240 or 0x800240\n",
+ "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;
}
* 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) {
+ (result[0] & 0x1f) == 0x1f &&
+ !scsi_is_wlun(lun)) {
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
"scsi scan: peripheral device type"
" of 31, no device added\n"));
* 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
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);
/*
* 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;
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:
/**
* 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)
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;
}
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
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
*/
struct device *parent = &shost->shost_gendev;
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);
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);
}
/**
- * 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
{
struct Scsi_Host *shost = dev_to_shost(parent);
+ 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);
{
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)) ||
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;
{
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);
}
}
* that other asynchronous scans started after this one won't affect the
* ordering of the discovered devices.
*/
-struct async_scan_data *scsi_prep_async_scan(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;
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);
* This function announces all the devices it has found to the rest
* of the system.
*/
-void scsi_finish_async_scan(struct async_scan_data *data)
+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;
}
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,
kfree(data);
}
-static int do_scan_async(void *_data)
+static void do_scsi_scan_host(struct Scsi_Host *shost)
{
- struct async_scan_data *data = _data;
- scsi_scan_host_selected(data->shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
+ 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 int do_scan_async(void *_data)
+{
+ struct async_scan_data *data = _data;
+ do_scsi_scan_host(data->shost);
scsi_finish_async_scan(data);
return 0;
}
**/
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)
data = scsi_prep_async_scan(shost);
if (!data) {
- scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
- SCAN_WILD_CARD, 0);
+ do_scsi_scan_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);