diff options
Diffstat (limited to 'drivers/scsi/libsas/sas_event.c')
-rw-r--r-- | drivers/scsi/libsas/sas_event.c | 96 |
1 files changed, 91 insertions, 5 deletions
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 9db30fb5caf..16639bbae62 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c | |||
@@ -22,15 +22,103 @@ | |||
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 | 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 | |||
55 | void __sas_drain_work(struct sas_ha_struct *ha) | ||
56 | { | ||
57 | struct workqueue_struct *wq = ha->core.shost->work_q; | ||
58 | struct work_struct *w, *_w; | ||
59 | |||
60 | set_bit(SAS_HA_DRAINING, &ha->state); | ||
61 | /* flush submitters */ | ||
62 | spin_lock_irq(&ha->state_lock); | ||
63 | spin_unlock_irq(&ha->state_lock); | ||
64 | |||
65 | drain_workqueue(wq); | ||
66 | |||
67 | spin_lock_irq(&ha->state_lock); | ||
68 | clear_bit(SAS_HA_DRAINING, &ha->state); | ||
69 | list_for_each_entry_safe(w, _w, &ha->defer_q, entry) { | ||
70 | list_del_init(&w->entry); | ||
71 | sas_queue_work(ha, w); | ||
72 | } | ||
73 | spin_unlock_irq(&ha->state_lock); | ||
74 | } | ||
75 | |||
76 | int sas_drain_work(struct sas_ha_struct *ha) | ||
77 | { | ||
78 | int err; | ||
79 | |||
80 | err = mutex_lock_interruptible(&ha->drain_mutex); | ||
81 | if (err) | ||
82 | return err; | ||
83 | if (test_bit(SAS_HA_REGISTERED, &ha->state)) | ||
84 | __sas_drain_work(ha); | ||
85 | mutex_unlock(&ha->drain_mutex); | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | EXPORT_SYMBOL_GPL(sas_drain_work); | ||
90 | |||
91 | void sas_disable_revalidation(struct sas_ha_struct *ha) | ||
92 | { | ||
93 | mutex_lock(&ha->disco_mutex); | ||
94 | set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); | ||
95 | mutex_unlock(&ha->disco_mutex); | ||
96 | } | ||
97 | |||
98 | void sas_enable_revalidation(struct sas_ha_struct *ha) | ||
99 | { | ||
100 | int i; | ||
101 | |||
102 | mutex_lock(&ha->disco_mutex); | ||
103 | clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); | ||
104 | for (i = 0; i < ha->num_phys; i++) { | ||
105 | struct asd_sas_port *port = ha->sas_port[i]; | ||
106 | const int ev = DISCE_REVALIDATE_DOMAIN; | ||
107 | struct sas_discovery *d = &port->disc; | ||
108 | |||
109 | if (!test_and_clear_bit(ev, &d->pending)) | ||
110 | continue; | ||
111 | |||
112 | sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha); | ||
113 | } | ||
114 | mutex_unlock(&ha->disco_mutex); | ||
115 | } | ||
116 | |||
29 | static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) | 117 | static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) |
30 | { | 118 | { |
31 | BUG_ON(event >= HA_NUM_EVENTS); | 119 | BUG_ON(event >= HA_NUM_EVENTS); |
32 | 120 | ||
33 | sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending, | 121 | sas_queue_event(event, &sas_ha->pending, |
34 | &sas_ha->ha_events[event].work, sas_ha); | 122 | &sas_ha->ha_events[event].work, sas_ha); |
35 | } | 123 | } |
36 | 124 | ||
@@ -40,7 +128,7 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) | |||
40 | 128 | ||
41 | BUG_ON(event >= PORT_NUM_EVENTS); | 129 | BUG_ON(event >= PORT_NUM_EVENTS); |
42 | 130 | ||
43 | sas_queue_event(event, &ha->event_lock, &phy->port_events_pending, | 131 | sas_queue_event(event, &phy->port_events_pending, |
44 | &phy->port_events[event].work, ha); | 132 | &phy->port_events[event].work, ha); |
45 | } | 133 | } |
46 | 134 | ||
@@ -50,7 +138,7 @@ static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) | |||
50 | 138 | ||
51 | BUG_ON(event >= PHY_NUM_EVENTS); | 139 | BUG_ON(event >= PHY_NUM_EVENTS); |
52 | 140 | ||
53 | sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending, | 141 | sas_queue_event(event, &phy->phy_events_pending, |
54 | &phy->phy_events[event].work, ha); | 142 | &phy->phy_events[event].work, ha); |
55 | } | 143 | } |
56 | 144 | ||
@@ -62,8 +150,6 @@ int sas_init_events(struct sas_ha_struct *sas_ha) | |||
62 | 150 | ||
63 | int i; | 151 | int i; |
64 | 152 | ||
65 | spin_lock_init(&sas_ha->event_lock); | ||
66 | |||
67 | for (i = 0; i < HA_NUM_EVENTS; i++) { | 153 | for (i = 0; i < HA_NUM_EVENTS; i++) { |
68 | INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); | 154 | INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); |
69 | sas_ha->ha_events[i].ha = sas_ha; | 155 | sas_ha->ha_events[i].ha = sas_ha; |