diff options
Diffstat (limited to 'drivers/scsi/libsas')
-rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 7 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 24 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 5 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 1 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_port.c | 7 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 38 |
6 files changed, 55 insertions, 27 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 5fdb63ad94b7..92f7e78a096c 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -284,9 +284,10 @@ static int smp_ata_check_ready(struct ata_link *link) struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; struct domain_device *ex_dev = dev->parent; - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr); + sas_put_local_phy(phy); /* break the wait early if the expander is unreachable, * otherwise keep polling */ @@ -319,10 +320,10 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, unsigned long deadline) { int ret = 0, res; + struct sas_phy *phy; struct ata_port *ap = link->ap; int (*check_ready)(struct ata_link *link); struct domain_device *dev = ap->private_data; - struct sas_phy *phy = sas_find_local_phy(dev); struct sas_internal *i = dev_to_sas_internal(dev); res = i->dft->lldd_I_T_nexus_reset(dev); @@ -330,10 +331,12 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, if (res != TMF_RESP_FUNC_COMPLETE) SAS_DPRINTK("%s: Unable to reset ata device?\n", __func__); + phy = sas_get_local_phy(dev); if (scsi_is_sas_phy_local(phy)) check_ready = local_ata_check_ready; else check_ready = smp_ata_check_ready; + sas_put_local_phy(phy); ret = ata_wait_after_reset(link, deadline, check_ready); if (ret && ret != -EAGAIN) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index c56cc6400819..789b50861bb9 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -147,6 +147,7 @@ static int sas_get_port_device(struct asd_sas_port *port) memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE); memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE); port->disc.max_level = 0; + sas_device_set_phy(dev, port->port); dev->rphy = rphy; @@ -234,6 +235,9 @@ void sas_free_device(struct kref *kref) if (dev->parent) sas_put_device(dev->parent); + sas_port_put_phy(dev->phy); + dev->phy = NULL; + /* remove the phys and ports, everything else should be gone */ if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) kfree(dev->ex_dev.ex_phy); @@ -308,6 +312,26 @@ void sas_unregister_domain_devices(struct asd_sas_port *port) } +void sas_device_set_phy(struct domain_device *dev, struct sas_port *port) +{ + struct sas_ha_struct *ha; + struct sas_phy *new_phy; + + if (!dev) + return; + + ha = dev->port->ha; + new_phy = sas_port_get_phy(port); + + /* pin and record last seen phy */ + spin_lock_irq(&ha->phy_port_lock); + if (new_phy) { + sas_port_put_phy(dev->phy); + dev->phy = new_phy; + } + spin_unlock_irq(&ha->phy_port_lock); +} + /* ---------- Discovery and Revalidation ---------- */ /** diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 6fb1f3afd1e0..68a80a00f73f 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -723,6 +723,7 @@ static struct domain_device *sas_ex_discover_end_dev( } } sas_ex_get_linkrate(parent, child, phy); + sas_device_set_phy(child, phy->port); #ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { @@ -1810,7 +1811,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, { struct expander_device *ex_dev = &parent->ex_dev; struct ex_phy *phy = &ex_dev->ex_phy[phy_id]; - struct domain_device *child, *n; + struct domain_device *child, *n, *found = NULL; if (last) { list_for_each_entry_safe(child, n, &ex_dev->children, siblings) { @@ -1822,6 +1823,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, sas_unregister_ex_tree(parent->port, child); else sas_unregister_dev(parent->port, child); + found = child; break; } } @@ -1830,6 +1832,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); if (phy->port) { sas_port_delete_phy(phy->port, phy->phy); + sas_device_set_phy(found, phy->port); if (phy->port->num_phys == 0) sas_port_delete(phy->port); phy->port = NULL; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index a9a3bb94c1bc..c8febc71c40d 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -87,6 +87,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, enum phy_func phy_func, struct sas_phy_linkrates *); int sas_smp_get_phy_events(struct sas_phy *phy); +void sas_device_set_phy(struct domain_device *dev, struct sas_port *port); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 2980bde4e34a..31adcd1b4191 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -108,9 +108,6 @@ static void sas_form_port(struct asd_sas_phy *phy) 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, @@ -175,8 +172,10 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) sas_unregister_domain_devices(port); 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); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 5cc44fddfe95..94ef76316c31 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -439,30 +439,26 @@ static int sas_recover_I_T(struct domain_device *dev) return res; } -/* Find the sas_phy that's attached to this device */ -struct sas_phy *sas_find_local_phy(struct domain_device *dev) +/* take a reference on the last known good phy for this device */ +struct sas_phy *sas_get_local_phy(struct domain_device *dev) { - struct domain_device *pdev = dev->parent; - struct ex_phy *exphy = NULL; - int i; + struct sas_ha_struct *ha = dev->port->ha; + struct sas_phy *phy; + unsigned long flags; - /* Directly attached device */ - if (!pdev) - return dev->port->phy; + /* a published domain device always has a valid phy, it may be + * stale, but it is never NULL + */ + BUG_ON(!dev->phy); - /* Otherwise look in the expander */ - for (i = 0; i < pdev->ex_dev.num_phys; i++) - if (!memcmp(dev->sas_addr, - pdev->ex_dev.ex_phy[i].attached_sas_addr, - SAS_ADDR_SIZE)) { - exphy = &pdev->ex_dev.ex_phy[i]; - break; - } + spin_lock_irqsave(&ha->phy_port_lock, flags); + phy = dev->phy; + get_device(&phy->dev); + spin_unlock_irqrestore(&ha->phy_port_lock, flags); - BUG_ON(!exphy); - return exphy->phy; + return phy; } -EXPORT_SYMBOL_GPL(sas_find_local_phy); +EXPORT_SYMBOL_GPL(sas_get_local_phy); /* Attempt to send a LUN reset message to a device */ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) @@ -489,7 +485,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) { struct domain_device *dev = cmd_to_domain_dev(cmd); - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); int res; res = sas_phy_reset(phy, 1); @@ -497,6 +493,8 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) SAS_DPRINTK("Bus reset of %s failed 0x%x\n", kobject_name(&phy->dev.kobj), res); + sas_put_local_phy(phy); + if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) return SUCCESS; |