[SCSI] libsas, libata: fix start of life for a sas ata_port
[linux-flexiantxendom0-3.2.10.git] / drivers / scsi / libsas / sas_discover.c
index b91866a..629a086 100644 (file)
@@ -48,6 +48,7 @@ void sas_init_dev(struct domain_device *dev)
        case SATA_DEV:
        case SATA_PM:
        case SATA_PM_PORT:
+       case SATA_PENDING:
                INIT_LIST_HEAD(&dev->sata_dev.children);
                break;
        default:
@@ -68,18 +69,18 @@ void sas_init_dev(struct domain_device *dev)
  */
 static int sas_get_port_device(struct asd_sas_port *port)
 {
-       unsigned long flags;
        struct asd_sas_phy *phy;
        struct sas_rphy *rphy;
        struct domain_device *dev;
+       int rc = -ENODEV;
 
        dev = sas_alloc_device();
        if (!dev)
                return -ENOMEM;
 
-       spin_lock_irqsave(&port->phy_list_lock, flags);
+       spin_lock_irq(&port->phy_list_lock);
        if (list_empty(&port->phy_list)) {
-               spin_unlock_irqrestore(&port->phy_list_lock, flags);
+               spin_unlock_irq(&port->phy_list_lock);
                sas_put_device(dev);
                return -ENODEV;
        }
@@ -88,7 +89,7 @@ static int sas_get_port_device(struct asd_sas_port *port)
        memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd),
                                             (size_t)phy->frame_rcvd_size));
        spin_unlock(&phy->frame_rcvd_lock);
-       spin_unlock_irqrestore(&port->phy_list_lock, flags);
+       spin_unlock_irq(&port->phy_list_lock);
 
        if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) {
                struct dev_to_host_fis *fis =
@@ -110,9 +111,16 @@ static int sas_get_port_device(struct asd_sas_port *port)
 
        sas_init_dev(dev);
 
+       dev->port = port;
        switch (dev->dev_type) {
-       case SAS_END_DEV:
        case SATA_DEV:
+               rc = sas_ata_init(dev);
+               if (rc) {
+                       rphy = NULL;
+                       break;
+               }
+               /* fall through */
+       case SAS_END_DEV:
                rphy = sas_end_device_alloc(port->port);
                break;
        case EDGE_DEV:
@@ -131,14 +139,14 @@ static int sas_get_port_device(struct asd_sas_port *port)
 
        if (!rphy) {
                sas_put_device(dev);
-               return -ENODEV;
+               return rc;
        }
+
        rphy->identify.phy_identifier = phy->phy->identify.phy_identifier;
        memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE);
        sas_fill_in_rphy(dev, rphy);
        sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);
        port->port_dev = dev;
-       dev->port = port;
        dev->linkrate = port->linkrate;
        dev->min_linkrate = port->linkrate;
        dev->max_linkrate = port->linkrate;
@@ -150,8 +158,9 @@ static int sas_get_port_device(struct asd_sas_port *port)
        sas_device_set_phy(dev, port->port);
 
        dev->rphy = rphy;
+       get_device(&dev->rphy->dev);
 
-       if (dev_is_sata(dev))
+       if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEV)
                list_add_tail(&dev->disco_list_node, &port->disco_list);
        else {
                spin_lock_irq(&port->dev_list_lock);
@@ -159,6 +168,11 @@ static int sas_get_port_device(struct asd_sas_port *port)
                spin_unlock_irq(&port->dev_list_lock);
        }
 
+       spin_lock_irq(&port->phy_list_lock);
+       list_for_each_entry(phy, &port->phy_list, port_phy_el)
+               sas_phy_set_target(phy, dev);
+       spin_unlock_irq(&port->phy_list_lock);
+
        return 0;
 }
 
@@ -197,8 +211,33 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)
        }
 }
 
-/* ---------- Common/dispatchers ---------- */
+static void sas_probe_devices(struct work_struct *work)
+{
+       struct domain_device *dev, *n;
+       struct sas_discovery_event *ev = to_sas_discovery_event(work);
+       struct asd_sas_port *port = ev->port;
+
+       clear_bit(DISCE_PROBE, &port->disc.pending);
+
+       /* devices must be domain members before link recovery and probe */
+       list_for_each_entry(dev, &port->disco_list, disco_list_node) {
+               spin_lock_irq(&port->dev_list_lock);
+               list_add_tail(&dev->dev_list_node, &port->dev_list);
+               spin_unlock_irq(&port->dev_list_lock);
+       }
+
+       sas_probe_sata(port);
+
+       list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
+               int err;
 
+               err = sas_rphy_add(dev->rphy);
+               if (err)
+                       sas_fail_probe(dev, __func__, err);
+               else
+                       list_del_init(&dev->disco_list_node);
+       }
+}
 
 /**
  * sas_discover_end_dev -- discover an end device (SSP, etc)
@@ -212,18 +251,10 @@ int sas_discover_end_dev(struct domain_device *dev)
 
        res = sas_notify_lldd_dev_found(dev);
        if (res)
-               goto out_err2;
-
-       res = sas_rphy_add(dev->rphy);
-       if (res)
-               goto out_err;
+               return res;
+       sas_discover_event(dev->port, DISCE_PROBE);
 
        return 0;
-
-out_err:
-       sas_notify_lldd_dev_gone(dev);
-out_err2:
-       return res;
 }
 
 /* ---------- Device registration and unregistration ---------- */
@@ -232,6 +263,9 @@ void sas_free_device(struct kref *kref)
 {
        struct domain_device *dev = container_of(kref, typeof(*dev), kref);
 
+       put_device(&dev->rphy->dev);
+       dev->rphy = NULL;
+
        if (dev->parent)
                sas_put_device(dev->parent);
 
@@ -242,6 +276,11 @@ void sas_free_device(struct kref *kref)
        if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV)
                kfree(dev->ex_dev.ex_phy);
 
+       if (dev_is_sata(dev) && dev->sata_dev.ap) {
+               ata_sas_port_destroy(dev->sata_dev.ap);
+               dev->sata_dev.ap = NULL;
+       }
+
        kfree(dev);
 }
 
@@ -263,8 +302,7 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d
 static void sas_destruct_devices(struct work_struct *work)
 {
        struct domain_device *dev, *n;
-       struct sas_discovery_event *ev =
-               container_of(work, struct sas_discovery_event, work);
+       struct sas_discovery_event *ev = to_sas_discovery_event(work);
        struct asd_sas_port *port = ev->port;
 
        clear_bit(DISCE_DESTRUCT, &port->disc.pending);
@@ -274,10 +312,7 @@ static void sas_destruct_devices(struct work_struct *work)
 
                sas_remove_children(&dev->rphy->dev);
                sas_rphy_delete(dev->rphy);
-               dev->rphy = NULL;
                sas_unregister_common_dev(port, dev);
-
-               sas_put_device(dev);
        }
 }
 
@@ -288,11 +323,11 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
                /* this rphy never saw sas_rphy_add */
                list_del_init(&dev->disco_list_node);
                sas_rphy_free(dev->rphy);
-               dev->rphy = NULL;
                sas_unregister_common_dev(port, dev);
+               return;
        }
 
-       if (dev->rphy && !test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
+       if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
                sas_rphy_unlink(dev->rphy);
                list_move_tail(&dev->disco_list_node, &port->destroy_list);
                sas_discover_event(dev->port, DISCE_DESTRUCT);
@@ -351,8 +386,7 @@ static void sas_discover_domain(struct work_struct *work)
 {
        struct domain_device *dev;
        int error = 0;
-       struct sas_discovery_event *ev =
-               container_of(work, struct sas_discovery_event, work);
+       struct sas_discovery_event *ev = to_sas_discovery_event(work);
        struct asd_sas_port *port = ev->port;
 
        clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending);
@@ -393,8 +427,6 @@ static void sas_discover_domain(struct work_struct *work)
 
        if (error) {
                sas_rphy_free(dev->rphy);
-               dev->rphy = NULL;
-
                list_del_init(&dev->disco_list_node);
                spin_lock_irq(&port->dev_list_lock);
                list_del_init(&dev->dev_list_node);
@@ -411,8 +443,7 @@ static void sas_discover_domain(struct work_struct *work)
 static void sas_revalidate_domain(struct work_struct *work)
 {
        int res = 0;
-       struct sas_discovery_event *ev =
-               container_of(work, struct sas_discovery_event, work);
+       struct sas_discovery_event *ev = to_sas_discovery_event(work);
        struct asd_sas_port *port = ev->port;
        struct sas_ha_struct *ha = port->ha;
 
@@ -440,21 +471,25 @@ static void sas_revalidate_domain(struct work_struct *work)
 
 /* ---------- Events ---------- */
 
-static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work)
+static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw)
 {
-       /* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */
-       scsi_queue_work(ha->core.shost, work);
+       /* chained work is not subject to SA_HA_DRAINING or
+        * SAS_HA_REGISTERED, because it is either submitted in the
+        * workqueue, or known to be submitted from a context that is
+        * not racing against draining
+        */
+       scsi_queue_work(ha->core.shost, &sw->work);
 }
 
 static void sas_chain_event(int event, unsigned long *pending,
-                           struct work_struct *work,
+                           struct sas_work *sw,
                            struct sas_ha_struct *ha)
 {
        if (!test_and_set_bit(event, pending)) {
                unsigned long flags;
 
                spin_lock_irqsave(&ha->state_lock, flags);
-               sas_chain_work(ha, work);
+               sas_chain_work(ha, sw);
                spin_unlock_irqrestore(&ha->state_lock, flags);
        }
 }
@@ -487,13 +522,13 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
        static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {
                [DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
                [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
-               [DISCE_PROBE] = sas_probe_sata,
+               [DISCE_PROBE] = sas_probe_devices,
                [DISCE_DESTRUCT] = sas_destruct_devices,
        };
 
        disc->pending = 0;
        for (i = 0; i < DISC_NUM_EVENTS; i++) {
-               INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]);
+               INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]);
                disc->disc_work[i].port = port;
        }
 }