aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-12-19 19:42:34 -0500
committerJames Bottomley <JBottomley@Parallels.com>2012-02-19 14:48:51 -0500
commitb1124cd3ec97406c767b90bf7e93ecd2d2915592 (patch)
treed0936775aacff4492177c14a73175738dfb51ee0 /drivers
parentf8daa6e6d83f60a721752cb53433bfdc1503b45f (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.c2
-rw-r--r--drivers/scsi/isci/host.c8
-rw-r--r--drivers/scsi/libsas/sas_discover.c21
-rw-r--r--drivers/scsi/libsas/sas_event.c55
-rw-r--r--drivers/scsi/libsas/sas_init.c9
-rw-r--r--drivers/scsi/libsas/sas_internal.h14
-rw-r--r--drivers/scsi/mvsas/mv_sas.c2
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.c4
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
651int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) 651int 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
370static 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
376static 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
370int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) 389int 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
30static 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
41static 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
54int 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}
82EXPORT_SYMBOL_GPL(sas_drain_work);
83
29static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) 84static 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
95static 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
109static inline void sas_fill_in_rphy(struct domain_device *dev, 95static 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
257int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time) 257int 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