diff options
-rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 86 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 69 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_dump.c | 1 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_event.c | 4 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 90 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 1 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_phy.c | 21 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_port.c | 52 | ||||
-rw-r--r-- | include/scsi/libsas.h | 20 | ||||
-rw-r--r-- | include/scsi/sas_ata.h | 10 |
10 files changed, 335 insertions, 19 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index a59fcdc8fd63..905ae45133fc 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c | |||
@@ -700,6 +700,92 @@ void sas_probe_sata(struct asd_sas_port *port) | |||
700 | if (ata_dev_disabled(sas_to_ata_dev(dev))) | 700 | if (ata_dev_disabled(sas_to_ata_dev(dev))) |
701 | sas_fail_probe(dev, __func__, -ENODEV); | 701 | sas_fail_probe(dev, __func__, -ENODEV); |
702 | } | 702 | } |
703 | |||
704 | } | ||
705 | |||
706 | static bool sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func) | ||
707 | { | ||
708 | struct domain_device *dev, *n; | ||
709 | bool retry = false; | ||
710 | |||
711 | list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) { | ||
712 | int rc; | ||
713 | |||
714 | if (!dev_is_sata(dev)) | ||
715 | continue; | ||
716 | |||
717 | sas_ata_wait_eh(dev); | ||
718 | rc = dev->sata_dev.pm_result; | ||
719 | if (rc == -EAGAIN) | ||
720 | retry = true; | ||
721 | else if (rc) { | ||
722 | /* since we don't have a | ||
723 | * ->port_{suspend|resume} routine in our | ||
724 | * ata_port ops, and no entanglements with | ||
725 | * acpi, suspend should just be mechanical trip | ||
726 | * through eh, catch cases where these | ||
727 | * assumptions are invalidated | ||
728 | */ | ||
729 | WARN_ONCE(1, "failed %s %s error: %d\n", func, | ||
730 | dev_name(&dev->rphy->dev), rc); | ||
731 | } | ||
732 | |||
733 | /* if libata failed to power manage the device, tear it down */ | ||
734 | if (ata_dev_disabled(sas_to_ata_dev(dev))) | ||
735 | sas_fail_probe(dev, func, -ENODEV); | ||
736 | } | ||
737 | |||
738 | return retry; | ||
739 | } | ||
740 | |||
741 | void sas_suspend_sata(struct asd_sas_port *port) | ||
742 | { | ||
743 | struct domain_device *dev; | ||
744 | |||
745 | retry: | ||
746 | mutex_lock(&port->ha->disco_mutex); | ||
747 | list_for_each_entry(dev, &port->dev_list, dev_list_node) { | ||
748 | struct sata_device *sata; | ||
749 | |||
750 | if (!dev_is_sata(dev)) | ||
751 | continue; | ||
752 | |||
753 | sata = &dev->sata_dev; | ||
754 | if (sata->ap->pm_mesg.event == PM_EVENT_SUSPEND) | ||
755 | continue; | ||
756 | |||
757 | sata->pm_result = -EIO; | ||
758 | ata_sas_port_async_suspend(sata->ap, &sata->pm_result); | ||
759 | } | ||
760 | mutex_unlock(&port->ha->disco_mutex); | ||
761 | |||
762 | if (sas_ata_flush_pm_eh(port, __func__)) | ||
763 | goto retry; | ||
764 | } | ||
765 | |||
766 | void sas_resume_sata(struct asd_sas_port *port) | ||
767 | { | ||
768 | struct domain_device *dev; | ||
769 | |||
770 | retry: | ||
771 | mutex_lock(&port->ha->disco_mutex); | ||
772 | list_for_each_entry(dev, &port->dev_list, dev_list_node) { | ||
773 | struct sata_device *sata; | ||
774 | |||
775 | if (!dev_is_sata(dev)) | ||
776 | continue; | ||
777 | |||
778 | sata = &dev->sata_dev; | ||
779 | if (sata->ap->pm_mesg.event == PM_EVENT_ON) | ||
780 | continue; | ||
781 | |||
782 | sata->pm_result = -EIO; | ||
783 | ata_sas_port_async_resume(sata->ap, &sata->pm_result); | ||
784 | } | ||
785 | mutex_unlock(&port->ha->disco_mutex); | ||
786 | |||
787 | if (sas_ata_flush_pm_eh(port, __func__)) | ||
788 | goto retry; | ||
703 | } | 789 | } |
704 | 790 | ||
705 | /** | 791 | /** |
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 3e9dc1a84358..a0c3003e0c7d 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include <linux/scatterlist.h> | 25 | #include <linux/scatterlist.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/async.h> | ||
27 | #include <scsi/scsi_host.h> | 28 | #include <scsi/scsi_host.h> |
28 | #include <scsi/scsi_eh.h> | 29 | #include <scsi/scsi_eh.h> |
29 | #include "sas_internal.h" | 30 | #include "sas_internal.h" |
@@ -180,16 +181,18 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) | |||
180 | struct Scsi_Host *shost = sas_ha->core.shost; | 181 | struct Scsi_Host *shost = sas_ha->core.shost; |
181 | struct sas_internal *i = to_sas_internal(shost->transportt); | 182 | struct sas_internal *i = to_sas_internal(shost->transportt); |
182 | 183 | ||
183 | if (i->dft->lldd_dev_found) { | 184 | if (!i->dft->lldd_dev_found) |
184 | res = i->dft->lldd_dev_found(dev); | 185 | return 0; |
185 | if (res) { | 186 | |
186 | printk("sas: driver on pcidev %s cannot handle " | 187 | res = i->dft->lldd_dev_found(dev); |
187 | "device %llx, error:%d\n", | 188 | if (res) { |
188 | dev_name(sas_ha->dev), | 189 | printk("sas: driver on pcidev %s cannot handle " |
189 | SAS_ADDR(dev->sas_addr), res); | 190 | "device %llx, error:%d\n", |
190 | } | 191 | dev_name(sas_ha->dev), |
191 | kref_get(&dev->kref); | 192 | SAS_ADDR(dev->sas_addr), res); |
192 | } | 193 | } |
194 | set_bit(SAS_DEV_FOUND, &dev->state); | ||
195 | kref_get(&dev->kref); | ||
193 | return res; | 196 | return res; |
194 | } | 197 | } |
195 | 198 | ||
@@ -200,7 +203,10 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) | |||
200 | struct Scsi_Host *shost = sas_ha->core.shost; | 203 | struct Scsi_Host *shost = sas_ha->core.shost; |
201 | struct sas_internal *i = to_sas_internal(shost->transportt); | 204 | struct sas_internal *i = to_sas_internal(shost->transportt); |
202 | 205 | ||
203 | if (i->dft->lldd_dev_gone) { | 206 | if (!i->dft->lldd_dev_gone) |
207 | return; | ||
208 | |||
209 | if (test_and_clear_bit(SAS_DEV_FOUND, &dev->state)) { | ||
204 | i->dft->lldd_dev_gone(dev); | 210 | i->dft->lldd_dev_gone(dev); |
205 | sas_put_device(dev); | 211 | sas_put_device(dev); |
206 | } | 212 | } |
@@ -234,6 +240,47 @@ static void sas_probe_devices(struct work_struct *work) | |||
234 | } | 240 | } |
235 | } | 241 | } |
236 | 242 | ||
243 | static void sas_suspend_devices(struct work_struct *work) | ||
244 | { | ||
245 | struct asd_sas_phy *phy; | ||
246 | struct domain_device *dev; | ||
247 | struct sas_discovery_event *ev = to_sas_discovery_event(work); | ||
248 | struct asd_sas_port *port = ev->port; | ||
249 | struct Scsi_Host *shost = port->ha->core.shost; | ||
250 | struct sas_internal *si = to_sas_internal(shost->transportt); | ||
251 | |||
252 | clear_bit(DISCE_SUSPEND, &port->disc.pending); | ||
253 | |||
254 | sas_suspend_sata(port); | ||
255 | |||
256 | /* lldd is free to forget the domain_device across the | ||
257 | * suspension, we force the issue here to keep the reference | ||
258 | * counts aligned | ||
259 | */ | ||
260 | list_for_each_entry(dev, &port->dev_list, dev_list_node) | ||
261 | sas_notify_lldd_dev_gone(dev); | ||
262 | |||
263 | /* we are suspending, so we know events are disabled and | ||
264 | * phy_list is not being mutated | ||
265 | */ | ||
266 | list_for_each_entry(phy, &port->phy_list, port_phy_el) { | ||
267 | if (si->dft->lldd_port_formed) | ||
268 | si->dft->lldd_port_deformed(phy); | ||
269 | phy->suspended = 1; | ||
270 | port->suspended = 1; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | static void sas_resume_devices(struct work_struct *work) | ||
275 | { | ||
276 | struct sas_discovery_event *ev = to_sas_discovery_event(work); | ||
277 | struct asd_sas_port *port = ev->port; | ||
278 | |||
279 | clear_bit(DISCE_RESUME, &port->disc.pending); | ||
280 | |||
281 | sas_resume_sata(port); | ||
282 | } | ||
283 | |||
237 | /** | 284 | /** |
238 | * sas_discover_end_dev -- discover an end device (SSP, etc) | 285 | * sas_discover_end_dev -- discover an end device (SSP, etc) |
239 | * @end: pointer to domain device of interest | 286 | * @end: pointer to domain device of interest |
@@ -530,6 +577,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) | |||
530 | [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, | 577 | [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, |
531 | [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, | 578 | [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, |
532 | [DISCE_PROBE] = sas_probe_devices, | 579 | [DISCE_PROBE] = sas_probe_devices, |
580 | [DISCE_SUSPEND] = sas_suspend_devices, | ||
581 | [DISCE_RESUME] = sas_resume_devices, | ||
533 | [DISCE_DESTRUCT] = sas_destruct_devices, | 582 | [DISCE_DESTRUCT] = sas_destruct_devices, |
534 | }; | 583 | }; |
535 | 584 | ||
diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c index fc460933575c..cd6f99c1ae7e 100644 --- a/drivers/scsi/libsas/sas_dump.c +++ b/drivers/scsi/libsas/sas_dump.c | |||
@@ -41,6 +41,7 @@ static const char *sas_phye_str[] = { | |||
41 | [1] = "PHYE_OOB_DONE", | 41 | [1] = "PHYE_OOB_DONE", |
42 | [2] = "PHYE_OOB_ERROR", | 42 | [2] = "PHYE_OOB_ERROR", |
43 | [3] = "PHYE_SPINUP_HOLD", | 43 | [3] = "PHYE_SPINUP_HOLD", |
44 | [4] = "PHYE_RESUME_TIMEOUT", | ||
44 | }; | 45 | }; |
45 | 46 | ||
46 | void sas_dprint_porte(int phyid, enum port_event pe) | 47 | void sas_dprint_porte(int phyid, enum port_event pe) |
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 789c4d8bb7a7..aadbd5314c5c 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c | |||
@@ -134,7 +134,7 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) | |||
134 | &phy->port_events[event].work, ha); | 134 | &phy->port_events[event].work, ha); |
135 | } | 135 | } |
136 | 136 | ||
137 | static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) | 137 | void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) |
138 | { | 138 | { |
139 | struct sas_ha_struct *ha = phy->ha; | 139 | struct sas_ha_struct *ha = phy->ha; |
140 | 140 | ||
@@ -159,7 +159,7 @@ int sas_init_events(struct sas_ha_struct *sas_ha) | |||
159 | 159 | ||
160 | sas_ha->notify_ha_event = notify_ha_event; | 160 | sas_ha->notify_ha_event = notify_ha_event; |
161 | sas_ha->notify_port_event = notify_port_event; | 161 | sas_ha->notify_port_event = notify_port_event; |
162 | sas_ha->notify_phy_event = notify_phy_event; | 162 | sas_ha->notify_phy_event = sas_notify_phy_event; |
163 | 163 | ||
164 | return 0; | 164 | return 0; |
165 | } | 165 | } |
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 014297c05880..dbc8a793fd86 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c | |||
@@ -178,7 +178,7 @@ Undo_phys: | |||
178 | return error; | 178 | return error; |
179 | } | 179 | } |
180 | 180 | ||
181 | int sas_unregister_ha(struct sas_ha_struct *sas_ha) | 181 | static void sas_disable_events(struct sas_ha_struct *sas_ha) |
182 | { | 182 | { |
183 | /* Set the state to unregistered to avoid further unchained | 183 | /* Set the state to unregistered to avoid further unchained |
184 | * events to be queued, and flush any in-progress drainers | 184 | * events to be queued, and flush any in-progress drainers |
@@ -189,7 +189,11 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) | |||
189 | spin_unlock_irq(&sas_ha->lock); | 189 | spin_unlock_irq(&sas_ha->lock); |
190 | __sas_drain_work(sas_ha); | 190 | __sas_drain_work(sas_ha); |
191 | mutex_unlock(&sas_ha->drain_mutex); | 191 | mutex_unlock(&sas_ha->drain_mutex); |
192 | } | ||
192 | 193 | ||
194 | int sas_unregister_ha(struct sas_ha_struct *sas_ha) | ||
195 | { | ||
196 | sas_disable_events(sas_ha); | ||
193 | sas_unregister_ports(sas_ha); | 197 | sas_unregister_ports(sas_ha); |
194 | 198 | ||
195 | /* flush unregistration work */ | 199 | /* flush unregistration work */ |
@@ -381,6 +385,90 @@ int sas_set_phy_speed(struct sas_phy *phy, | |||
381 | return ret; | 385 | return ret; |
382 | } | 386 | } |
383 | 387 | ||
388 | void sas_prep_resume_ha(struct sas_ha_struct *ha) | ||
389 | { | ||
390 | int i; | ||
391 | |||
392 | set_bit(SAS_HA_REGISTERED, &ha->state); | ||
393 | |||
394 | /* clear out any stale link events/data from the suspension path */ | ||
395 | for (i = 0; i < ha->num_phys; i++) { | ||
396 | struct asd_sas_phy *phy = ha->sas_phy[i]; | ||
397 | |||
398 | memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); | ||
399 | phy->port_events_pending = 0; | ||
400 | phy->phy_events_pending = 0; | ||
401 | phy->frame_rcvd_size = 0; | ||
402 | } | ||
403 | } | ||
404 | EXPORT_SYMBOL(sas_prep_resume_ha); | ||
405 | |||
406 | static int phys_suspended(struct sas_ha_struct *ha) | ||
407 | { | ||
408 | int i, rc = 0; | ||
409 | |||
410 | for (i = 0; i < ha->num_phys; i++) { | ||
411 | struct asd_sas_phy *phy = ha->sas_phy[i]; | ||
412 | |||
413 | if (phy->suspended) | ||
414 | rc++; | ||
415 | } | ||
416 | |||
417 | return rc; | ||
418 | } | ||
419 | |||
420 | void sas_resume_ha(struct sas_ha_struct *ha) | ||
421 | { | ||
422 | const unsigned long tmo = msecs_to_jiffies(25000); | ||
423 | int i; | ||
424 | |||
425 | /* deform ports on phys that did not resume | ||
426 | * at this point we may be racing the phy coming back (as posted | ||
427 | * by the lldd). So we post the event and once we are in the | ||
428 | * libsas context check that the phy remains suspended before | ||
429 | * tearing it down. | ||
430 | */ | ||
431 | i = phys_suspended(ha); | ||
432 | if (i) | ||
433 | dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", | ||
434 | i, i > 1 ? "s" : ""); | ||
435 | wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); | ||
436 | for (i = 0; i < ha->num_phys; i++) { | ||
437 | struct asd_sas_phy *phy = ha->sas_phy[i]; | ||
438 | |||
439 | if (phy->suspended) { | ||
440 | dev_warn(&phy->phy->dev, "resume timeout\n"); | ||
441 | sas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT); | ||
442 | } | ||
443 | } | ||
444 | |||
445 | /* all phys are back up or timed out, turn on i/o so we can | ||
446 | * flush out disks that did not return | ||
447 | */ | ||
448 | scsi_unblock_requests(ha->core.shost); | ||
449 | sas_drain_work(ha); | ||
450 | } | ||
451 | EXPORT_SYMBOL(sas_resume_ha); | ||
452 | |||
453 | void sas_suspend_ha(struct sas_ha_struct *ha) | ||
454 | { | ||
455 | int i; | ||
456 | |||
457 | sas_disable_events(ha); | ||
458 | scsi_block_requests(ha->core.shost); | ||
459 | for (i = 0; i < ha->num_phys; i++) { | ||
460 | struct asd_sas_port *port = ha->sas_port[i]; | ||
461 | |||
462 | sas_discover_event(port, DISCE_SUSPEND); | ||
463 | } | ||
464 | |||
465 | /* flush suspend events while unregistered */ | ||
466 | mutex_lock(&ha->drain_mutex); | ||
467 | __sas_drain_work(ha); | ||
468 | mutex_unlock(&ha->drain_mutex); | ||
469 | } | ||
470 | EXPORT_SYMBOL(sas_suspend_ha); | ||
471 | |||
384 | static void sas_phy_release(struct sas_phy *phy) | 472 | static void sas_phy_release(struct sas_phy *phy) |
385 | { | 473 | { |
386 | kfree(phy->hostdata); | 474 | kfree(phy->hostdata); |
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 507e4cf12e56..1de67964e5a1 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h | |||
@@ -89,6 +89,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, | |||
89 | enum phy_func phy_func, struct sas_phy_linkrates *); | 89 | enum phy_func phy_func, struct sas_phy_linkrates *); |
90 | int sas_smp_get_phy_events(struct sas_phy *phy); | 90 | int sas_smp_get_phy_events(struct sas_phy *phy); |
91 | 91 | ||
92 | void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event); | ||
92 | void sas_device_set_phy(struct domain_device *dev, struct sas_port *port); | 93 | void sas_device_set_phy(struct domain_device *dev, struct sas_port *port); |
93 | struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); | 94 | struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); |
94 | struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); | 95 | struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); |
diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index 521422e857ab..cdee446c29e1 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c | |||
@@ -94,6 +94,25 @@ static void sas_phye_spinup_hold(struct work_struct *work) | |||
94 | i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); | 94 | i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); |
95 | } | 95 | } |
96 | 96 | ||
97 | static void sas_phye_resume_timeout(struct work_struct *work) | ||
98 | { | ||
99 | struct asd_sas_event *ev = to_asd_sas_event(work); | ||
100 | struct asd_sas_phy *phy = ev->phy; | ||
101 | |||
102 | clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending); | ||
103 | |||
104 | /* phew, lldd got the phy back in the nick of time */ | ||
105 | if (!phy->suspended) { | ||
106 | dev_info(&phy->phy->dev, "resume timeout cancelled\n"); | ||
107 | return; | ||
108 | } | ||
109 | |||
110 | phy->error = 0; | ||
111 | phy->suspended = 0; | ||
112 | sas_deform_port(phy, 1); | ||
113 | } | ||
114 | |||
115 | |||
97 | /* ---------- Phy class registration ---------- */ | 116 | /* ---------- Phy class registration ---------- */ |
98 | 117 | ||
99 | int sas_register_phys(struct sas_ha_struct *sas_ha) | 118 | int sas_register_phys(struct sas_ha_struct *sas_ha) |
@@ -105,6 +124,8 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) | |||
105 | [PHYE_OOB_DONE] = sas_phye_oob_done, | 124 | [PHYE_OOB_DONE] = sas_phye_oob_done, |
106 | [PHYE_OOB_ERROR] = sas_phye_oob_error, | 125 | [PHYE_OOB_ERROR] = sas_phye_oob_error, |
107 | [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, | 126 | [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, |
127 | [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, | ||
128 | |||
108 | }; | 129 | }; |
109 | 130 | ||
110 | static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { | 131 | static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { |
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index e884a8c58a0c..1398b714c018 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c | |||
@@ -39,6 +39,49 @@ static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy | |||
39 | return true; | 39 | return true; |
40 | } | 40 | } |
41 | 41 | ||
42 | static void sas_resume_port(struct asd_sas_phy *phy) | ||
43 | { | ||
44 | struct domain_device *dev; | ||
45 | struct asd_sas_port *port = phy->port; | ||
46 | struct sas_ha_struct *sas_ha = phy->ha; | ||
47 | struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); | ||
48 | |||
49 | if (si->dft->lldd_port_formed) | ||
50 | si->dft->lldd_port_formed(phy); | ||
51 | |||
52 | if (port->suspended) | ||
53 | port->suspended = 0; | ||
54 | else { | ||
55 | /* we only need to handle "link returned" actions once */ | ||
56 | return; | ||
57 | } | ||
58 | |||
59 | /* if the port came back: | ||
60 | * 1/ presume every device came back | ||
61 | * 2/ force the next revalidation to check all expander phys | ||
62 | */ | ||
63 | list_for_each_entry(dev, &port->dev_list, dev_list_node) { | ||
64 | int i, rc; | ||
65 | |||
66 | rc = sas_notify_lldd_dev_found(dev); | ||
67 | if (rc) { | ||
68 | sas_unregister_dev(port, dev); | ||
69 | continue; | ||
70 | } | ||
71 | |||
72 | if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) { | ||
73 | dev->ex_dev.ex_change_count = -1; | ||
74 | for (i = 0; i < dev->ex_dev.num_phys; i++) { | ||
75 | struct ex_phy *phy = &dev->ex_dev.ex_phy[i]; | ||
76 | |||
77 | phy->phy_change_count = -1; | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | sas_discover_event(port, DISCE_RESUME); | ||
83 | } | ||
84 | |||
42 | /** | 85 | /** |
43 | * sas_form_port -- add this phy to a port | 86 | * sas_form_port -- add this phy to a port |
44 | * @phy: the phy of interest | 87 | * @phy: the phy of interest |
@@ -58,7 +101,14 @@ static void sas_form_port(struct asd_sas_phy *phy) | |||
58 | if (port) { | 101 | if (port) { |
59 | if (!phy_is_wideport_member(port, phy)) | 102 | if (!phy_is_wideport_member(port, phy)) |
60 | sas_deform_port(phy, 0); | 103 | sas_deform_port(phy, 0); |
61 | else { | 104 | else if (phy->suspended) { |
105 | phy->suspended = 0; | ||
106 | sas_resume_port(phy); | ||
107 | |||
108 | /* phy came back, try to cancel the timeout */ | ||
109 | wake_up(&sas_ha->eh_wait_q); | ||
110 | return; | ||
111 | } else { | ||
62 | SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", | 112 | SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", |
63 | __func__, phy->id, phy->port->id, | 113 | __func__, phy->id, phy->port->id, |
64 | phy->port->num_phys); | 114 | phy->port->num_phys); |
diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index ae33706afeb0..ef937b56f9b5 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h | |||
@@ -79,7 +79,8 @@ enum phy_event { | |||
79 | PHYE_OOB_DONE = 1, | 79 | PHYE_OOB_DONE = 1, |
80 | PHYE_OOB_ERROR = 2, | 80 | PHYE_OOB_ERROR = 2, |
81 | PHYE_SPINUP_HOLD = 3, /* hot plug SATA, no COMWAKE sent */ | 81 | PHYE_SPINUP_HOLD = 3, /* hot plug SATA, no COMWAKE sent */ |
82 | PHY_NUM_EVENTS = 4, | 82 | PHYE_RESUME_TIMEOUT = 4, |
83 | PHY_NUM_EVENTS = 5, | ||
83 | }; | 84 | }; |
84 | 85 | ||
85 | enum discover_event { | 86 | enum discover_event { |
@@ -87,8 +88,10 @@ enum discover_event { | |||
87 | DISCE_REVALIDATE_DOMAIN = 1, | 88 | DISCE_REVALIDATE_DOMAIN = 1, |
88 | DISCE_PORT_GONE = 2, | 89 | DISCE_PORT_GONE = 2, |
89 | DISCE_PROBE = 3, | 90 | DISCE_PROBE = 3, |
90 | DISCE_DESTRUCT = 4, | 91 | DISCE_SUSPEND = 4, |
91 | DISC_NUM_EVENTS = 5, | 92 | DISCE_RESUME = 5, |
93 | DISCE_DESTRUCT = 6, | ||
94 | DISC_NUM_EVENTS = 7, | ||
92 | }; | 95 | }; |
93 | 96 | ||
94 | /* ---------- Expander Devices ---------- */ | 97 | /* ---------- Expander Devices ---------- */ |
@@ -128,7 +131,7 @@ struct ex_phy { | |||
128 | u8 attached_sas_addr[SAS_ADDR_SIZE]; | 131 | u8 attached_sas_addr[SAS_ADDR_SIZE]; |
129 | u8 attached_phy_id; | 132 | u8 attached_phy_id; |
130 | 133 | ||
131 | u8 phy_change_count; | 134 | int phy_change_count; |
132 | enum routing_attribute routing_attr; | 135 | enum routing_attribute routing_attr; |
133 | u8 virtual:1; | 136 | u8 virtual:1; |
134 | 137 | ||
@@ -141,7 +144,7 @@ struct ex_phy { | |||
141 | struct expander_device { | 144 | struct expander_device { |
142 | struct list_head children; | 145 | struct list_head children; |
143 | 146 | ||
144 | u16 ex_change_count; | 147 | int ex_change_count; |
145 | u16 max_route_indexes; | 148 | u16 max_route_indexes; |
146 | u8 num_phys; | 149 | u8 num_phys; |
147 | 150 | ||
@@ -169,6 +172,7 @@ struct sata_device { | |||
169 | enum ata_command_set command_set; | 172 | enum ata_command_set command_set; |
170 | struct smp_resp rps_resp; /* report_phy_sata_resp */ | 173 | struct smp_resp rps_resp; /* report_phy_sata_resp */ |
171 | u8 port_no; /* port number, if this is a PM (Port) */ | 174 | u8 port_no; /* port number, if this is a PM (Port) */ |
175 | int pm_result; | ||
172 | 176 | ||
173 | struct ata_port *ap; | 177 | struct ata_port *ap; |
174 | struct ata_host ata_host; | 178 | struct ata_host ata_host; |
@@ -182,6 +186,7 @@ struct ssp_device { | |||
182 | 186 | ||
183 | enum { | 187 | enum { |
184 | SAS_DEV_GONE, | 188 | SAS_DEV_GONE, |
189 | SAS_DEV_FOUND, /* device notified to lldd */ | ||
185 | SAS_DEV_DESTROY, | 190 | SAS_DEV_DESTROY, |
186 | SAS_DEV_EH_PENDING, | 191 | SAS_DEV_EH_PENDING, |
187 | SAS_DEV_LU_RESET, | 192 | SAS_DEV_LU_RESET, |
@@ -273,6 +278,7 @@ struct asd_sas_port { | |||
273 | enum sas_linkrate linkrate; | 278 | enum sas_linkrate linkrate; |
274 | 279 | ||
275 | struct sas_work work; | 280 | struct sas_work work; |
281 | int suspended; | ||
276 | 282 | ||
277 | /* public: */ | 283 | /* public: */ |
278 | int id; | 284 | int id; |
@@ -321,6 +327,7 @@ struct asd_sas_phy { | |||
321 | unsigned long phy_events_pending; | 327 | unsigned long phy_events_pending; |
322 | 328 | ||
323 | int error; | 329 | int error; |
330 | int suspended; | ||
324 | 331 | ||
325 | struct sas_phy *phy; | 332 | struct sas_phy *phy; |
326 | 333 | ||
@@ -687,6 +694,9 @@ struct sas_domain_function_template { | |||
687 | 694 | ||
688 | extern int sas_register_ha(struct sas_ha_struct *); | 695 | extern int sas_register_ha(struct sas_ha_struct *); |
689 | extern int sas_unregister_ha(struct sas_ha_struct *); | 696 | extern int sas_unregister_ha(struct sas_ha_struct *); |
697 | extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); | ||
698 | extern void sas_resume_ha(struct sas_ha_struct *sas_ha); | ||
699 | extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); | ||
690 | 700 | ||
691 | int sas_set_phy_speed(struct sas_phy *phy, | 701 | int sas_set_phy_speed(struct sas_phy *phy, |
692 | struct sas_phy_linkrates *rates); | 702 | struct sas_phy_linkrates *rates); |
diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 2dfbdaa0b34a..ff71a5654684 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h | |||
@@ -45,6 +45,8 @@ void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, | |||
45 | void sas_ata_schedule_reset(struct domain_device *dev); | 45 | void sas_ata_schedule_reset(struct domain_device *dev); |
46 | void sas_ata_wait_eh(struct domain_device *dev); | 46 | void sas_ata_wait_eh(struct domain_device *dev); |
47 | void sas_probe_sata(struct asd_sas_port *port); | 47 | void sas_probe_sata(struct asd_sas_port *port); |
48 | void sas_suspend_sata(struct asd_sas_port *port); | ||
49 | void sas_resume_sata(struct asd_sas_port *port); | ||
48 | void sas_ata_end_eh(struct ata_port *ap); | 50 | void sas_ata_end_eh(struct ata_port *ap); |
49 | #else | 51 | #else |
50 | 52 | ||
@@ -82,6 +84,14 @@ static inline void sas_probe_sata(struct asd_sas_port *port) | |||
82 | { | 84 | { |
83 | } | 85 | } |
84 | 86 | ||
87 | static inline void sas_suspend_sata(struct asd_sas_port *port) | ||
88 | { | ||
89 | } | ||
90 | |||
91 | static inline void sas_resume_sata(struct asd_sas_port *port) | ||
92 | { | ||
93 | } | ||
94 | |||
85 | static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) | 95 | static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) |
86 | { | 96 | { |
87 | return 0; | 97 | return 0; |