Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 156313
b: refs/heads/master
c: 19252de
h: refs/heads/master
i:
  156311: 10538a8
v: v3
  • Loading branch information
Tom Peng authored and James Bottomley committed Jul 30, 2009
1 parent 5639c13 commit 42ef21f
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 41 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a0cc1ecc098e31d03b3265712a3e280a7fabf438
refs/heads/master: 19252de6818ced0def0551d64a0a2975f52a523d
147 changes: 107 additions & 40 deletions trunk/drivers/scsi/libsas/sas_expander.c
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id)
if (!memcmp(phy->attached_sas_addr, ephy->attached_sas_addr,
SAS_ADDR_SIZE) && ephy->port) {
sas_port_add_phy(ephy->port, phy->phy);
phy->port = ephy->port;
phy->phy_state = PHY_DEVICE_DISCOVERED;
return 0;
}
Expand Down Expand Up @@ -945,11 +946,21 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
if (ex->ex_phy[i].phy_state == PHY_VACANT ||
ex->ex_phy[i].phy_state == PHY_NOT_PRESENT)
continue;

/*
* Due to races, the phy might not get added to the
* wide port, so we add the phy to the wide port here.
*/
if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) ==
SAS_ADDR(child->sas_addr))
SAS_ADDR(child->sas_addr)) {
ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED;
res = sas_ex_join_wide_port(dev, i);
if (!res)
SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n",
i, SAS_ADDR(ex->ex_phy[i].attached_sas_addr));

}
}
res = 0;
}

return res;
Expand Down Expand Up @@ -1598,7 +1609,7 @@ static int sas_get_phy_attached_sas_addr(struct domain_device *dev,
}

static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
int from_phy)
int from_phy, bool update)
{
struct expander_device *ex = &dev->ex_dev;
int res = 0;
Expand All @@ -1611,7 +1622,9 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
if (res)
goto out;
else if (phy_change_count != ex->ex_phy[i].phy_change_count) {
ex->ex_phy[i].phy_change_count = phy_change_count;
if (update)
ex->ex_phy[i].phy_change_count =
phy_change_count;
*phy_id = i;
return 0;
}
Expand Down Expand Up @@ -1653,31 +1666,52 @@ static int sas_get_ex_change_count(struct domain_device *dev, int *ecc)
kfree(rg_req);
return res;
}
/**
* sas_find_bcast_dev - find the device issue BROADCAST(CHANGE).
* @dev:domain device to be detect.
* @src_dev: the device which originated BROADCAST(CHANGE).
*
* Add self-configuration expander suport. Suppose two expander cascading,
* when the first level expander is self-configuring, hotplug the disks in
* second level expander, BROADCAST(CHANGE) will not only be originated
* in the second level expander, but also be originated in the first level
* expander (see SAS protocol SAS 2r-14, 7.11 for detail), it is to say,
* expander changed count in two level expanders will all increment at least
* once, but the phy which chang count has changed is the source device which
* we concerned.
*/

static int sas_find_bcast_dev(struct domain_device *dev,
struct domain_device **src_dev)
{
struct expander_device *ex = &dev->ex_dev;
int ex_change_count = -1;
int phy_id = -1;
int res;
struct domain_device *ch;

res = sas_get_ex_change_count(dev, &ex_change_count);
if (res)
goto out;
if (ex_change_count != -1 &&
ex_change_count != ex->ex_change_count) {
*src_dev = dev;
ex->ex_change_count = ex_change_count;
} else {
struct domain_device *ch;

list_for_each_entry(ch, &ex->children, siblings) {
if (ch->dev_type == EDGE_DEV ||
ch->dev_type == FANOUT_DEV) {
res = sas_find_bcast_dev(ch, src_dev);
if (src_dev)
return res;
}
if (ex_change_count != -1 && ex_change_count != ex->ex_change_count) {
/* Just detect if this expander phys phy change count changed,
* in order to determine if this expander originate BROADCAST,
* and do not update phy change count field in our structure.
*/
res = sas_find_bcast_phy(dev, &phy_id, 0, false);
if (phy_id != -1) {
*src_dev = dev;
ex->ex_change_count = ex_change_count;
SAS_DPRINTK("Expander phy change count has changed\n");
return res;
} else
SAS_DPRINTK("Expander phys DID NOT change\n");
}
list_for_each_entry(ch, &ex->children, siblings) {
if (ch->dev_type == EDGE_DEV || ch->dev_type == FANOUT_DEV) {
res = sas_find_bcast_dev(ch, src_dev);
if (src_dev)
return res;
}
}
out:
Expand All @@ -1700,24 +1734,26 @@ static void sas_unregister_ex_tree(struct domain_device *dev)
}

static void sas_unregister_devs_sas_addr(struct domain_device *parent,
int phy_id)
int phy_id, bool last)
{
struct expander_device *ex_dev = &parent->ex_dev;
struct ex_phy *phy = &ex_dev->ex_phy[phy_id];
struct domain_device *child, *n;

list_for_each_entry_safe(child, n, &ex_dev->children, siblings) {
if (SAS_ADDR(child->sas_addr) ==
SAS_ADDR(phy->attached_sas_addr)) {
if (child->dev_type == EDGE_DEV ||
child->dev_type == FANOUT_DEV)
sas_unregister_ex_tree(child);
else
sas_unregister_dev(child);
break;
if (last) {
list_for_each_entry_safe(child, n,
&ex_dev->children, siblings) {
if (SAS_ADDR(child->sas_addr) ==
SAS_ADDR(phy->attached_sas_addr)) {
if (child->dev_type == EDGE_DEV ||
child->dev_type == FANOUT_DEV)
sas_unregister_ex_tree(child);
else
sas_unregister_dev(child);
break;
}
}
sas_disable_routing(parent, phy->attached_sas_addr);
}
sas_disable_routing(parent, phy->attached_sas_addr);
memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
sas_port_delete_phy(phy->port, phy->phy);
if (phy->port->num_phys == 0)
Expand Down Expand Up @@ -1770,15 +1806,31 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
{
struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id];
struct domain_device *child;
int res;
bool found = false;
int res, i;

SAS_DPRINTK("ex %016llx phy%d new device attached\n",
SAS_ADDR(dev->sas_addr), phy_id);
res = sas_ex_phy_discover(dev, phy_id);
if (res)
goto out;
/* to support the wide port inserted */
for (i = 0; i < dev->ex_dev.num_phys; i++) {
struct ex_phy *ex_phy_temp = &dev->ex_dev.ex_phy[i];
if (i == phy_id)
continue;
if (SAS_ADDR(ex_phy_temp->attached_sas_addr) ==
SAS_ADDR(ex_phy->attached_sas_addr)) {
found = true;
break;
}
}
if (found) {
sas_ex_join_wide_port(dev, phy_id);
return 0;
}
res = sas_ex_discover_devices(dev, phy_id);
if (res)
if (!res)
goto out;
list_for_each_entry(child, &dev->ex_dev.children, siblings) {
if (SAS_ADDR(child->sas_addr) ==
Expand All @@ -1793,7 +1845,7 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
return res;
}

static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)
{
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *phy = &ex->ex_phy[phy_id];
Expand All @@ -1804,19 +1856,19 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
switch (res) {
case SMP_RESP_NO_PHY:
phy->phy_state = PHY_NOT_PRESENT;
sas_unregister_devs_sas_addr(dev, phy_id);
sas_unregister_devs_sas_addr(dev, phy_id, last);
goto out; break;
case SMP_RESP_PHY_VACANT:
phy->phy_state = PHY_VACANT;
sas_unregister_devs_sas_addr(dev, phy_id);
sas_unregister_devs_sas_addr(dev, phy_id, last);
goto out; break;
case SMP_RESP_FUNC_ACC:
break;
}

if (SAS_ADDR(attached_sas_addr) == 0) {
phy->phy_state = PHY_EMPTY;
sas_unregister_devs_sas_addr(dev, phy_id);
sas_unregister_devs_sas_addr(dev, phy_id, last);
} else if (SAS_ADDR(attached_sas_addr) ==
SAS_ADDR(phy->attached_sas_addr)) {
SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
Expand All @@ -1828,12 +1880,27 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
return res;
}

/**
* sas_rediscover - revalidate the domain.
* @dev:domain device to be detect.
* @phy_id: the phy id will be detected.
*
* NOTE: this process _must_ quit (return) as soon as any connection
* errors are encountered. Connection recovery is done elsewhere.
* Discover process only interrogates devices in order to discover the
* domain.For plugging out, we un-register the device only when it is
* the last phy in the port, for other phys in this port, we just delete it
* from the port.For inserting, we do discovery when it is the
* first phy,for other phys in this port, we add it to the port to
* forming the wide-port.
*/
static int sas_rediscover(struct domain_device *dev, const int phy_id)
{
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *changed_phy = &ex->ex_phy[phy_id];
int res = 0;
int i;
bool last = true; /* is this the last phy of the port */

SAS_DPRINTK("ex %016llx phy%d originated BROADCAST(CHANGE)\n",
SAS_ADDR(dev->sas_addr), phy_id);
Expand All @@ -1848,13 +1915,13 @@ static int sas_rediscover(struct domain_device *dev, const int phy_id)
SAS_ADDR(changed_phy->attached_sas_addr)) {
SAS_DPRINTK("phy%d part of wide port with "
"phy%d\n", phy_id, i);
goto out;
last = false;
break;
}
}
res = sas_rediscover_dev(dev, phy_id);
res = sas_rediscover_dev(dev, phy_id, last);
} else
res = sas_discover_new(dev, phy_id);
out:
return res;
}

Expand All @@ -1881,7 +1948,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)

do {
phy_id = -1;
res = sas_find_bcast_phy(dev, &phy_id, i);
res = sas_find_bcast_phy(dev, &phy_id, i, true);
if (phy_id == -1)
break;
res = sas_rediscover(dev, phy_id);
Expand Down

0 comments on commit 42ef21f

Please sign in to comment.