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; |
