mirror of https://gitee.com/openkylin/linux.git
[SCSI] mpt fusion: SAS topology scan changes, expander events
SAS topology scan is restructured. HBA firmware is generating more events. Expander Events are added, Link status events are also added with respect to SAS topology scan optimization. Signed-off-by: Kashyap Desai <kadesai@lsi.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
parent
3eb0822c67
commit
f9c34022ea
|
@ -704,6 +704,11 @@ typedef struct _MPT_ADAPTER
|
|||
struct mutex sas_discovery_mutex;
|
||||
u8 sas_discovery_runtime;
|
||||
u8 sas_discovery_ignore_events;
|
||||
|
||||
/* port_info object for the host */
|
||||
struct mptsas_portinfo *hba_port_info;
|
||||
u64 hba_port_sas_addr;
|
||||
u16 hba_port_num_phy;
|
||||
struct list_head sas_device_info_list;
|
||||
struct mutex sas_device_info_mutex;
|
||||
u8 sas_discovery_quiesce_io;
|
||||
|
|
|
@ -113,6 +113,12 @@ static int mptsas_add_end_device(MPT_ADAPTER *ioc,
|
|||
struct mptsas_phyinfo *phy_info);
|
||||
static void mptsas_del_end_device(MPT_ADAPTER *ioc,
|
||||
struct mptsas_phyinfo *phy_info);
|
||||
static void mptsas_send_link_status_event(struct fw_event_work *fw_event);
|
||||
static struct mptsas_portinfo *mptsas_find_portinfo_by_sas_address
|
||||
(MPT_ADAPTER *ioc, u64 sas_address);
|
||||
static void mptsas_expander_delete(MPT_ADAPTER *ioc,
|
||||
struct mptsas_portinfo *port_info, u8 force);
|
||||
static void mptsas_send_expander_event(struct fw_event_work *fw_event);
|
||||
|
||||
static void mptsas_print_phy_data(MPT_ADAPTER *ioc,
|
||||
MPI_SAS_IO_UNIT0_PHY_DATA *phy_data)
|
||||
|
@ -342,20 +348,6 @@ static inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy)
|
|||
return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
|
||||
}
|
||||
|
||||
static struct mptsas_portinfo *
|
||||
mptsas_get_hba_portinfo(MPT_ADAPTER *ioc)
|
||||
{
|
||||
struct list_head *head = &ioc->sas_topology;
|
||||
struct mptsas_portinfo *pi = NULL;
|
||||
|
||||
/* always the first entry on sas_topology list */
|
||||
|
||||
if (!list_empty(head))
|
||||
pi = list_entry(head->next, struct mptsas_portinfo, list);
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
/*
|
||||
* mptsas_find_portinfo_by_handle
|
||||
*
|
||||
|
@ -377,6 +369,38 @@ mptsas_find_portinfo_by_handle(MPT_ADAPTER *ioc, u16 handle)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* mptsas_find_portinfo_by_sas_address -
|
||||
* @ioc: Pointer to MPT_ADAPTER structure
|
||||
* @handle:
|
||||
*
|
||||
* This function should be called with the sas_topology_mutex already held
|
||||
*
|
||||
**/
|
||||
static struct mptsas_portinfo *
|
||||
mptsas_find_portinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
|
||||
{
|
||||
struct mptsas_portinfo *port_info, *rc = NULL;
|
||||
int i;
|
||||
|
||||
if (sas_address >= ioc->hba_port_sas_addr &&
|
||||
sas_address < (ioc->hba_port_sas_addr +
|
||||
ioc->hba_port_num_phy))
|
||||
return ioc->hba_port_info;
|
||||
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
list_for_each_entry(port_info, &ioc->sas_topology, list)
|
||||
for (i = 0; i < port_info->num_phys; i++)
|
||||
if (port_info->phy_info[i].identify.sas_address ==
|
||||
sas_address) {
|
||||
rc = port_info;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if there is a scsi end device
|
||||
*/
|
||||
|
@ -940,7 +964,6 @@ mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
|
|||
{
|
||||
MPT_SCSI_HOST *hd = shost_priv(ioc->sh);
|
||||
struct list_head *head = &hd->target_reset_list;
|
||||
EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data;
|
||||
u8 id, channel;
|
||||
struct mptsas_target_reset_event *target_reset_list;
|
||||
SCSITaskMgmtReply_t *pScsiTmReply;
|
||||
|
@ -995,7 +1018,6 @@ mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
|
|||
ioc->name, jiffies_to_msecs(jiffies -
|
||||
target_reset_list->time_count)/1000));
|
||||
|
||||
sas_event_data = &target_reset_list->sas_event_data;
|
||||
id = pScsiTmReply->TargetID;
|
||||
channel = pScsiTmReply->Bus;
|
||||
target_reset_list->time_count = jiffies;
|
||||
|
@ -1410,6 +1432,12 @@ mptsas_firmware_event_work(struct work_struct *work)
|
|||
MPI_SAS_OP_CLEAR_NOT_PRESENT);
|
||||
mptsas_free_fw_event(ioc, fw_event);
|
||||
break;
|
||||
case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
|
||||
mptsas_send_expander_event(fw_event);
|
||||
break;
|
||||
case MPI_EVENT_SAS_PHY_LINK_STATUS:
|
||||
mptsas_send_link_status_event(fw_event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1909,7 +1937,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
|
|||
struct mptsas_portinfo *port_info;
|
||||
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
port_info = mptsas_get_hba_portinfo(ioc);
|
||||
port_info = ioc->hba_port_info;
|
||||
if (port_info && port_info->phy_info)
|
||||
sas_address =
|
||||
port_info->phy_info[0].phy->identify.sas_address;
|
||||
|
@ -2646,9 +2674,7 @@ static int mptsas_probe_one_phy(struct device *dev,
|
|||
struct mptsas_portinfo *port_info;
|
||||
int i;
|
||||
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
port_info = mptsas_get_hba_portinfo(ioc);
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
port_info = ioc->hba_port_info;
|
||||
|
||||
for (i = 0; i < port_info->num_phys; i++)
|
||||
if (port_info->phy_info[i].identify.sas_address ==
|
||||
|
@ -2707,7 +2733,7 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
|
|||
struct mptsas_portinfo *port_info, *hba;
|
||||
int error = -ENOMEM, i;
|
||||
|
||||
hba = kzalloc(sizeof(*port_info), GFP_KERNEL);
|
||||
hba = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
|
||||
if (! hba)
|
||||
goto out;
|
||||
|
||||
|
@ -2717,9 +2743,10 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
|
|||
|
||||
mptsas_sas_io_unit_pg1(ioc);
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
port_info = mptsas_get_hba_portinfo(ioc);
|
||||
port_info = ioc->hba_port_info;
|
||||
if (!port_info) {
|
||||
port_info = hba;
|
||||
ioc->hba_port_info = port_info = hba;
|
||||
ioc->hba_port_num_phy = port_info->num_phys;
|
||||
list_add_tail(&port_info->list, &ioc->sas_topology);
|
||||
} else {
|
||||
for (i = 0; i < hba->num_phys; i++) {
|
||||
|
@ -2735,15 +2762,22 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
|
|||
hba = NULL;
|
||||
}
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
#if defined(CPQ_CIM)
|
||||
ioc->num_ports = port_info->num_phys;
|
||||
#endif
|
||||
for (i = 0; i < port_info->num_phys; i++) {
|
||||
mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i],
|
||||
(MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER <<
|
||||
MPI_SAS_PHY_PGAD_FORM_SHIFT), i);
|
||||
|
||||
port_info->phy_info[i].identify.handle =
|
||||
port_info->phy_info[i].handle;
|
||||
mptsas_sas_device_pg0(ioc, &port_info->phy_info[i].identify,
|
||||
(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
|
||||
MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
|
||||
port_info->phy_info[i].handle);
|
||||
port_info->phy_info[i].identify.handle);
|
||||
if (!ioc->hba_port_sas_addr)
|
||||
ioc->hba_port_sas_addr =
|
||||
port_info->phy_info[i].identify.sas_address;
|
||||
port_info->phy_info[i].identify.phy_id =
|
||||
port_info->phy_info[i].phy_id = i;
|
||||
if (port_info->phy_info[i].attached.handle)
|
||||
|
@ -2768,180 +2802,462 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
|
|||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle)
|
||||
static void
|
||||
mptsas_expander_refresh(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
|
||||
{
|
||||
struct mptsas_portinfo *port_info, *p, *ex;
|
||||
struct device *parent;
|
||||
struct sas_rphy *rphy;
|
||||
int error = -ENOMEM, i, j;
|
||||
|
||||
ex = kzalloc(sizeof(*port_info), GFP_KERNEL);
|
||||
if (!ex)
|
||||
goto out;
|
||||
|
||||
error = mptsas_sas_expander_pg0(ioc, ex,
|
||||
(MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
|
||||
MPI_SAS_EXPAND_PGAD_FORM_SHIFT), *handle);
|
||||
if (error)
|
||||
goto out_free_port_info;
|
||||
|
||||
*handle = ex->phy_info[0].handle;
|
||||
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
port_info = mptsas_find_portinfo_by_handle(ioc, *handle);
|
||||
if (!port_info) {
|
||||
port_info = ex;
|
||||
list_add_tail(&port_info->list, &ioc->sas_topology);
|
||||
} else {
|
||||
for (i = 0; i < ex->num_phys; i++) {
|
||||
port_info->phy_info[i].handle =
|
||||
ex->phy_info[i].handle;
|
||||
port_info->phy_info[i].port_id =
|
||||
ex->phy_info[i].port_id;
|
||||
}
|
||||
kfree(ex->phy_info);
|
||||
kfree(ex);
|
||||
ex = NULL;
|
||||
}
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
struct mptsas_portinfo *parent;
|
||||
struct device *parent_dev;
|
||||
struct sas_rphy *rphy;
|
||||
int i;
|
||||
u64 sas_address; /* expander sas address */
|
||||
u32 handle;
|
||||
|
||||
handle = port_info->phy_info[0].handle;
|
||||
sas_address = port_info->phy_info[0].identify.sas_address;
|
||||
for (i = 0; i < port_info->num_phys; i++) {
|
||||
mptsas_sas_expander_pg1(ioc, &port_info->phy_info[i],
|
||||
(MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM <<
|
||||
MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + *handle);
|
||||
(MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM <<
|
||||
MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + handle);
|
||||
|
||||
if (port_info->phy_info[i].identify.handle) {
|
||||
mptsas_sas_device_pg0(ioc,
|
||||
&port_info->phy_info[i].identify,
|
||||
(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
|
||||
MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
|
||||
port_info->phy_info[i].identify.handle);
|
||||
port_info->phy_info[i].identify.phy_id =
|
||||
port_info->phy_info[i].phy_id;
|
||||
}
|
||||
mptsas_sas_device_pg0(ioc,
|
||||
&port_info->phy_info[i].identify,
|
||||
(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
|
||||
MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
|
||||
port_info->phy_info[i].identify.handle);
|
||||
port_info->phy_info[i].identify.phy_id =
|
||||
port_info->phy_info[i].phy_id;
|
||||
|
||||
if (port_info->phy_info[i].attached.handle) {
|
||||
mptsas_sas_device_pg0(ioc,
|
||||
&port_info->phy_info[i].attached,
|
||||
(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
|
||||
MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
|
||||
port_info->phy_info[i].attached.handle);
|
||||
&port_info->phy_info[i].attached,
|
||||
(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
|
||||
MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
|
||||
port_info->phy_info[i].attached.handle);
|
||||
port_info->phy_info[i].attached.phy_id =
|
||||
port_info->phy_info[i].phy_id;
|
||||
}
|
||||
}
|
||||
|
||||
parent = &ioc->sh->shost_gendev;
|
||||
for (i = 0; i < port_info->num_phys; i++) {
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
list_for_each_entry(p, &ioc->sas_topology, list) {
|
||||
for (j = 0; j < p->num_phys; j++) {
|
||||
if (port_info->phy_info[i].identify.handle !=
|
||||
p->phy_info[j].attached.handle)
|
||||
continue;
|
||||
rphy = mptsas_get_rphy(&p->phy_info[j]);
|
||||
parent = &rphy->dev;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
}
|
||||
|
||||
mptsas_setup_wide_ports(ioc, port_info);
|
||||
|
||||
for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++)
|
||||
mptsas_probe_one_phy(parent, &port_info->phy_info[i],
|
||||
ioc->sas_index, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_port_info:
|
||||
if (ex) {
|
||||
kfree(ex->phy_info);
|
||||
kfree(ex);
|
||||
}
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* mptsas_delete_expander_phys
|
||||
*
|
||||
*
|
||||
* This will traverse topology, and remove expanders
|
||||
* that are no longer present
|
||||
*/
|
||||
static void
|
||||
mptsas_delete_expander_phys(MPT_ADAPTER *ioc)
|
||||
{
|
||||
struct mptsas_portinfo buffer;
|
||||
struct mptsas_portinfo *port_info, *n, *parent;
|
||||
struct mptsas_phyinfo *phy_info;
|
||||
struct sas_port * port;
|
||||
int i;
|
||||
u64 expander_sas_address;
|
||||
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
list_for_each_entry_safe(port_info, n, &ioc->sas_topology, list) {
|
||||
|
||||
if (!(port_info->phy_info[0].identify.device_info &
|
||||
MPI_SAS_DEVICE_INFO_SMP_TARGET))
|
||||
continue;
|
||||
|
||||
if (mptsas_sas_expander_pg0(ioc, &buffer,
|
||||
(MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
|
||||
MPI_SAS_EXPAND_PGAD_FORM_SHIFT),
|
||||
port_info->phy_info[0].handle)) {
|
||||
|
||||
/*
|
||||
* Obtain the port_info instance to the parent port
|
||||
*/
|
||||
parent = mptsas_find_portinfo_by_handle(ioc,
|
||||
port_info->phy_info[0].identify.handle_parent);
|
||||
|
||||
if (!parent)
|
||||
goto next_port;
|
||||
|
||||
expander_sas_address =
|
||||
port_info->phy_info[0].identify.sas_address;
|
||||
|
||||
/*
|
||||
* Delete rphys in the parent that point
|
||||
* to this expander. The transport layer will
|
||||
* cleanup all the children.
|
||||
*/
|
||||
phy_info = parent->phy_info;
|
||||
for (i = 0; i < parent->num_phys; i++, phy_info++) {
|
||||
port = mptsas_get_port(phy_info);
|
||||
if (!port)
|
||||
continue;
|
||||
if (phy_info->attached.sas_address !=
|
||||
expander_sas_address)
|
||||
continue;
|
||||
dsaswideprintk(ioc,
|
||||
dev_printk(KERN_DEBUG, &port->dev,
|
||||
MYIOC_s_FMT "delete port (%d)\n", ioc->name,
|
||||
port->port_identifier));
|
||||
sas_port_delete(port);
|
||||
mptsas_port_delete(ioc, phy_info->port_details);
|
||||
}
|
||||
next_port:
|
||||
|
||||
phy_info = port_info->phy_info;
|
||||
for (i = 0; i < port_info->num_phys; i++, phy_info++)
|
||||
mptsas_port_delete(ioc, phy_info->port_details);
|
||||
|
||||
list_del(&port_info->list);
|
||||
kfree(port_info->phy_info);
|
||||
kfree(port_info);
|
||||
parent = mptsas_find_portinfo_by_handle(ioc,
|
||||
port_info->phy_info[0].identify.handle_parent);
|
||||
if (!parent) {
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
return;
|
||||
}
|
||||
for (i = 0, parent_dev = NULL; i < parent->num_phys && !parent_dev;
|
||||
i++) {
|
||||
if (parent->phy_info[i].attached.sas_address == sas_address) {
|
||||
rphy = mptsas_get_rphy(&parent->phy_info[i]);
|
||||
parent_dev = &rphy->dev;
|
||||
}
|
||||
/*
|
||||
* Free this memory allocated from inside
|
||||
* mptsas_sas_expander_pg0
|
||||
*/
|
||||
kfree(buffer.phy_info);
|
||||
}
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
|
||||
mptsas_setup_wide_ports(ioc, port_info);
|
||||
for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++)
|
||||
mptsas_probe_one_phy(parent_dev, &port_info->phy_info[i],
|
||||
ioc->sas_index, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
mptsas_expander_event_add(MPT_ADAPTER *ioc,
|
||||
MpiEventDataSasExpanderStatusChange_t *expander_data)
|
||||
{
|
||||
struct mptsas_portinfo *port_info;
|
||||
int i;
|
||||
__le64 sas_address;
|
||||
|
||||
port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
|
||||
if (!port_info)
|
||||
BUG();
|
||||
port_info->num_phys = (expander_data->NumPhys) ?
|
||||
expander_data->NumPhys : 1;
|
||||
port_info->phy_info = kcalloc(port_info->num_phys,
|
||||
sizeof(struct mptsas_phyinfo), GFP_KERNEL);
|
||||
if (!port_info->phy_info)
|
||||
BUG();
|
||||
memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
|
||||
for (i = 0; i < port_info->num_phys; i++) {
|
||||
port_info->phy_info[i].portinfo = port_info;
|
||||
port_info->phy_info[i].handle =
|
||||
le16_to_cpu(expander_data->DevHandle);
|
||||
port_info->phy_info[i].identify.sas_address =
|
||||
le64_to_cpu(sas_address);
|
||||
port_info->phy_info[i].identify.handle_parent =
|
||||
le16_to_cpu(expander_data->ParentDevHandle);
|
||||
}
|
||||
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
list_add_tail(&port_info->list, &ioc->sas_topology);
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
|
||||
printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
|
||||
"sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
|
||||
(unsigned long long)sas_address);
|
||||
|
||||
mptsas_expander_refresh(ioc, port_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* mptsas_delete_expander_siblings - remove siblings attached to expander
|
||||
* @ioc: Pointer to MPT_ADAPTER structure
|
||||
* @parent: the parent port_info object
|
||||
* @expander: the expander port_info object
|
||||
**/
|
||||
static void
|
||||
mptsas_delete_expander_siblings(MPT_ADAPTER *ioc, struct mptsas_portinfo
|
||||
*parent, struct mptsas_portinfo *expander)
|
||||
{
|
||||
struct mptsas_phyinfo *phy_info;
|
||||
struct mptsas_portinfo *port_info;
|
||||
struct sas_rphy *rphy;
|
||||
int i;
|
||||
|
||||
phy_info = expander->phy_info;
|
||||
for (i = 0; i < expander->num_phys; i++, phy_info++) {
|
||||
rphy = mptsas_get_rphy(phy_info);
|
||||
if (!rphy)
|
||||
continue;
|
||||
if (rphy->identify.device_type == SAS_END_DEVICE)
|
||||
mptsas_del_end_device(ioc, phy_info);
|
||||
}
|
||||
|
||||
phy_info = expander->phy_info;
|
||||
for (i = 0; i < expander->num_phys; i++, phy_info++) {
|
||||
rphy = mptsas_get_rphy(phy_info);
|
||||
if (!rphy)
|
||||
continue;
|
||||
if (rphy->identify.device_type ==
|
||||
MPI_SAS_DEVICE_INFO_EDGE_EXPANDER ||
|
||||
rphy->identify.device_type ==
|
||||
MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER) {
|
||||
port_info = mptsas_find_portinfo_by_sas_address(ioc,
|
||||
rphy->identify.sas_address);
|
||||
if (!port_info)
|
||||
continue;
|
||||
if (port_info == parent) /* backlink rphy */
|
||||
continue;
|
||||
/*
|
||||
Delete this expander even if the expdevpage is exists
|
||||
because the parent expander is already deleted
|
||||
*/
|
||||
mptsas_expander_delete(ioc, port_info, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mptsas_expander_delete - remove this expander
|
||||
* @ioc: Pointer to MPT_ADAPTER structure
|
||||
* @port_info: expander port_info struct
|
||||
* @force: Flag to forcefully delete the expander
|
||||
*
|
||||
**/
|
||||
|
||||
static void mptsas_expander_delete(MPT_ADAPTER *ioc,
|
||||
struct mptsas_portinfo *port_info, u8 force)
|
||||
{
|
||||
|
||||
struct mptsas_portinfo *parent;
|
||||
int i;
|
||||
u64 expander_sas_address;
|
||||
struct mptsas_phyinfo *phy_info;
|
||||
struct mptsas_portinfo buffer;
|
||||
struct mptsas_portinfo_details *port_details;
|
||||
struct sas_port *port;
|
||||
|
||||
if (!port_info)
|
||||
return;
|
||||
|
||||
/* see if expander is still there before deleting */
|
||||
mptsas_sas_expander_pg0(ioc, &buffer,
|
||||
(MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
|
||||
MPI_SAS_EXPAND_PGAD_FORM_SHIFT),
|
||||
port_info->phy_info[0].identify.handle);
|
||||
|
||||
if (buffer.num_phys) {
|
||||
kfree(buffer.phy_info);
|
||||
if (!force)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Obtain the port_info instance to the parent port
|
||||
*/
|
||||
port_details = NULL;
|
||||
expander_sas_address =
|
||||
port_info->phy_info[0].identify.sas_address;
|
||||
parent = mptsas_find_portinfo_by_handle(ioc,
|
||||
port_info->phy_info[0].identify.handle_parent);
|
||||
mptsas_delete_expander_siblings(ioc, parent, port_info);
|
||||
if (!parent)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Delete rphys in the parent that point
|
||||
* to this expander.
|
||||
*/
|
||||
phy_info = parent->phy_info;
|
||||
port = NULL;
|
||||
for (i = 0; i < parent->num_phys; i++, phy_info++) {
|
||||
if (!phy_info->phy)
|
||||
continue;
|
||||
if (phy_info->attached.sas_address !=
|
||||
expander_sas_address)
|
||||
continue;
|
||||
if (!port) {
|
||||
port = mptsas_get_port(phy_info);
|
||||
port_details = phy_info->port_details;
|
||||
}
|
||||
dev_printk(KERN_DEBUG, &phy_info->phy->dev,
|
||||
MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n", ioc->name,
|
||||
phy_info->phy_id, phy_info->phy);
|
||||
sas_port_delete_phy(port, phy_info->phy);
|
||||
}
|
||||
if (port) {
|
||||
dev_printk(KERN_DEBUG, &port->dev,
|
||||
MYIOC_s_FMT "delete port %d, sas_addr (0x%llx)\n",
|
||||
ioc->name, port->port_identifier,
|
||||
(unsigned long long)expander_sas_address);
|
||||
sas_port_delete(port);
|
||||
mptsas_port_delete(ioc, port_details);
|
||||
}
|
||||
out:
|
||||
|
||||
printk(MYIOC_s_INFO_FMT "delete expander: num_phys %d, "
|
||||
"sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
|
||||
(unsigned long long)expander_sas_address);
|
||||
|
||||
/*
|
||||
* free link
|
||||
*/
|
||||
list_del(&port_info->list);
|
||||
kfree(port_info->phy_info);
|
||||
kfree(port_info);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mptsas_send_expander_event - expanders events
|
||||
* @ioc: Pointer to MPT_ADAPTER structure
|
||||
* @expander_data: event data
|
||||
*
|
||||
*
|
||||
* This function handles adding, removing, and refreshing
|
||||
* device handles within the expander objects.
|
||||
*/
|
||||
static void
|
||||
mptsas_send_expander_event(struct fw_event_work *fw_event)
|
||||
{
|
||||
MPT_ADAPTER *ioc;
|
||||
MpiEventDataSasExpanderStatusChange_t *expander_data;
|
||||
struct mptsas_portinfo *port_info;
|
||||
__le64 sas_address;
|
||||
int i;
|
||||
|
||||
ioc = fw_event->ioc;
|
||||
expander_data = (MpiEventDataSasExpanderStatusChange_t *)
|
||||
fw_event->event_data;
|
||||
memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
|
||||
port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
|
||||
|
||||
if (expander_data->ReasonCode == MPI_EVENT_SAS_EXP_RC_ADDED) {
|
||||
if (port_info) {
|
||||
for (i = 0; i < port_info->num_phys; i++) {
|
||||
port_info->phy_info[i].portinfo = port_info;
|
||||
port_info->phy_info[i].handle =
|
||||
le16_to_cpu(expander_data->DevHandle);
|
||||
port_info->phy_info[i].identify.sas_address =
|
||||
le64_to_cpu(sas_address);
|
||||
port_info->phy_info[i].identify.handle_parent =
|
||||
le16_to_cpu(expander_data->ParentDevHandle);
|
||||
}
|
||||
mptsas_expander_refresh(ioc, port_info);
|
||||
} else if (!port_info && expander_data->NumPhys)
|
||||
mptsas_expander_event_add(ioc, expander_data);
|
||||
} else if (expander_data->ReasonCode ==
|
||||
MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING)
|
||||
mptsas_expander_delete(ioc, port_info, 0);
|
||||
|
||||
mptsas_free_fw_event(ioc, fw_event);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mptsas_expander_add -
|
||||
* @ioc: Pointer to MPT_ADAPTER structure
|
||||
* @handle:
|
||||
*
|
||||
*/
|
||||
struct mptsas_portinfo *
|
||||
mptsas_expander_add(MPT_ADAPTER *ioc, u16 handle)
|
||||
{
|
||||
struct mptsas_portinfo buffer, *port_info;
|
||||
int i;
|
||||
|
||||
if ((mptsas_sas_expander_pg0(ioc, &buffer,
|
||||
(MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
|
||||
MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)))
|
||||
return NULL;
|
||||
|
||||
port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_ATOMIC);
|
||||
if (!port_info) {
|
||||
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
|
||||
"%s: exit at line=%d\n", ioc->name,
|
||||
__func__, __LINE__));
|
||||
return NULL;
|
||||
}
|
||||
port_info->num_phys = buffer.num_phys;
|
||||
port_info->phy_info = buffer.phy_info;
|
||||
for (i = 0; i < port_info->num_phys; i++)
|
||||
port_info->phy_info[i].portinfo = port_info;
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
list_add_tail(&port_info->list, &ioc->sas_topology);
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
|
||||
"sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
|
||||
(unsigned long long)buffer.phy_info[0].identify.sas_address);
|
||||
mptsas_expander_refresh(ioc, port_info);
|
||||
return port_info;
|
||||
}
|
||||
|
||||
static void
|
||||
mptsas_send_link_status_event(struct fw_event_work *fw_event)
|
||||
{
|
||||
MPT_ADAPTER *ioc;
|
||||
MpiEventDataSasPhyLinkStatus_t *link_data;
|
||||
struct mptsas_portinfo *port_info;
|
||||
struct mptsas_phyinfo *phy_info = NULL;
|
||||
__le64 sas_address;
|
||||
u8 phy_num;
|
||||
u8 link_rate;
|
||||
|
||||
ioc = fw_event->ioc;
|
||||
link_data = (MpiEventDataSasPhyLinkStatus_t *)fw_event->event_data;
|
||||
|
||||
memcpy(&sas_address, &link_data->SASAddress, sizeof(__le64));
|
||||
sas_address = le64_to_cpu(sas_address);
|
||||
link_rate = link_data->LinkRates >> 4;
|
||||
phy_num = link_data->PhyNum;
|
||||
|
||||
port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
|
||||
if (port_info) {
|
||||
phy_info = &port_info->phy_info[phy_num];
|
||||
if (phy_info)
|
||||
phy_info->negotiated_link_rate = link_rate;
|
||||
}
|
||||
|
||||
if (link_rate == MPI_SAS_IOUNIT0_RATE_1_5 ||
|
||||
link_rate == MPI_SAS_IOUNIT0_RATE_3_0) {
|
||||
|
||||
if (!port_info)
|
||||
goto out;
|
||||
|
||||
if (port_info == ioc->hba_port_info)
|
||||
mptsas_probe_hba_phys(ioc);
|
||||
else
|
||||
mptsas_expander_refresh(ioc, port_info);
|
||||
} else if (phy_info && phy_info->phy) {
|
||||
if (link_rate == MPI_SAS_IOUNIT0_RATE_PHY_DISABLED)
|
||||
phy_info->phy->negotiated_linkrate =
|
||||
SAS_PHY_DISABLED;
|
||||
else if (link_rate ==
|
||||
MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION)
|
||||
phy_info->phy->negotiated_linkrate =
|
||||
SAS_LINK_RATE_FAILED;
|
||||
else
|
||||
phy_info->phy->negotiated_linkrate =
|
||||
SAS_LINK_RATE_UNKNOWN;
|
||||
}
|
||||
out:
|
||||
mptsas_free_fw_event(ioc, fw_event);
|
||||
}
|
||||
|
||||
/**
|
||||
* mptsas_probe_expanders - adding expanders
|
||||
* @ioc: Pointer to MPT_ADAPTER structure
|
||||
*
|
||||
**/
|
||||
static void
|
||||
mptsas_probe_expanders(MPT_ADAPTER *ioc)
|
||||
{
|
||||
struct mptsas_portinfo buffer, *port_info;
|
||||
u32 handle;
|
||||
int i;
|
||||
|
||||
handle = 0xFFFF;
|
||||
while (!mptsas_sas_expander_pg0(ioc, &buffer,
|
||||
(MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
|
||||
MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)) {
|
||||
|
||||
handle = buffer.phy_info[0].handle;
|
||||
port_info = mptsas_find_portinfo_by_sas_address(ioc,
|
||||
buffer.phy_info[0].identify.sas_address);
|
||||
|
||||
if (port_info) {
|
||||
/* refreshing handles */
|
||||
for (i = 0; i < buffer.num_phys; i++) {
|
||||
port_info->phy_info[i].handle = handle;
|
||||
port_info->phy_info[i].identify.handle_parent =
|
||||
buffer.phy_info[0].identify.handle_parent;
|
||||
}
|
||||
mptsas_expander_refresh(ioc, port_info);
|
||||
kfree(buffer.phy_info);
|
||||
continue;
|
||||
}
|
||||
|
||||
port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
|
||||
if (!port_info) {
|
||||
dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
|
||||
"%s: exit at line=%d\n", ioc->name,
|
||||
__func__, __LINE__));
|
||||
return;
|
||||
}
|
||||
port_info->num_phys = buffer.num_phys;
|
||||
port_info->phy_info = buffer.phy_info;
|
||||
for (i = 0; i < port_info->num_phys; i++)
|
||||
port_info->phy_info[i].portinfo = port_info;
|
||||
mutex_lock(&ioc->sas_topology_mutex);
|
||||
list_add_tail(&port_info->list, &ioc->sas_topology);
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
|
||||
"sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
|
||||
(unsigned long long)buffer.phy_info[0].identify.sas_address);
|
||||
mptsas_expander_refresh(ioc, port_info);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mptsas_probe_devices(MPT_ADAPTER *ioc)
|
||||
{
|
||||
u16 handle;
|
||||
struct mptsas_devinfo sas_device;
|
||||
struct mptsas_phyinfo *phy_info;
|
||||
|
||||
handle = 0xFFFF;
|
||||
while (!(mptsas_sas_device_pg0(ioc, &sas_device,
|
||||
MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
|
||||
|
||||
handle = sas_device.handle;
|
||||
|
||||
if ((sas_device.device_info &
|
||||
(MPI_SAS_DEVICE_INFO_SSP_TARGET |
|
||||
MPI_SAS_DEVICE_INFO_STP_TARGET |
|
||||
MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0)
|
||||
continue;
|
||||
|
||||
phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
|
||||
if (!phy_info)
|
||||
continue;
|
||||
|
||||
if (mptsas_get_rphy(phy_info))
|
||||
continue;
|
||||
|
||||
mptsas_add_end_device(ioc, phy_info);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2950,67 +3266,34 @@ mptsas_delete_expander_phys(MPT_ADAPTER *ioc)
|
|||
static void
|
||||
mptsas_scan_sas_topology(MPT_ADAPTER *ioc)
|
||||
{
|
||||
u32 handle = 0xFFFF;
|
||||
struct scsi_device *sdev;
|
||||
int i;
|
||||
|
||||
mutex_lock(&ioc->sas_discovery_mutex);
|
||||
mptsas_probe_hba_phys(ioc);
|
||||
while (!mptsas_probe_expander_phys(ioc, &handle))
|
||||
;
|
||||
mptsas_probe_expanders(ioc);
|
||||
mptsas_probe_devices(ioc);
|
||||
|
||||
/*
|
||||
Reporting RAID volumes.
|
||||
*/
|
||||
if (!ioc->ir_firmware)
|
||||
goto out;
|
||||
if (!ioc->raid_data.pIocPg2)
|
||||
goto out;
|
||||
if (!ioc->raid_data.pIocPg2->NumActiveVolumes)
|
||||
goto out;
|
||||
if (!ioc->ir_firmware || !ioc->raid_data.pIocPg2 ||
|
||||
!ioc->raid_data.pIocPg2->NumActiveVolumes)
|
||||
return;
|
||||
for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
|
||||
sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
|
||||
ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
|
||||
if (sdev) {
|
||||
scsi_device_put(sdev);
|
||||
continue;
|
||||
}
|
||||
printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
|
||||
"id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
|
||||
ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID);
|
||||
scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
|
||||
ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ioc->sas_discovery_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Work queue thread to handle Runtime discovery
|
||||
* Mere purpose is the hot add/delete of expanders
|
||||
*(Mutex UNLOCKED)
|
||||
*/
|
||||
static void
|
||||
__mptsas_discovery_work(MPT_ADAPTER *ioc)
|
||||
{
|
||||
u32 handle = 0xFFFF;
|
||||
|
||||
ioc->sas_discovery_runtime=1;
|
||||
mptsas_delete_expander_phys(ioc);
|
||||
mptsas_probe_hba_phys(ioc);
|
||||
while (!mptsas_probe_expander_phys(ioc, &handle))
|
||||
;
|
||||
ioc->sas_discovery_runtime=0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Work queue thread to handle Runtime discovery
|
||||
* Mere purpose is the hot add/delete of expanders
|
||||
*(Mutex LOCKED)
|
||||
*/
|
||||
static void
|
||||
mptsas_discovery_work(struct work_struct *work)
|
||||
{
|
||||
struct mptsas_discovery_event *ev =
|
||||
container_of(work, struct mptsas_discovery_event, work);
|
||||
MPT_ADAPTER *ioc = ev->ioc;
|
||||
|
||||
mutex_lock(&ioc->sas_discovery_mutex);
|
||||
__mptsas_discovery_work(ioc);
|
||||
mutex_unlock(&ioc->sas_discovery_mutex);
|
||||
kfree(ev);
|
||||
}
|
||||
|
||||
|
||||
static struct mptsas_phyinfo *
|
||||
mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
|
||||
{
|
||||
|
@ -3544,33 +3827,6 @@ mptsas_send_raid_event(struct fw_event_work *fw_event)
|
|||
mptsas_free_fw_event(ioc, fw_event);
|
||||
}
|
||||
|
||||
static void
|
||||
mptsas_send_discovery_event(MPT_ADAPTER *ioc,
|
||||
EVENT_DATA_SAS_DISCOVERY *discovery_data)
|
||||
{
|
||||
struct mptsas_discovery_event *ev;
|
||||
u32 discovery_status;
|
||||
|
||||
/*
|
||||
* DiscoveryStatus
|
||||
*
|
||||
* This flag will be non-zero when firmware
|
||||
* kicks off discovery, and return to zero
|
||||
* once its completed.
|
||||
*/
|
||||
discovery_status = le32_to_cpu(discovery_data->DiscoveryStatus);
|
||||
ioc->sas_discovery_quiesce_io = discovery_status ? 1 : 0;
|
||||
if (discovery_status)
|
||||
return;
|
||||
|
||||
ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
|
||||
if (!ev)
|
||||
return;
|
||||
INIT_WORK(&ev->work, mptsas_discovery_work);
|
||||
ev->ioc = ioc;
|
||||
schedule_work(&ev->work);
|
||||
};
|
||||
|
||||
/*
|
||||
* mptsas_send_ir2_event - handle exposing hidden disk when
|
||||
* an inactive raid volume is added
|
||||
|
@ -3634,10 +3890,28 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case MPI_EVENT_SAS_DISCOVERY:
|
||||
mptsas_send_discovery_event(ioc,
|
||||
(EVENT_DATA_SAS_DISCOVERY *)reply->Data);
|
||||
case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
|
||||
{
|
||||
MpiEventDataSasExpanderStatusChange_t *expander_data =
|
||||
(MpiEventDataSasExpanderStatusChange_t *)reply->Data;
|
||||
|
||||
|
||||
if (expander_data->ReasonCode ==
|
||||
MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING &&
|
||||
ioc->device_missing_delay)
|
||||
delay = HZ * ioc->device_missing_delay;
|
||||
break;
|
||||
}
|
||||
case MPI_EVENT_SAS_DISCOVERY:
|
||||
{
|
||||
u32 discovery_status;
|
||||
EventDataSasDiscovery_t *discovery_data =
|
||||
(EventDataSasDiscovery_t *)reply->Data;
|
||||
|
||||
discovery_status = le32_to_cpu(discovery_data->DiscoveryStatus);
|
||||
ioc->sas_discovery_quiesce_io = discovery_status ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
case MPI_EVENT_INTEGRATED_RAID:
|
||||
case MPI_EVENT_PERSISTENT_TABLE_FULL:
|
||||
case MPI_EVENT_IR2:
|
||||
|
@ -3885,7 +4159,7 @@ static void __devexit mptsas_remove(struct pci_dev *pdev)
|
|||
kfree(p);
|
||||
}
|
||||
mutex_unlock(&ioc->sas_topology_mutex);
|
||||
|
||||
ioc->hba_port_info = NULL;
|
||||
mptscsih_remove(pdev);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue