diff options
Diffstat (limited to 'drivers/scsi/libsas/sas_port.c')
| -rw-r--r-- | drivers/scsi/libsas/sas_port.c | 132 |
1 files changed, 92 insertions, 40 deletions
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index fe8b74c706d..d3c5297c6c8 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -28,6 +28,60 @@ #include <scsi/scsi_transport_sas.h> #include "../scsi_sas_internal.h" +static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy *phy) +{ + struct sas_ha_struct *sas_ha = phy->ha; + + if (memcmp(port->attached_sas_addr, phy->attached_sas_addr, + SAS_ADDR_SIZE) != 0 || (sas_ha->strict_wide_ports && + memcmp(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE) != 0)) + return false; + return true; +} + +static void sas_resume_port(struct asd_sas_phy *phy) +{ + struct domain_device *dev; + struct asd_sas_port *port = phy->port; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); + + if (si->dft->lldd_port_formed) + si->dft->lldd_port_formed(phy); + + if (port->suspended) + port->suspended = 0; + else { + /* we only need to handle "link returned" actions once */ + return; + } + + /* if the port came back: + * 1/ presume every device came back + * 2/ force the next revalidation to check all expander phys + */ + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + int i, rc; + + rc = sas_notify_lldd_dev_found(dev); + if (rc) { + sas_unregister_dev(port, dev); + continue; + } + + if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { + dev->ex_dev.ex_change_count = -1; + for (i = 0; i < dev->ex_dev.num_phys; i++) { + struct ex_phy *phy = &dev->ex_dev.ex_phy[i]; + + phy->phy_change_count = -1; + } + } + } + + sas_discover_event(port, DISCE_RESUME); +} + /** * sas_form_port -- add this phy to a port * @phy: the phy of interest @@ -45,10 +99,16 @@ static void sas_form_port(struct asd_sas_phy *phy) unsigned long flags; if (port) { - if (memcmp(port->attached_sas_addr, phy->attached_sas_addr, - SAS_ADDR_SIZE) != 0) - sas_deform_port(phy); - else { + if (!phy_is_wideport_member(port, phy)) + sas_deform_port(phy, 0); + else if (phy->suspended) { + phy->suspended = 0; + sas_resume_port(phy); + + /* phy came back, try to cancel the timeout */ + wake_up(&sas_ha->eh_wait_q); + return; + } else { SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", __func__, phy->id, phy->port->id, phy->port->num_phys); @@ -62,9 +122,7 @@ static void sas_form_port(struct asd_sas_phy *phy) port = sas_ha->sas_port[i]; spin_lock(&port->phy_list_lock); if (*(u64 *) port->sas_addr && - memcmp(port->attached_sas_addr, - phy->attached_sas_addr, SAS_ADDR_SIZE) == 0 && - port->num_phys > 0) { + phy_is_wideport_member(port, phy) && port->num_phys > 0) { /* wide port */ SAS_DPRINTK("phy%d matched wide port%d\n", phy->id, port->id); @@ -96,13 +154,11 @@ static void sas_form_port(struct asd_sas_phy *phy) /* add the phy to the port */ list_add_tail(&phy->port_phy_el, &port->phy_list); + sas_phy_set_target(phy, port->port_dev); phy->port = port; port->num_phys++; port->phy_mask |= (1U << phy->id); - if (!port->phy) - port->phy = phy->phy; - if (*(u64 *)port->attached_sas_addr == 0) { port->class = phy->class; memcpy(port->attached_sas_addr, phy->attached_sas_addr, @@ -145,27 +201,30 @@ static void sas_form_port(struct asd_sas_phy *phy) * This is called when the physical link to the other phy has been * lost (on this phy), in Event thread context. We cannot delay here. */ -void sas_deform_port(struct asd_sas_phy *phy) +void sas_deform_port(struct asd_sas_phy *phy, int gone) { struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); + struct domain_device *dev; unsigned long flags; if (!port) return; /* done by a phy event */ - if (port->port_dev) - port->port_dev->pathways--; + dev = port->port_dev; + if (dev) + dev->pathways--; if (port->num_phys == 1) { - sas_unregister_domain_devices(port); + sas_unregister_domain_devices(port, gone); sas_port_delete(port->port); port->port = NULL; - } else + } else { sas_port_delete_phy(port->port, phy->phy); - + sas_device_set_phy(dev, port->port); + } if (si->dft->lldd_port_deformed) si->dft->lldd_port_deformed(phy); @@ -174,6 +233,7 @@ void sas_deform_port(struct asd_sas_phy *phy) spin_lock(&port->phy_list_lock); list_del_init(&phy->port_phy_el); + sas_phy_set_target(phy, NULL); phy->port = NULL; port->num_phys--; port->phy_mask &= ~(1U << phy->id); @@ -198,26 +258,22 @@ void sas_deform_port(struct asd_sas_phy *phy) void sas_porte_bytes_dmaed(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending); sas_form_port(phy); } void sas_porte_broadcast_rcvd(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; unsigned long flags; u32 prim; - sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending); spin_lock_irqsave(&phy->sas_prim_lock, flags); prim = phy->sas_prim; @@ -229,38 +285,32 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) void sas_porte_link_reset_err(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); - sas_deform_port(phy); + sas_deform_port(phy, 1); } void sas_porte_timer_event(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); - sas_deform_port(phy); + sas_deform_port(phy, 1); } void sas_porte_hard_reset(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); - sas_deform_port(phy); + sas_deform_port(phy, 1); } /* ---------- SAS port registration ---------- */ @@ -271,6 +321,8 @@ static void sas_init_port(struct asd_sas_port *port, memset(port, 0, sizeof(*port)); port->id = i; INIT_LIST_HEAD(&port->dev_list); + INIT_LIST_HEAD(&port->disco_list); + INIT_LIST_HEAD(&port->destroy_list); spin_lock_init(&port->phy_list_lock); INIT_LIST_HEAD(&port->phy_list); port->ha = sas_ha; @@ -298,6 +350,6 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha) for (i = 0; i < sas_ha->num_phys; i++) if (sas_ha->sas_phy[i]->port) - sas_deform_port(sas_ha->sas_phy[i]); + sas_deform_port(sas_ha->sas_phy[i], 0); } |
