diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-12-19 19:42:34 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-02-19 14:48:51 -0500 |
commit | b1124cd3ec97406c767b90bf7e93ecd2d2915592 (patch) | |
tree | d0936775aacff4492177c14a73175738dfb51ee0 /drivers | |
parent | f8daa6e6d83f60a721752cb53433bfdc1503b45f (diff) |
[SCSI] libsas: introduce sas_drain_work()
When an lldd invokes ->notify_port_event() it can trigger a chain of libsas
events to:
1/ form the port and find the direct attached device
2/ if the attached device is an expander perform domain discovery
A call to flush_workqueue() will only flush the initial port formation work.
Currently libsas users need to call scsi_flush_work() up to the max depth of
chain (which will grow from 2 to 3 when ata discovery is moved to its own
discovery event). Instead of open coding multiple calls switch to use
drain_workqueue() to flush sas work.
drain_workqueue() does not handle new work submitted during the drain so
libsas needs a bit of infrastructure to hold off unchained work submissions
while a drain is in flight. A lldd ->notify() event is considered 'unchained'
while a sas_discover_event() is 'chained'. As Tejun notes:
"For now, I think it would be best to add private wrapper in libsas to
support deferring unchained work items while draining."
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_init.c | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/host.c | 8 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 21 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_event.c | 55 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 9 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 14 | ||||
-rw-r--r-- | drivers/scsi/mvsas/mv_sas.c | 2 | ||||
-rw-r--r-- | drivers/scsi/pm8001/pm8001_sas.c | 4 |
8 files changed, 89 insertions, 26 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 8db4e727628a..2b3717f6d22c 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c | |||
@@ -971,7 +971,7 @@ static int asd_scan_finished(struct Scsi_Host *shost, unsigned long time) | |||
971 | if (time < HZ) | 971 | if (time < HZ) |
972 | return 0; | 972 | return 0; |
973 | /* Wait for discovery to finish */ | 973 | /* Wait for discovery to finish */ |
974 | scsi_flush_work(shost); | 974 | sas_drain_work(SHOST_TO_SAS_HA(shost)); |
975 | return 1; | 975 | return 1; |
976 | } | 976 | } |
977 | 977 | ||
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 508aa8ce25b4..e3cf3832c5b6 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c | |||
@@ -650,15 +650,13 @@ static void isci_host_start_complete(struct isci_host *ihost, enum sci_status co | |||
650 | 650 | ||
651 | int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) | 651 | int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) |
652 | { | 652 | { |
653 | struct isci_host *ihost = SHOST_TO_SAS_HA(shost)->lldd_ha; | 653 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); |
654 | struct isci_host *ihost = ha->lldd_ha; | ||
654 | 655 | ||
655 | if (test_bit(IHOST_START_PENDING, &ihost->flags)) | 656 | if (test_bit(IHOST_START_PENDING, &ihost->flags)) |
656 | return 0; | 657 | return 0; |
657 | 658 | ||
658 | /* todo: use sas_flush_discovery once it is upstream */ | 659 | sas_drain_work(ha); |
659 | scsi_flush_work(shost); | ||
660 | |||
661 | scsi_flush_work(shost); | ||
662 | 660 | ||
663 | dev_dbg(&ihost->pdev->dev, | 661 | dev_dbg(&ihost->pdev->dev, |
664 | "%s: ihost->status = %d, time = %ld\n", | 662 | "%s: ihost->status = %d, time = %ld\n", |
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index ed041189e764..32e011766046 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c | |||
@@ -367,6 +367,25 @@ static void sas_revalidate_domain(struct work_struct *work) | |||
367 | 367 | ||
368 | /* ---------- Events ---------- */ | 368 | /* ---------- Events ---------- */ |
369 | 369 | ||
370 | static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work) | ||
371 | { | ||
372 | /* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */ | ||
373 | scsi_queue_work(ha->core.shost, work); | ||
374 | } | ||
375 | |||
376 | static void sas_chain_event(int event, unsigned long *pending, | ||
377 | struct work_struct *work, | ||
378 | struct sas_ha_struct *ha) | ||
379 | { | ||
380 | if (!test_and_set_bit(event, pending)) { | ||
381 | unsigned long flags; | ||
382 | |||
383 | spin_lock_irqsave(&ha->state_lock, flags); | ||
384 | sas_chain_work(ha, work); | ||
385 | spin_unlock_irqrestore(&ha->state_lock, flags); | ||
386 | } | ||
387 | } | ||
388 | |||
370 | int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) | 389 | int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) |
371 | { | 390 | { |
372 | struct sas_discovery *disc; | 391 | struct sas_discovery *disc; |
@@ -377,7 +396,7 @@ int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) | |||
377 | 396 | ||
378 | BUG_ON(ev >= DISC_NUM_EVENTS); | 397 | BUG_ON(ev >= DISC_NUM_EVENTS); |
379 | 398 | ||
380 | sas_queue_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha); | 399 | sas_chain_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha); |
381 | 400 | ||
382 | return 0; | 401 | return 0; |
383 | } | 402 | } |
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 9c084bc09bbd..e5035aa4c2a6 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c | |||
@@ -22,10 +22,65 @@ | |||
22 | * | 22 | * |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/export.h> | ||
25 | #include <scsi/scsi_host.h> | 26 | #include <scsi/scsi_host.h> |
26 | #include "sas_internal.h" | 27 | #include "sas_internal.h" |
27 | #include "sas_dump.h" | 28 | #include "sas_dump.h" |
28 | 29 | ||
30 | static void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) | ||
31 | { | ||
32 | if (!test_bit(SAS_HA_REGISTERED, &ha->state)) | ||
33 | return; | ||
34 | |||
35 | if (test_bit(SAS_HA_DRAINING, &ha->state)) | ||
36 | list_add(&work->entry, &ha->defer_q); | ||
37 | else | ||
38 | scsi_queue_work(ha->core.shost, work); | ||
39 | } | ||
40 | |||
41 | static void sas_queue_event(int event, unsigned long *pending, | ||
42 | struct work_struct *work, | ||
43 | struct sas_ha_struct *ha) | ||
44 | { | ||
45 | if (!test_and_set_bit(event, pending)) { | ||
46 | unsigned long flags; | ||
47 | |||
48 | spin_lock_irqsave(&ha->state_lock, flags); | ||
49 | sas_queue_work(ha, work); | ||
50 | spin_unlock_irqrestore(&ha->state_lock, flags); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | int sas_drain_work(struct sas_ha_struct *ha) | ||
55 | { | ||
56 | struct workqueue_struct *wq = ha->core.shost->work_q; | ||
57 | struct work_struct *w, *_w; | ||
58 | int err; | ||
59 | |||
60 | err = mutex_lock_interruptible(&ha->drain_mutex); | ||
61 | if (err) | ||
62 | return err; | ||
63 | |||
64 | set_bit(SAS_HA_DRAINING, &ha->state); | ||
65 | /* flush submitters */ | ||
66 | spin_lock_irq(&ha->state_lock); | ||
67 | spin_unlock_irq(&ha->state_lock); | ||
68 | |||
69 | drain_workqueue(wq); | ||
70 | |||
71 | spin_lock_irq(&ha->state_lock); | ||
72 | clear_bit(SAS_HA_DRAINING, &ha->state); | ||
73 | list_for_each_entry_safe(w, _w, &ha->defer_q, entry) { | ||
74 | list_del_init(&w->entry); | ||
75 | sas_queue_work(ha, w); | ||
76 | } | ||
77 | spin_unlock_irq(&ha->state_lock); | ||
78 | mutex_unlock(&ha->drain_mutex); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | EXPORT_SYMBOL_GPL(sas_drain_work); | ||
83 | |||
29 | static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) | 84 | static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) |
30 | { | 85 | { |
31 | BUG_ON(event >= HA_NUM_EVENTS); | 86 | BUG_ON(event >= HA_NUM_EVENTS); |
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index da244e68fe6f..572b943d7603 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c | |||
@@ -114,6 +114,8 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) | |||
114 | 114 | ||
115 | set_bit(SAS_HA_REGISTERED, &sas_ha->state); | 115 | set_bit(SAS_HA_REGISTERED, &sas_ha->state); |
116 | spin_lock_init(&sas_ha->state_lock); | 116 | spin_lock_init(&sas_ha->state_lock); |
117 | mutex_init(&sas_ha->drain_mutex); | ||
118 | INIT_LIST_HEAD(&sas_ha->defer_q); | ||
117 | 119 | ||
118 | error = sas_register_phys(sas_ha); | 120 | error = sas_register_phys(sas_ha); |
119 | if (error) { | 121 | if (error) { |
@@ -157,12 +159,13 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) | |||
157 | { | 159 | { |
158 | unsigned long flags; | 160 | unsigned long flags; |
159 | 161 | ||
160 | /* Set the state to unregistered to avoid further | 162 | /* Set the state to unregistered to avoid further unchained |
161 | * events to be queued */ | 163 | * events to be queued |
164 | */ | ||
162 | spin_lock_irqsave(&sas_ha->state_lock, flags); | 165 | spin_lock_irqsave(&sas_ha->state_lock, flags); |
163 | clear_bit(SAS_HA_REGISTERED, &sas_ha->state); | 166 | clear_bit(SAS_HA_REGISTERED, &sas_ha->state); |
164 | spin_unlock_irqrestore(&sas_ha->state_lock, flags); | 167 | spin_unlock_irqrestore(&sas_ha->state_lock, flags); |
165 | scsi_flush_work(sas_ha->core.shost); | 168 | sas_drain_work(sas_ha); |
166 | 169 | ||
167 | sas_unregister_ports(sas_ha); | 170 | sas_unregister_ports(sas_ha); |
168 | 171 | ||
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 1fd84b3f091f..948ea64cc2eb 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h | |||
@@ -92,20 +92,6 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost, | |||
92 | } | 92 | } |
93 | #endif | 93 | #endif |
94 | 94 | ||
95 | static inline void sas_queue_event(int event, unsigned long *pending, | ||
96 | struct work_struct *work, | ||
97 | struct sas_ha_struct *sas_ha) | ||
98 | { | ||
99 | if (!test_and_set_bit(event, pending)) { | ||
100 | unsigned long flags; | ||
101 | |||
102 | spin_lock_irqsave(&sas_ha->state_lock, flags); | ||
103 | if (test_bit(SAS_HA_REGISTERED, &sas_ha->state)) | ||
104 | scsi_queue_work(sas_ha->core.shost, work); | ||
105 | spin_unlock_irqrestore(&sas_ha->state_lock, flags); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | static inline void sas_fill_in_rphy(struct domain_device *dev, | 95 | static inline void sas_fill_in_rphy(struct domain_device *dev, |
110 | struct sas_rphy *rphy) | 96 | struct sas_rphy *rphy) |
111 | { | 97 | { |
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index a4884a57cf79..b118e632bc7d 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c | |||
@@ -308,7 +308,7 @@ int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time) | |||
308 | if (mvs_prv->scan_finished == 0) | 308 | if (mvs_prv->scan_finished == 0) |
309 | return 0; | 309 | return 0; |
310 | 310 | ||
311 | scsi_flush_work(shost); | 311 | sas_drain_work(sha); |
312 | return 1; | 312 | return 1; |
313 | } | 313 | } |
314 | 314 | ||
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 9589fc941a8b..50837933a1e5 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c | |||
@@ -256,12 +256,14 @@ void pm8001_scan_start(struct Scsi_Host *shost) | |||
256 | 256 | ||
257 | int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time) | 257 | int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time) |
258 | { | 258 | { |
259 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); | ||
260 | |||
259 | /* give the phy enabling interrupt event time to come in (1s | 261 | /* give the phy enabling interrupt event time to come in (1s |
260 | * is empirically about all it takes) */ | 262 | * is empirically about all it takes) */ |
261 | if (time < HZ) | 263 | if (time < HZ) |
262 | return 0; | 264 | return 0; |
263 | /* Wait for discovery to finish */ | 265 | /* Wait for discovery to finish */ |
264 | scsi_flush_work(shost); | 266 | sas_drain_work(ha); |
265 | return 1; | 267 | return 1; |
266 | } | 268 | } |
267 | 269 | ||