[SCSI] mpt2sas: Modify code to support Expander switch
Issue : Switch swap doesn't work when device missing delay is enabled. (1) add support to individually add and remove phys to and from existing ports. This replaces the routine _transport_delete_duplicate_port. (2) _scsih_sas_host_refresh - was modified to change the link rate from zero to 1.5 GB rate when the firmware reports there is an attached device with zero link. (3) add new function mpt2sas_device_remove, this is wrapper function deletes some redundant code through out driver by combining into one subrountine (4) two subroutines were modified so the sas_device, raid_device, and port lists are traversed once when objects are deleted from the list. Previously it was looping back each time an object was deleted from the list. Signed-off-by: Kashyap Desai <kashyap.desai@lsi.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:

committed by
James Bottomley

parent
35f805b52c
commit
7f6f794dee
@@ -465,62 +465,149 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* _transport_delete_duplicate_port - (see below description)
|
||||
* _transport_delete_port - helper function to removing a port
|
||||
* @ioc: per adapter object
|
||||
* @sas_node: sas node object (either expander or sas host)
|
||||
* @sas_address: sas address of device being added
|
||||
* @phy_num: phy number
|
||||
* @mpt2sas_port: mpt2sas per port object
|
||||
*
|
||||
* This function is called when attempting to add a new port that is claiming
|
||||
* the same phy resources already in use by another port. If we don't release
|
||||
* the claimed phy resources, the sas transport layer will hang from the BUG
|
||||
* in sas_port_add_phy.
|
||||
*
|
||||
* The reason we would hit this issue is becuase someone is changing the
|
||||
* sas address of a device on the fly, meanwhile controller firmware sends
|
||||
* EVENTs out of order when removing the previous instance of the device.
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void
|
||||
_transport_delete_duplicate_port(struct MPT2SAS_ADAPTER *ioc,
|
||||
struct _sas_node *sas_node, u64 sas_address, int phy_num)
|
||||
_transport_delete_port(struct MPT2SAS_ADAPTER *ioc,
|
||||
struct _sas_port *mpt2sas_port)
|
||||
{
|
||||
struct _sas_port *mpt2sas_port, *mpt2sas_port_duplicate;
|
||||
struct _sas_phy *mpt2sas_phy;
|
||||
u64 sas_address = mpt2sas_port->remote_identify.sas_address;
|
||||
enum sas_device_type device_type =
|
||||
mpt2sas_port->remote_identify.device_type;
|
||||
|
||||
printk(MPT2SAS_ERR_FMT "new device located at sas_addr(0x%016llx), "
|
||||
"phy_id(%d)\n", ioc->name, (unsigned long long)sas_address,
|
||||
phy_num);
|
||||
dev_printk(KERN_INFO, &mpt2sas_port->port->dev,
|
||||
"remove: sas_addr(0x%016llx)\n",
|
||||
(unsigned long long) sas_address);
|
||||
|
||||
mpt2sas_port_duplicate = NULL;
|
||||
list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list, port_list) {
|
||||
dev_printk(KERN_ERR, &mpt2sas_port->port->dev,
|
||||
"existing device at sas_addr(0x%016llx), num_phys(%d)\n",
|
||||
(unsigned long long)
|
||||
mpt2sas_port->remote_identify.sas_address,
|
||||
mpt2sas_port->num_phys);
|
||||
list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list,
|
||||
port_siblings) {
|
||||
dev_printk(KERN_ERR, &mpt2sas_phy->phy->dev,
|
||||
"phy_number(%d)\n", mpt2sas_phy->phy_id);
|
||||
if (mpt2sas_phy->phy_id == phy_num)
|
||||
mpt2sas_port_duplicate = mpt2sas_port;
|
||||
}
|
||||
}
|
||||
ioc->logging_level |= MPT_DEBUG_TRANSPORT;
|
||||
if (device_type == SAS_END_DEVICE)
|
||||
mpt2sas_device_remove(ioc, sas_address);
|
||||
else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
|
||||
device_type == SAS_FANOUT_EXPANDER_DEVICE)
|
||||
mpt2sas_expander_remove(ioc, sas_address);
|
||||
ioc->logging_level &= ~MPT_DEBUG_TRANSPORT;
|
||||
}
|
||||
|
||||
if (!mpt2sas_port_duplicate)
|
||||
/**
|
||||
* _transport_delete_phy - helper function to removing single phy from port
|
||||
* @ioc: per adapter object
|
||||
* @mpt2sas_port: mpt2sas per port object
|
||||
* @mpt2sas_phy: mpt2sas per phy object
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void
|
||||
_transport_delete_phy(struct MPT2SAS_ADAPTER *ioc,
|
||||
struct _sas_port *mpt2sas_port, struct _sas_phy *mpt2sas_phy)
|
||||
{
|
||||
u64 sas_address = mpt2sas_port->remote_identify.sas_address;
|
||||
|
||||
dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
|
||||
"remove: sas_addr(0x%016llx), phy(%d)\n",
|
||||
(unsigned long long) sas_address, mpt2sas_phy->phy_id);
|
||||
|
||||
list_del(&mpt2sas_phy->port_siblings);
|
||||
mpt2sas_port->num_phys--;
|
||||
sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy);
|
||||
mpt2sas_phy->phy_belongs_to_port = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _transport_add_phy - helper function to adding single phy to port
|
||||
* @ioc: per adapter object
|
||||
* @mpt2sas_port: mpt2sas per port object
|
||||
* @mpt2sas_phy: mpt2sas per phy object
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void
|
||||
_transport_add_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_port *mpt2sas_port,
|
||||
struct _sas_phy *mpt2sas_phy)
|
||||
{
|
||||
u64 sas_address = mpt2sas_port->remote_identify.sas_address;
|
||||
|
||||
dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
|
||||
"add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long)
|
||||
sas_address, mpt2sas_phy->phy_id);
|
||||
|
||||
list_add_tail(&mpt2sas_phy->port_siblings, &mpt2sas_port->phy_list);
|
||||
mpt2sas_port->num_phys++;
|
||||
sas_port_add_phy(mpt2sas_port->port, mpt2sas_phy->phy);
|
||||
mpt2sas_phy->phy_belongs_to_port = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* _transport_add_phy_to_an_existing_port - adding new phy to existing port
|
||||
* @ioc: per adapter object
|
||||
* @sas_node: sas node object (either expander or sas host)
|
||||
* @mpt2sas_phy: mpt2sas per phy object
|
||||
* @sas_address: sas address of device/expander were phy needs to be added to
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void
|
||||
_transport_add_phy_to_an_existing_port(struct MPT2SAS_ADAPTER *ioc,
|
||||
struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy, u64 sas_address)
|
||||
{
|
||||
struct _sas_port *mpt2sas_port;
|
||||
struct _sas_phy *phy_srch;
|
||||
|
||||
if (mpt2sas_phy->phy_belongs_to_port == 1)
|
||||
return;
|
||||
|
||||
dev_printk(KERN_ERR, &mpt2sas_port_duplicate->port->dev,
|
||||
"deleting duplicate device at sas_addr(0x%016llx), phy(%d)!!!!\n",
|
||||
(unsigned long long)
|
||||
mpt2sas_port_duplicate->remote_identify.sas_address, phy_num);
|
||||
ioc->logging_level |= MPT_DEBUG_TRANSPORT;
|
||||
mpt2sas_transport_port_remove(ioc,
|
||||
mpt2sas_port_duplicate->remote_identify.sas_address,
|
||||
sas_node->sas_address);
|
||||
ioc->logging_level &= ~MPT_DEBUG_TRANSPORT;
|
||||
list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list,
|
||||
port_list) {
|
||||
if (mpt2sas_port->remote_identify.sas_address !=
|
||||
sas_address)
|
||||
continue;
|
||||
list_for_each_entry(phy_srch, &mpt2sas_port->phy_list,
|
||||
port_siblings) {
|
||||
if (phy_srch == mpt2sas_phy)
|
||||
return;
|
||||
}
|
||||
_transport_add_phy(ioc, mpt2sas_port, mpt2sas_phy);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* _transport_del_phy_from_an_existing_port - delete phy from existing port
|
||||
* @ioc: per adapter object
|
||||
* @sas_node: sas node object (either expander or sas host)
|
||||
* @mpt2sas_phy: mpt2sas per phy object
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void
|
||||
_transport_del_phy_from_an_existing_port(struct MPT2SAS_ADAPTER *ioc,
|
||||
struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy)
|
||||
{
|
||||
struct _sas_port *mpt2sas_port, *next;
|
||||
struct _sas_phy *phy_srch;
|
||||
|
||||
if (mpt2sas_phy->phy_belongs_to_port == 0)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list,
|
||||
port_list) {
|
||||
list_for_each_entry(phy_srch, &mpt2sas_port->phy_list,
|
||||
port_siblings) {
|
||||
if (phy_srch != mpt2sas_phy)
|
||||
continue;
|
||||
if (mpt2sas_port->num_phys == 1)
|
||||
_transport_delete_port(ioc, mpt2sas_port);
|
||||
else
|
||||
_transport_delete_phy(ioc, mpt2sas_port,
|
||||
mpt2sas_phy);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -537,11 +624,13 @@ _transport_sanity_check(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_node,
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sas_node->num_phys; i++)
|
||||
if (sas_node->phy[i].remote_identify.sas_address == sas_address)
|
||||
if (sas_node->phy[i].phy_belongs_to_port)
|
||||
_transport_delete_duplicate_port(ioc, sas_node,
|
||||
sas_address, i);
|
||||
for (i = 0; i < sas_node->num_phys; i++) {
|
||||
if (sas_node->phy[i].remote_identify.sas_address != sas_address)
|
||||
continue;
|
||||
if (sas_node->phy[i].phy_belongs_to_port == 1)
|
||||
_transport_del_phy_from_an_existing_port(ioc, sas_node,
|
||||
&sas_node->phy[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -905,10 +994,12 @@ mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
|
||||
|
||||
mpt2sas_phy = &sas_node->phy[phy_number];
|
||||
mpt2sas_phy->attached_handle = handle;
|
||||
if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5))
|
||||
if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) {
|
||||
_transport_set_identify(ioc, handle,
|
||||
&mpt2sas_phy->remote_identify);
|
||||
else
|
||||
_transport_add_phy_to_an_existing_port(ioc, sas_node,
|
||||
mpt2sas_phy, mpt2sas_phy->remote_identify.sas_address);
|
||||
} else
|
||||
memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct
|
||||
sas_identify));
|
||||
|
||||
|
Reference in New Issue
Block a user