* [PATCH] async scsi scanning, version 13
@ 2006-09-28 21:19 Matthew Wilcox
2006-10-01 20:00 ` James Bottomley
0 siblings, 1 reply; 4+ messages in thread
From: Matthew Wilcox @ 2006-09-28 21:19 UTC (permalink / raw)
To: linux-scsi; +Cc: linux-kernel
Version 13 ...
changes vs version 12:
Address akpm's comments:
- use DEFINE_SPINLOCK
- document how scsi_complete_async_scans works
- document why the memory allocation loop in scsi_complete_async_scans
will eventually terminate
- document why we don't add ourselves to the list if it's already empty
shost_for_each_device is safe and shost_for_each_device_safe isn't.
Delete shost_for_each_device_safe, use shost_for_each_device and update
the docs for __shost_for_each_device and shost_for_each_device to be
more accurate.
changes vs version 11:
Fix whitespace pointed out by Randy Dunlap.
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 5498324..4b687ef 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1426,6 +1426,11 @@ running once the system is up.
scsi_logging= [SCSI]
+ scsi_mod.scan= [SCSI] sync (default) scans SCSI busses as they are
+ discovered. async scans them in kernel threads,
+ allowing boot to proceed. none ignores them, expecting
+ user space to do the scan.
+
selinux [SELINUX] Disable or enable SELinux at boot time.
Format: { "0" | "1" }
See security/selinux/Kconfig help text.
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 1ef951b..38d0e25 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -140,6 +140,8 @@ obj-$(CONFIG_CHR_DEV_SCH) += ch.o
# This goes last, so that "real" scsi devices probe earlier
obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
+obj-$(CONFIG_SCSI) += scsi_wait_scan.o
+
scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \
scsicam.o scsi_error.o scsi_lib.o \
scsi_scan.o scsi_sysfs.o \
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 5d023d4..f458c2f 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -39,6 +39,9 @@ static inline void scsi_log_completion(s
{ };
#endif
+/* scsi_scan.c */
+int scsi_complete_async_scans(void);
+
/* scsi_devinfo.c */
extern int scsi_get_device_flags(struct scsi_device *sdev,
const unsigned char *vendor,
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index fd9e281..148e24c 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -29,7 +29,9 @@ #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 <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -87,6 +89,11 @@ module_param_named(max_luns, max_scsi_lu
MODULE_PARM_DESC(max_luns,
"last scsi LUN (should be between 1 and 2^32-1)");
+static char scsi_scan_type[6] = "sync";
+
+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
@@ -108,6 +115,68 @@ MODULE_PARM_DESC(inq_timeout,
"Timeout (in seconds) waiting for devices to answer INQUIRY."
" Default is 5. Some non-compliant devices need more.");
+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
+ *
+ * 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.
+ */
+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);
+ done:
+ spin_unlock(&async_scan_lock);
+
+ kfree(data);
+ return 0;
+}
+
+#ifdef MODULE
+/* Only exported for the benefit of scsi_wait_scan */
+EXPORT_SYMBOL_GPL(scsi_complete_async_scans);
+#endif
+
/**
* scsi_unlock_floptical - unlock device via a special MODE SENSE command
* @sdev: scsi device to send command to
@@ -619,7 +688,7 @@ static int scsi_probe_lun(struct scsi_de
* SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized
**/
static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
- int *bflags)
+ int *bflags, int async)
{
/*
* XXX do not save the inquiry, since it can change underneath us,
@@ -795,7 +864,7 @@ static int scsi_add_lun(struct scsi_devi
* 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;
@@ -964,7 +1033,7 @@ static int scsi_probe_and_add_lun(struct
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;
@@ -1464,6 +1533,9 @@ void scsi_scan_target(struct device *par
{
struct Scsi_Host *shost = dev_to_shost(parent);
+ 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);
@@ -1509,6 +1581,9 @@ int scsi_scan_host_selected(struct Scsi_
"%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)))
@@ -1529,14 +1604,130 @@ int scsi_scan_host_selected(struct Scsi_
return 0;
}
+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)
+ scsi_destroy_sdev(sdev);
+ }
+}
+
+/**
+ * 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.
+ */
+struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost)
+{
+ struct async_scan_data *data;
+
+ if (strncmp(scsi_scan_type, "sync", 4) == 0)
+ return NULL;
+
+ if (shost->async_scan) {
+ printk("%s called twice for host %d", __FUNCTION__,
+ 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);
+
+ spin_lock(&async_scan_lock);
+ shost->async_scan = 1;
+ 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.
+ */
+void scsi_finish_async_scan(struct async_scan_data *data)
+{
+ struct Scsi_Host *shost;
+
+ if (!data)
+ return;
+
+ shost = data->shost;
+ if (!shost->async_scan) {
+ printk("%s called twice for host %d", __FUNCTION__,
+ shost->host_no);
+ dump_stack();
+ return;
+ }
+
+ wait_for_completion(&data->prev_finished);
+
+ scsi_sysfs_add_devices(shost);
+
+ spin_lock(&async_scan_lock);
+ shost->async_scan = 0;
+ 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_host_put(shost);
+ kfree(data);
+}
+
+static int do_scan_async(void *_data)
+{
+ struct async_scan_data *data = _data;
+ scsi_scan_host_selected(data->shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
+ SCAN_WILD_CARD, 0);
+
+ scsi_finish_async_scan(data);
+ return 0;
+}
+
/**
* scsi_scan_host - scan the given adapter
* @shost: adapter to scan
**/
void scsi_scan_host(struct Scsi_Host *shost)
{
- scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
- SCAN_WILD_CARD, 0);
+ struct async_scan_data *data;
+
+ if (strncmp(scsi_scan_type, "none", 4) == 0)
+ return;
+
+ data = scsi_prep_async_scan(shost);
+ if (!data) {
+ scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
+ SCAN_WILD_CARD, 0);
+ return;
+ }
+ kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no);
}
EXPORT_SYMBOL(scsi_scan_host);
diff --git a/drivers/scsi/scsi_wait_scan.c b/drivers/scsi/scsi_wait_scan.c
new file mode 100644
index 0000000..f7aea46
--- /dev/null
+++ b/drivers/scsi/scsi_wait_scan.c
@@ -0,0 +1,31 @@
+/*
+ * scsi_wait_scan.c
+ *
+ * Copyright (C) 2006 James Bottomley <James.Bottomley@SteelEye.com>
+ *
+ * This is a simple module to wait until all the async scans are
+ * complete. The idea is to use it in initrd/initramfs scripts. You
+ * modprobe it after all the modprobes of the root SCSI drivers and it
+ * will wait until they have all finished scanning their busses before
+ * allowing the boot to proceed
+ */
+
+#include <linux/module.h>
+#include "scsi_priv.h"
+
+static int __init wait_scan_init(void)
+{
+ scsi_complete_async_scans();
+ return 0;
+}
+
+static void __exit wait_scan_exit(void)
+{
+}
+
+MODULE_DESCRIPTION("SCSI wait for scans");
+MODULE_AUTHOR("James Bottomley");
+MODULE_LICENSE("GPL");
+
+late_initcall(wait_scan_init);
+module_exit(wait_scan_exit);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 895d212..0f75a48 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -223,13 +223,13 @@ extern struct scsi_device *__scsi_iterat
struct scsi_device *);
/**
- * shost_for_each_device - iterate over all devices of a host
- * @sdev: iterator
- * @host: host whiches devices we want to iterate over
+ * shost_for_each_device - iterate over all devices of a host
+ * @sdev: the &struct scsi_device to use as a cursor
+ * @shost: the &struct scsi_host to iterate over
*
- * This traverses over each devices of @shost. The devices have
- * a reference that must be released by scsi_host_put when breaking
- * out of the loop.
+ * Iterator that returns each device attached to @shost. This loop
+ * takes a reference on each device and releases it at the end. If
+ * you break out of the loop, you must call scsi_device_put(sdev).
*/
#define shost_for_each_device(sdev, shost) \
for ((sdev) = __scsi_iterate_devices((shost), NULL); \
@@ -237,17 +237,17 @@ #define shost_for_each_device(sdev, shos
(sdev) = __scsi_iterate_devices((shost), (sdev)))
/**
- * __shost_for_each_device - iterate over all devices of a host (UNLOCKED)
- * @sdev: iterator
- * @host: host whiches devices we want to iterate over
+ * __shost_for_each_device - iterate over all devices of a host (UNLOCKED)
+ * @sdev: the &struct scsi_device to use as a cursor
+ * @shost: the &struct scsi_host to iterate over
*
- * This traverses over each devices of @shost. It does _not_ take a
- * reference on the scsi_device, thus it the whole loop must be protected
- * by shost->host_lock.
+ * Iterator that returns each device attached to @shost. It does _not_
+ * take a reference on the scsi_device, so the whole loop must be
+ * protected by shost->host_lock.
*
- * Note: The only reason why drivers would want to use this is because
- * they're need to access the device list in irq context. Otherwise you
- * really want to use shost_for_each_device instead.
+ * Note: The only reason to use this is because you need to access the
+ * device list in interrupt context. Otherwise you really want to use
+ * shost_for_each_device instead.
*/
#define __shost_for_each_device(sdev, shost) \
list_for_each_entry((sdev), &((shost)->__devices), siblings)
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 39c6f8c..ba5b3eb 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -552,6 +552,9 @@ struct Scsi_Host {
/* task mgmt function in progress */
unsigned tmf_in_progress:1;
+ /* Asynchronous scan in progress */
+ unsigned async_scan:1;
+
/*
* Optional work queue to be utilized by the transport
*/
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] async scsi scanning, version 13
2006-09-28 21:19 [PATCH] async scsi scanning, version 13 Matthew Wilcox
@ 2006-10-01 20:00 ` James Bottomley
2006-10-01 20:17 ` Matthew Wilcox
0 siblings, 1 reply; 4+ messages in thread
From: James Bottomley @ 2006-10-01 20:00 UTC (permalink / raw)
To: Matthew Wilcox; +Cc: linux-scsi, linux-kernel
On Thu, 2006-09-28 at 15:19 -0600, Matthew Wilcox wrote:
> Version 13 ...
>
> changes vs version 12:
>
> Address akpm's comments:
> - use DEFINE_SPINLOCK
> - document how scsi_complete_async_scans works
> - document why the memory allocation loop in scsi_complete_async_scans
> will eventually terminate
> - document why we don't add ourselves to the list if it's already empty
>
> shost_for_each_device is safe and shost_for_each_device_safe isn't.
> Delete shost_for_each_device_safe, use shost_for_each_device and update
> the docs for __shost_for_each_device and shost_for_each_device to be
> more accurate.
>
> changes vs version 11:
>
> Fix whitespace pointed out by Randy Dunlap.
OK, my plan for this is to place it in SCSI misc as soon as we get
2.6.19-rc1. That way we'll give it a thorough check out in -mm before
it hits mainline.
By the way, a global change log (rather than changes relative to
previous versions) would be appreciated.
James
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] async scsi scanning, version 13
2006-10-01 20:00 ` James Bottomley
@ 2006-10-01 20:17 ` Matthew Wilcox
2006-10-02 10:02 ` Stefan Richter
0 siblings, 1 reply; 4+ messages in thread
From: Matthew Wilcox @ 2006-10-01 20:17 UTC (permalink / raw)
To: James Bottomley; +Cc: linux-scsi, linux-kernel
On Sun, Oct 01, 2006 at 03:00:57PM -0500, James Bottomley wrote:
> OK, my plan for this is to place it in SCSI misc as soon as we get
> 2.6.19-rc1. That way we'll give it a thorough check out in -mm before
> it hits mainline.
>
> By the way, a global change log (rather than changes relative to
> previous versions) would be appreciated.
Certainly ...
Add ability to scan scsi busses asynchronously
Since it often takes around 20-30 seconds to scan a scsi bus, it's
highly advantageous to do this in parallel with other things. The bulk
of this patch is ensuring that devices don't change numbering, and that
all devices are discovered prior to trying to start init. For those
who build SCSI as modules, there's a new scsi_wait_scan module that will
ensure all bus scans are finished.
This patch only handles drivers which call scsi_scan_host. Fibre Channel,
SAS, SATA, USB and Firewire all need additional work.
Signed-off-by: Matthew Wilcox <matthew@wil.cx>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] async scsi scanning, version 13
2006-10-01 20:17 ` Matthew Wilcox
@ 2006-10-02 10:02 ` Stefan Richter
0 siblings, 0 replies; 4+ messages in thread
From: Stefan Richter @ 2006-10-02 10:02 UTC (permalink / raw)
To: Matthew Wilcox; +Cc: James Bottomley, linux-scsi, linux-kernel
Matthew Wilcox wrote:
> Add ability to scan scsi busses asynchronously
>
> Since it often takes around 20-30 seconds to scan a scsi bus, it's
> highly advantageous to do this in parallel with other things. The bulk
> of this patch is ensuring that devices don't change numbering, and that
> all devices are discovered prior to trying to start init. For those
> who build SCSI as modules, there's a new scsi_wait_scan module that will
> ensure all bus scans are finished.
>
> This patch only handles drivers which call scsi_scan_host. Fibre Channel,
> SAS, SATA, USB and Firewire all need additional work.
A note on FireWire/ SBP-2's status: We basically scan* asynchronously,
although the scans are serialized across all FireWire devices on all
buses. Parallelized scanning is on my long term to-do list. Serialized
scanning is actually quick enough unless something goes wrong.
Integration of sbp2 with scsi_wait_scan is now on my to-do list too. It
is orthogonal to parallelized scanning as a feature but probably
somewhat coupled when it comes to an actual implementation.
So far, the few people who have the root filesystem (or any other
filesystem that they want to have ready on boot) on an SBP-2 disk simply
put a sleep into their initrd or run a more or less complete hotplug
environment from initrd. The scsi_wait_scan module would certainly be
more comfortable than that.
[ *) "scan" = read ISO 13213 configuration ROMs, then run protocol
driver probes including sbp2's login ]
--
Stefan Richter
-=====-=-==- =-=- ---=-
http://arcgraph.de/sr/
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2006-10-02 10:02 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-09-28 21:19 [PATCH] async scsi scanning, version 13 Matthew Wilcox
2006-10-01 20:00 ` James Bottomley
2006-10-01 20:17 ` Matthew Wilcox
2006-10-02 10:02 ` Stefan Richter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox