|
@@ -72,6 +72,7 @@ static int sas_get_port_device(struct asd_sas_port *port)
|
|
|
struct asd_sas_phy *phy;
|
|
|
struct sas_rphy *rphy;
|
|
|
struct domain_device *dev;
|
|
|
+ int rc = -ENODEV;
|
|
|
|
|
|
dev = sas_alloc_device();
|
|
|
if (!dev)
|
|
@@ -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,19 +139,14 @@ static int sas_get_port_device(struct asd_sas_port *port)
|
|
|
|
|
|
if (!rphy) {
|
|
|
sas_put_device(dev);
|
|
|
- return -ENODEV;
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
- 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);
|
|
|
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;
|
|
@@ -155,6 +158,7 @@ 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) || dev->dev_type == SAS_END_DEV)
|
|
|
list_add_tail(&dev->disco_list_node, &port->disco_list);
|
|
@@ -164,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;
|
|
|
}
|
|
|
|
|
@@ -205,8 +214,7 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)
|
|
|
static void sas_probe_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_PROBE, &port->disc.pending);
|
|
@@ -255,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);
|
|
|
|
|
@@ -291,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);
|
|
@@ -302,7 +312,6 @@ 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);
|
|
|
}
|
|
|
}
|
|
@@ -314,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);
|
|
@@ -377,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);
|
|
@@ -419,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);
|
|
@@ -437,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;
|
|
|
|
|
@@ -466,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);
|
|
|
}
|
|
|
}
|
|
@@ -519,7 +528,7 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
}
|