int asd_I_T_nexus_reset(struct domain_device *dev)
 {
        int res, tmp_res, i;
-       struct sas_phy *phy = sas_find_local_phy(dev);
+       struct sas_phy *phy = sas_get_local_phy(dev);
        /* Standard mandates link reset for ATA  (type 0) and
         * hard reset for SSP (type 1) */
        int reset_type = (dev->dev_type == SATA_DEV ||
        for (i = 0 ; i < 3; i++) {
                tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME);
                if (tmp_res == TC_RESUME)
-                       return res;
+                       goto out;
                msleep(500);
        }
 
        dev_printk(KERN_ERR, &phy->dev,
                   "Failed to resume nexus after reset 0x%x\n", tmp_res);
 
-       return TMF_RESP_FUNC_FAILED;
+       res = TMF_RESP_FUNC_FAILED;
+ out:
+       sas_put_local_phy(phy);
+       return res;
 }
 
 static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
 
 static int isci_reset_device(struct isci_host *ihost,
                             struct isci_remote_device *idev)
 {
-       struct sas_phy *phy = sas_find_local_phy(idev->domain_dev);
+       struct sas_phy *phy = sas_get_local_phy(idev->domain_dev);
        enum sci_status status;
        unsigned long flags;
        int rc;
                dev_dbg(&ihost->pdev->dev,
                         "%s: sci_remote_device_reset(%p) returned %d!\n",
                         __func__, idev, status);
-
-               return TMF_RESP_FUNC_FAILED;
+               rc = TMF_RESP_FUNC_FAILED;
+               goto out;
        }
        spin_unlock_irqrestore(&ihost->scic_lock, flags);
 
        }
 
        dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev);
-
+ out:
+       sas_put_local_phy(phy);
        return rc;
 }
 
 
        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
         */
                              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);
        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)
 
        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;
 
        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);
 
 }
 
+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 ---------- */
 
 /**
 
                }
        }
        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) {
 {
        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) {
                                        sas_unregister_ex_tree(parent->port, child);
                                else
                                        sas_unregister_dev(parent->port, child);
+                               found = child;
                                break;
                        }
                }
        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;
 
                        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,
 
        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,
                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);
 
        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)
 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);
                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;
 
 
 static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
 {
        int rc;
-       struct sas_phy *phy = sas_find_local_phy(dev);
+       struct sas_phy *phy = sas_get_local_phy(dev);
        int reset_type = (dev->dev_type == SATA_DEV ||
                        (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
        rc = sas_phy_reset(phy, reset_type);
+       sas_put_local_phy(phy);
        msleep(2000);
        return rc;
 }
 
 
        pm8001_dev = dev->lldd_dev;
        pm8001_ha = pm8001_find_ha_by_dev(dev);
-       phy = sas_find_local_phy(dev);
+       phy = sas_get_local_phy(dev);
 
        if (dev_is_sata(dev)) {
                DECLARE_COMPLETION_ONSTACK(completion_setstate);
-               if (scsi_is_sas_phy_local(phy))
-                       return 0;
+               if (scsi_is_sas_phy_local(phy)) {
+                       rc = 0;
+                       goto out;
+               }
                rc = sas_phy_reset(phy, 1);
                msleep(2000);
                rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
                rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
                        pm8001_dev, 0x01);
                wait_for_completion(&completion_setstate);
-       } else{
-       rc = sas_phy_reset(phy, 1);
-       msleep(2000);
+       } else {
+               rc = sas_phy_reset(phy, 1);
+               msleep(2000);
        }
        PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n",
                pm8001_dev->device_id, rc));
+ out:
+       sas_put_local_phy(phy);
        return rc;
 }
 
        struct pm8001_device *pm8001_dev = dev->lldd_dev;
        struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
        if (dev_is_sata(dev)) {
-               struct sas_phy *phy = sas_find_local_phy(dev);
+               struct sas_phy *phy = sas_get_local_phy(dev);
                rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
                        dev, 1, 0);
                rc = sas_phy_reset(phy, 1);
+               sas_put_local_phy(phy);
                rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
                        pm8001_dev, 0x01);
                msleep(2000);
 
 }
 EXPORT_SYMBOL(scsi_is_sas_port);
 
+/**
+ * sas_port_get_phy - try to take a reference on a port member
+ * @port: port to check
+ */
+struct sas_phy *sas_port_get_phy(struct sas_port *port)
+{
+       struct sas_phy *phy;
+
+       mutex_lock(&port->phy_list_mutex);
+       if (list_empty(&port->phy_list))
+               phy = NULL;
+       else {
+               struct list_head *ent = port->phy_list.next;
+
+               phy = list_entry(ent, typeof(*phy), port_siblings);
+               get_device(&phy->dev);
+       }
+       mutex_unlock(&port->phy_list_mutex);
+
+       return phy;
+}
+EXPORT_SYMBOL(sas_port_get_phy);
+
 /**
  * sas_port_add_phy - add another phy to a port to form a wide port
  * @port:      port to add the phy to
 
         struct domain_device *parent;
         struct list_head siblings; /* devices on the same level */
         struct asd_sas_port *port;        /* shortcut to root of the tree */
+       struct sas_phy *phy;
 
         struct list_head dev_list_node;
        struct list_head disco_list_node; /* awaiting probe or destruct */
        struct list_head destroy_list;
        enum   sas_linkrate linkrate;
 
-       struct sas_phy *phy;
        struct work_struct work;
 
 /* public: */
        return 3 * device + bit;
 }
 
+static inline void sas_put_local_phy(struct sas_phy *phy)
+{
+       put_device(&phy->dev);
+}
+
 #ifdef CONFIG_SCSI_SAS_HOST_SMP
 int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count);
 #else
 
 extern void sas_ssp_task_response(struct device *dev, struct sas_task *task,
                                  struct ssp_response_iu *iu);
-struct sas_phy *sas_find_local_phy(struct domain_device *dev);
+struct sas_phy *sas_get_local_phy(struct domain_device *dev);
 
 int sas_request_addr(struct Scsi_Host *shost, u8 *addr);
 
 
 void sas_port_delete_phy(struct sas_port *, struct sas_phy *);
 void sas_port_mark_backlink(struct sas_port *);
 int scsi_is_sas_port(const struct device *);
+struct sas_phy *sas_port_get_phy(struct sas_port *port);
+static inline void sas_port_put_phy(struct sas_phy *phy)
+{
+       if (phy)
+               put_device(&phy->dev);
+}
 
 extern struct scsi_transport_template *
 sas_attach_transport(struct sas_function_template *);