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 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_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 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);
}
+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);
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);
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;
}
}
{
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
{
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);
}
/**
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;
#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,
scsi_inq_str(vend, result, 8, 16),
scsi_inq_str(mod, result, 16, 32));
});
+
}
-
+
res = SCSI_SCAN_TARGET_PRESENT;
goto out_free_result;
}
* 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
/**
* 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)
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);
}
/**
- * 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
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);
"%s: <%u:%u:%u>\n",
__FUNCTION__, channel, id, lun));
- if (!shost->async_scan)
- scsi_complete_async_scans();
-
if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
((id != SCAN_WILD_CARD) && (id >= shost->max_id)) ||
((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
return -EINVAL;
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);
}
}
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;
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);
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__,
shost->host_no);
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,
**/
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)
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 (unlikely(IS_ERR(p)))
+ do_scan_async(data);
}
EXPORT_SYMBOL(scsi_scan_host);