* [RFC] Asynchronous scanning for FC/SAS
@ 2006-10-20 15:56 Matthew Wilcox
0 siblings, 0 replies; only message in thread
From: Matthew Wilcox @ 2006-10-20 15:56 UTC (permalink / raw)
To: linux-scsi
This patch, relative to scsi-misc, adds infrastructure to support
asynchronous scanning for drivers which call scsi_scan_target, and
changes the aic94xx, lpfc and qla2xxx drivers to use it.
Also add the SCSI_SCAN_ASYNC Kconfig option so people can turn it on by
default rather than having to pass a kernel command line param.
Other changes relative to scsi-misc:
- Added missing "none" case to scsi_scan_target()
- Refactor scsi_prep_async_scan/scsi_finish_async_scan so the async_scan_data
is now passed into them rather than being allocated and freed by them.
- Make the above two functions static.
Relative to earlier versions of this patch:
- Add documentation for scsi_target_discovery()
- Handle errors from scsi_prep_async_scan() properly (including sync mode)
- Embed the async_scan_data in the target_discovery_data since they're both
tiny structs.
Todo:
- I'd like a better name than scsi_target_discovery().
- Find out why it still doesn't actually help qla2xxx.
- Testing. Lots and lots of testing.
- See if it can help out the FireWire/USB/iSCSI people
- Convert zfcp to use it
- Work out how to get Fusion using this.
diff --git a/Documentation/scsi/scsi_mid_low_api.txt b/Documentation/scsi/scsi_mid_low_api.txt
index 75a535a..f7e019e 100644
--- a/Documentation/scsi/scsi_mid_low_api.txt
+++ b/Documentation/scsi/scsi_mid_low_api.txt
@@ -388,6 +388,7 @@ Summary:
scsi_remove_host - detach and remove all SCSI devices owned by host
scsi_report_bus_reset - report scsi _bus_ reset observed
scsi_scan_host - scan SCSI bus
+ scsi_target_discovery - register host as doing target discovery
scsi_track_queue_full - track successive QUEUE_FULL events
scsi_unblock_requests - allow further commands to be queued to given host
scsi_unregister - [calls scsi_host_put()]
@@ -718,6 +719,19 @@ void scsi_scan_host(struct Scsi_Host *sh
/**
+ * scsi_target_discovery - register host as performing target discovery
+ * @shost: the host which is discovering targets
+ * @timeout: how long to wait (in jiffies)
+ * @finished: callback to determine if all targets have been discovered
+ *
+ * Might block: yes
+ * Defined in: drivers/scsi/scsi_scan.c
+ */
+void scsi_target_discovery(struct Scsi_Host *shost, unsigned long timeout,
+ int (*finished)(struct Scsi_Host *, unsigned long))
+
+
+/**
* scsi_track_queue_full - track successive QUEUE_FULL events on given
* device to determine if and when there is a need
* to adjust the queue depth on the device.
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 9540eb8..c312444 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -216,6 +216,23 @@ config SCSI_LOGGING
there should be no noticeable performance impact as long as you have
logging turned off.
+config SCSI_SCAN_ASYNC
+ bool "Asynchronous SCSI scanning"
+ depends on SCSI
+ help
+ The SCSI subsystem can probe for devices while the rest of the
+ system continues booting, and even probe devices on different
+ busses in parallel, leading to a significant speed-up.
+ If you have built SCSI as modules, enabling this option can
+ be a problem as the devices may not have been found by the
+ time your system expects them to have been. You can load the
+ scsi_wait_scan module to ensure that all scans have completed.
+ If you build your SCSI drivers into the kernel, then everything
+ will work fine if you say Y here.
+
+ You can override this choice by specifying scsi_mod.scan="sync"
+ or "async" on the kernel's command line.
+
menu "SCSI Transports"
depends on SCSI
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
index 99743ca..8000d88 100644
--- a/drivers/scsi/aic94xx/aic94xx_init.c
+++ b/drivers/scsi/aic94xx/aic94xx_init.c
@@ -546,6 +546,19 @@ static int asd_unregister_sas_ha(struct
return err;
}
+static int asd_finished_discovery(struct Scsi_Host *shost,
+ unsigned long elapsed_jiffies)
+{
+ /* give the phy enabling interrupt event time to come in (1s
+ * is empirically about all it takes) */
+ if (elapsed_jiffies < HZ)
+ return 0;
+
+ /* Wait for discovery to finish */
+ scsi_flush_work(shost);
+ return 1;
+}
+
static int __devinit asd_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
@@ -677,11 +690,7 @@ static int __devinit asd_pci_probe(struc
goto Err_en_phys;
}
ASD_DPRINTK("enabled phys\n");
- /* give the phy enabling interrupt event time to come in (1s
- * is empirically about all it takes) */
- ssleep(1);
- /* Wait for discovery to finish */
- scsi_flush_work(asd_ha->sas_ha.core.shost);
+ scsi_target_discovery(shost, 30 * HZ, asd_finished_discovery);
return 0;
Err_en_phys:
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index a5723ad..c2b0766 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -416,33 +416,6 @@ lpfc_config_port_post(struct lpfc_hba *
return (0);
}
-static int
-lpfc_discovery_wait(struct lpfc_hba *phba)
-{
- int i = 0;
-
- while ((phba->hba_state != LPFC_HBA_READY) ||
- (phba->num_disc_nodes) || (phba->fc_prli_sent) ||
- ((phba->fc_map_cnt == 0) && (i<2)) ||
- (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE)) {
- /* Check every second for 30 retries. */
- i++;
- if (i > 30) {
- return -ETIMEDOUT;
- }
- if ((i >= 15) && (phba->hba_state <= LPFC_LINK_DOWN)) {
- /* The link is down. Set linkdown timeout */
- return -ETIMEDOUT;
- }
-
- /* Delay for 1 second to give discovery time to complete. */
- msleep(1000);
-
- }
-
- return 0;
-}
-
/************************************************************************/
/* */
/* lpfc_hba_down_prep */
@@ -1441,6 +1414,23 @@ lpfc_scsi_free(struct lpfc_hba * phba)
return 0;
}
+static int lpfc_finished_discovery(struct Scsi_Host *shost,
+ unsigned long elapsed_jiffies)
+{
+ struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata;
+
+ if (phba->hba_state != LPFC_HBA_READY)
+ return 0;
+ if (phba->num_disc_nodes || phba->fc_prli_sent)
+ return 0;
+ if ((phba->fc_map_cnt == 0) && (elapsed_jiffies < 2 * HZ))
+ return 0;
+ if (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE)
+ return 0;
+ if ((phba->hba_state > LPFC_LINK_DOWN) || (elapsed_jiffies < 15 * HZ))
+ return 0;
+ return 1;
+}
static int __devinit
lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
@@ -1647,6 +1637,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev,
if (error)
goto out_kthread_stop;
+ scsi_target_discovery(host, 30 * HZ, lpfc_finished_discovery);
error = lpfc_alloc_sysfs_attr(phba);
if (error)
goto out_remove_host;
@@ -1677,8 +1668,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev,
*/
host->can_queue = phba->cfg_hba_queue_depth - 10;
- lpfc_discovery_wait(phba);
-
if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
spin_lock_irq(phba->host->host_lock);
lpfc_poll_start_timer(phba);
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 3f20d76..74726c2 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1353,6 +1353,17 @@ qla24xx_disable_intrs(scsi_qla_host_t *h
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
+static int qla2x00_scan_finished(struct Scsi_Host *shost, unsigned long x)
+{
+ scsi_qla_host_t *ha = (scsi_qla_host_t *)shost->hostdata;
+
+ qla2x00_check_fabric_devices(ha);
+
+ if (ha->device_flags & (DFLG_NO_CABLE | DFLG_FABRIC_DEVICES))
+ return 1;
+ return !(ha->device_flags & SWITCH_FOUND);
+}
+
/*
* PCI driver interface
*/
@@ -1363,8 +1374,7 @@ qla2x00_probe_one(struct pci_dev *pdev,
device_reg_t __iomem *reg;
struct Scsi_Host *host;
scsi_qla_host_t *ha;
- unsigned long flags = 0;
- unsigned long wait_switch = 0;
+ unsigned long flags;
char pci_info[20];
char fw_str[30];
fc_port_t *fcport;
@@ -1614,22 +1624,6 @@ qla2x00_probe_one(struct pci_dev *pdev,
ha->isp_ops.enable_intrs(ha);
- /* v2.19.5b6 */
- /*
- * Wait around max loop_reset_delay secs for the devices to come
- * on-line. We don't want Linux scanning before we are ready.
- *
- */
- for (wait_switch = jiffies + (ha->loop_reset_delay * HZ);
- time_before(jiffies,wait_switch) &&
- !(ha->device_flags & (DFLG_NO_CABLE | DFLG_FABRIC_DEVICES))
- && (ha->device_flags & SWITCH_FOUND) ;) {
-
- qla2x00_check_fabric_devices(ha);
-
- msleep(10);
- }
-
pci_set_drvdata(pdev, ha);
ha->flags.init_done = 1;
num_hosts++;
@@ -1638,6 +1632,8 @@ qla2x00_probe_one(struct pci_dev *pdev,
if (ret)
goto probe_failed;
+ scsi_target_discovery(host, ha->loop_reset_delay * HZ,
+ qla2x00_scan_finished);
qla2x00_alloc_sysfs_attr(ha);
qla2x00_init_host_attr(ha);
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 148e24c..2548fec 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -89,7 +89,13 @@ 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";
+#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");
@@ -1533,6 +1539,9 @@ void scsi_scan_target(struct device *par
{
struct Scsi_Host *shost = dev_to_shost(parent);
+ if (strncmp(scsi_scan_type, "none", 4) == 0)
+ return;
+
if (!shost->async_scan)
scsi_complete_async_scans();
@@ -1616,33 +1625,30 @@ static void scsi_sysfs_add_devices(struc
/**
* 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()
+ * @data: filled in by this function
+ * Returns: an errno, or 0 on success
*
* 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)
+static int scsi_prep_async_scan(struct Scsi_Host *shost,
+ struct async_scan_data *data)
{
- struct async_scan_data *data;
-
if (strncmp(scsi_scan_type, "sync", 4) == 0)
- return NULL;
+ return -EINVAL;
if (shost->async_scan) {
printk("%s called twice for host %d", __FUNCTION__,
shost->host_no);
dump_stack();
- return NULL;
+ return -EBUSY;
}
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- goto err;
data->shost = scsi_host_get(shost);
if (!data->shost)
- goto err;
+ return -ENODEV;
init_completion(&data->prev_finished);
spin_lock(&async_scan_lock);
@@ -1652,22 +1658,18 @@ struct async_scan_data *scsi_prep_async_
list_add_tail(&data->list, &scanning_hosts);
spin_unlock(&async_scan_lock);
- return data;
-
- err:
- kfree(data);
- return NULL;
+ return 0;
}
/**
* scsi_finish_async_scan - asynchronous scan has finished
- * @data: cookie returned from earlier call to scsi_prep_async_scan()
+ * @data: asynchronous scan data
*
* 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)
+static void scsi_finish_async_scan(struct async_scan_data *data)
{
struct Scsi_Host *shost;
@@ -1697,7 +1699,6 @@ void scsi_finish_async_scan(struct async
spin_unlock(&async_scan_lock);
scsi_host_put(shost);
- kfree(data);
}
static int do_scan_async(void *_data)
@@ -1707,6 +1708,7 @@ static int do_scan_async(void *_data)
SCAN_WILD_CARD, 0);
scsi_finish_async_scan(data);
+ kfree(data);
return 0;
}
@@ -1721,16 +1723,100 @@ void scsi_scan_host(struct Scsi_Host *sh
if (strncmp(scsi_scan_type, "none", 4) == 0)
return;
- data = scsi_prep_async_scan(shost);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (data) {
+ int err = scsi_prep_async_scan(shost, data);
+ if (err) {
+ kfree(data);
+ data = NULL;
+ }
+ }
+
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);
+/* If only kthread_run allowed the threadfn to be variadic ... */
+struct target_discovery_data {
+ struct async_scan_data async_data;
+ int (*finished)(struct Scsi_Host *, unsigned long);
+ unsigned long timeout;
+};
+
+static void wait_for_target_scan(struct Scsi_Host *shost, unsigned long timeout,
+ int (*finished)(struct Scsi_Host *, unsigned long))
+{
+ unsigned long start = jiffies;
+ while (time_before(jiffies, start + timeout)) {
+ if (finished(shost, jiffies - start))
+ break;
+ msleep(10);
+ }
+}
+
+static int do_target_discovery(void *_data)
+{
+ struct target_discovery_data *data = _data;
+ wait_for_target_scan(data->async_data.shost, data->timeout,
+ data->finished);
+ scsi_finish_async_scan(&data->async_data);
+ kfree(data);
+ return 0;
+}
+
+/**
+ * scsi_target_discovery - register host as performing target discovery
+ * @shost: the host which is discovering targets
+ * @timeout: how long to wait (in jiffies)
+ * @finished: callback to determine if all targets have been discovered
+ *
+ * Drivers should call this for each host that is going to perform its
+ * own target discovery (ie not if they call scsi_scan_host()). If the
+ * user has requested synchronous target discovery, or there is a problem
+ * with allocating the target struct, it will wait until all targets have
+ * been discovered, or we hit the timeout. Otherwise, it will spawn a
+ * thread and return immediately. Drivers should take care to call this
+ * function before any devices can be discovered, otherwise the first
+ * call to scsi_scan_target() may block, making asynchronous discovery
+ * useless.
+ *
+ * If this function spawns a thread, it will have taken a reference on the
+ * @shost so it cannot disappear from under us. The finished callback will
+ * be called periodically, and passed the number of jiffies that have
+ * elapsed since we started probing. It should return 0 to continue waiting
+ * and non-0 to finish.
+ */
+void scsi_target_discovery(struct Scsi_Host *shost, unsigned long timeout,
+ int (*finished)(struct Scsi_Host *, unsigned long))
+{
+ struct target_discovery_data *data;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (data) {
+ int err = scsi_prep_async_scan(shost, &data->async_data);
+ if (err) {
+ kfree(data);
+ data = NULL;
+ }
+ }
+
+ if (!data) {
+ wait_for_target_scan(shost, timeout, finished);
+ return;
+ }
+
+ data->finished = finished;
+ data->timeout = timeout;
+ kthread_run(do_target_discovery, data, "scsi_scan_%d", shost->host_no);
+}
+EXPORT_SYMBOL_GPL(scsi_target_discovery);
+
void scsi_forget_host(struct Scsi_Host *shost)
{
struct scsi_device *sdev;
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index ba5b3eb..88be61e 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -649,6 +649,9 @@ extern void scsi_host_put(struct Scsi_Ho
extern struct Scsi_Host *scsi_host_lookup(unsigned short);
extern const char *scsi_host_state_name(enum scsi_host_state);
+void scsi_target_discovery(struct Scsi_Host *shost, unsigned long timeout,
+ int (*finished)(struct Scsi_Host *, unsigned long));
+
extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *);
static inline void scsi_assign_lock(struct Scsi_Host *shost, spinlock_t *lock)
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2006-10-20 15:56 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-10-20 15:56 [RFC] Asynchronous scanning for FC/SAS Matthew Wilcox
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.