> On Aug 4, 2022, at 6:12 AM, Sreekanth Reddy <sreekanth.reddy@xxxxxxxxxxxx> wrote: > > Register/unregister the expander devices to > SCSI Transport Layer(STL) whenever the corresponding expander > is added/removed from topology. > > Signed-off-by: Sreekanth Reddy <sreekanth.reddy@xxxxxxxxxxxx> > --- > drivers/scsi/mpi3mr/mpi3mr.h | 7 + > drivers/scsi/mpi3mr/mpi3mr_os.c | 36 ++- > drivers/scsi/mpi3mr/mpi3mr_transport.c | 398 ++++++++++++++++++++++++- > 3 files changed, 432 insertions(+), 9 deletions(-) > > diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h > index 8c8703e..9cd5f88 100644 > --- a/drivers/scsi/mpi3mr/mpi3mr.h > +++ b/drivers/scsi/mpi3mr/mpi3mr.h > @@ -1341,6 +1341,11 @@ int mpi3mr_cfg_get_driver_pg1(struct mpi3mr_ioc *mrioc, > struct mpi3_driver_page1 *driver_pg1, u16 pg_sz); > > u8 mpi3mr_is_expander_device(u16 device_info); > +int mpi3mr_expander_add(struct mpi3mr_ioc *mrioc, u16 handle); > +void mpi3mr_expander_remove(struct mpi3mr_ioc *mrioc, u64 sas_address, > + struct mpi3mr_hba_port *hba_port); > +struct mpi3mr_sas_node *__mpi3mr_expander_find_by_handle(struct mpi3mr_ioc > + *mrioc, u16 handle); > struct mpi3mr_hba_port *mpi3mr_get_hba_port_by_id(struct mpi3mr_ioc *mrioc, > u8 port_id); > void mpi3mr_sas_host_refresh(struct mpi3mr_ioc *mrioc); > @@ -1348,6 +1353,8 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc); > void mpi3mr_update_links(struct mpi3mr_ioc *mrioc, > u64 sas_address_parent, u16 handle, u8 phy_number, u8 link_rate, > struct mpi3mr_hba_port *hba_port); > +void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc, > + struct mpi3mr_tgt_dev *tgtdev); > void mpi3mr_print_device_event_notice(struct mpi3mr_ioc *mrioc, > bool device_add); > #endif /*MPI3MR_H_INCLUDED*/ > diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c > index ae77422..9a32dc6 100644 > --- a/drivers/scsi/mpi3mr/mpi3mr_os.c > +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c > @@ -824,7 +824,7 @@ void mpi3mr_print_device_event_notice(struct mpi3mr_ioc *mrioc, > * > * Return: 0 on success, non zero on failure. > */ > -static void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc, > +void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc, > struct mpi3mr_tgt_dev *tgtdev) > { > struct mpi3mr_stgt_priv_data *tgt_priv; > @@ -1494,7 +1494,10 @@ static void mpi3mr_sastopochg_evt_bh(struct mpi3mr_ioc *mrioc, > int i; > u16 handle; > u8 reason_code; > + u64 exp_sas_address = 0; > + struct mpi3mr_hba_port *hba_port = NULL; > struct mpi3mr_tgt_dev *tgtdev = NULL; > + struct mpi3mr_sas_node *sas_expander = NULL; > > mpi3mr_sastopochg_evt_debug(mrioc, event_data); > > @@ -1524,6 +1527,13 @@ static void mpi3mr_sastopochg_evt_bh(struct mpi3mr_ioc *mrioc, > if (tgtdev) > mpi3mr_tgtdev_put(tgtdev); > } > + > + if (mrioc->sas_transport_enabled && (event_data->exp_status == > + MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING)) { > + if (sas_expander) > + mpi3mr_expander_remove(mrioc, exp_sas_address, > + hba_port); > + } > } > > /** > @@ -1744,7 +1754,8 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc, > struct mpi3mr_fwevt *fwevt) > { > struct mpi3_device_page0 *dev_pg0 = NULL; > - u16 perst_id; > + u16 perst_id, handle, dev_info; > + struct mpi3_device0_sas_sata_format *sasinf = NULL; > > mpi3mr_fwevt_del_from_list(mrioc, fwevt); > mrioc->current_event = fwevt; > @@ -1758,10 +1769,23 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc, > switch (fwevt->event_id) { > case MPI3_EVENT_DEVICE_ADDED: > { > - struct mpi3_device_page0 *dev_pg0 = > - (struct mpi3_device_page0 *)fwevt->event_data; > - mpi3mr_report_tgtdev_to_host(mrioc, > - le16_to_cpu(dev_pg0->persistent_id)); > + dev_pg0 = (struct mpi3_device_page0 *)fwevt->event_data; > + perst_id = le16_to_cpu(dev_pg0->persistent_id); > + handle = le16_to_cpu(dev_pg0->dev_handle); > + if (perst_id != MPI3_DEVICE0_PERSISTENTID_INVALID) > + mpi3mr_report_tgtdev_to_host(mrioc, perst_id); > + else if (mrioc->sas_transport_enabled && > + (dev_pg0->device_form == MPI3_DEVICE_DEVFORM_SAS_SATA)) { > + sasinf = &dev_pg0->device_specific.sas_sata_format; > + dev_info = le16_to_cpu(sasinf->device_info); > + if (!mrioc->sas_hba.num_phys) > + mpi3mr_sas_host_add(mrioc); > + else > + mpi3mr_sas_host_refresh(mrioc); > + > + if (mpi3mr_is_expander_device(dev_info)) > + mpi3mr_expander_add(mrioc, handle); > + } > break; > } > case MPI3_EVENT_DEVICE_INFO_CHANGED: > diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c > index 3de9fa0..ff85cf3 100644 > --- a/drivers/scsi/mpi3mr/mpi3mr_transport.c > +++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c > @@ -21,7 +21,7 @@ > * > * Return: Expander sas_node object reference or NULL > */ > -static struct mpi3mr_sas_node *__mpi3mr_expander_find_by_handle(struct mpi3mr_ioc > +struct mpi3mr_sas_node *__mpi3mr_expander_find_by_handle(struct mpi3mr_ioc > *mrioc, u16 handle) > { > struct mpi3mr_sas_node *sas_expander, *r; > @@ -159,6 +159,45 @@ out: > return tgtdev; > } > > +/** > + * mpi3mr_remove_device_by_sas_address - remove the device > + * @mrioc: Adapter instance reference > + * @sas_address: SAS address of the device > + * @hba_port: HBA port entry > + * > + * This searches for target device using sas address and hba > + * port pointer then removes it from the OS. > + * > + * Return: None > + */ > +static void mpi3mr_remove_device_by_sas_address(struct mpi3mr_ioc *mrioc, > + u64 sas_address, struct mpi3mr_hba_port *hba_port) > +{ > + struct mpi3mr_tgt_dev *tgtdev = NULL; > + unsigned long flags; > + u8 was_on_tgtdev_list = 0; > + > + if (!hba_port) > + return; > + > + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); > + tgtdev = __mpi3mr_get_tgtdev_by_addr(mrioc, > + sas_address, hba_port); > + if (tgtdev) { > + if (!list_empty(&tgtdev->list)) { > + list_del_init(&tgtdev->list); > + was_on_tgtdev_list = 1; > + mpi3mr_tgtdev_put(tgtdev); > + } > + } > + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); > + if (was_on_tgtdev_list) { > + if (tgtdev->host_exposed) > + mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev); > + mpi3mr_tgtdev_put(tgtdev); > + } > +} > + > /** > * mpi3mr_expander_find_by_sas_address - sas expander search > * @mrioc: Adapter instance reference > @@ -378,6 +417,34 @@ static void mpi3mr_add_phy_to_an_existing_port(struct mpi3mr_ioc *mrioc, > } > } > > +/** > + * mpi3mr_delete_sas_port - helper function to removing a port > + * @mrioc: Adapter instance reference > + * @mr_sas_port: Internal Port object > + * > + * Return: None. > + */ > +static void mpi3mr_delete_sas_port(struct mpi3mr_ioc *mrioc, > + struct mpi3mr_sas_port *mr_sas_port) > +{ > + u64 sas_address = mr_sas_port->remote_identify.sas_address; > + struct mpi3mr_hba_port *hba_port = mr_sas_port->hba_port; > + enum sas_device_type device_type = > + mr_sas_port->remote_identify.device_type; > + > + dev_info(&mr_sas_port->port->dev, > + "remove: sas_address(0x%016llx)\n", > + (unsigned long long) sas_address); > + > + if (device_type == SAS_END_DEVICE) > + mpi3mr_remove_device_by_sas_address(mrioc, sas_address, > + hba_port); > + > + else if (device_type == SAS_EDGE_EXPANDER_DEVICE || > + device_type == SAS_FANOUT_EXPANDER_DEVICE) > + mpi3mr_expander_remove(mrioc, sas_address, hba_port); > +} > + > /** > * mpi3mr_del_phy_from_an_existing_port - del phy from a port > * @mrioc: Adapter instance reference > @@ -401,8 +468,12 @@ static void mpi3mr_del_phy_from_an_existing_port(struct mpi3mr_ioc *mrioc, > port_siblings) { > if (srch_phy != mr_sas_phy) > continue; > - mpi3mr_delete_sas_phy(mrioc, mr_sas_port, > - mr_sas_phy); > + if ((mr_sas_port->num_phys == 1) && > + !mrioc->reset_in_progress) > + mpi3mr_delete_sas_port(mrioc, mr_sas_port); > + else > + mpi3mr_delete_sas_phy(mrioc, mr_sas_port, > + mr_sas_phy); > return; > } > } > @@ -1231,3 +1302,324 @@ static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address, > > kfree(mr_sas_port); > } > + > +/** > + * mpi3mr_expander_node_add - insert an expander to the list. > + * @mrioc: Adapter instance reference > + * @sas_expander: Expander sas node > + * Context: This function will acquire sas_node_lock. > + * > + * Adding new object to the ioc->sas_expander_list. > + * > + * Return: None. > + */ > +static void mpi3mr_expander_node_add(struct mpi3mr_ioc *mrioc, > + struct mpi3mr_sas_node *sas_expander) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&mrioc->sas_node_lock, flags); > + list_add_tail(&sas_expander->list, &mrioc->sas_expander_list); > + spin_unlock_irqrestore(&mrioc->sas_node_lock, flags); > +} > + > +/** > + * mpi3mr_expander_add - Create expander object > + * @mrioc: Adapter instance reference > + * @handle: Expander firmware device handle > + * > + * This function creating expander object, stored in > + * sas_expander_list and expose it to the SAS transport > + * layer. > + * > + * Return: 0 for success, non-zero for failure. > + */ > +int mpi3mr_expander_add(struct mpi3mr_ioc *mrioc, u16 handle) > +{ > + struct mpi3mr_sas_node *sas_expander; > + struct mpi3mr_enclosure_node *enclosure_dev; > + struct mpi3_sas_expander_page0 expander_pg0; > + struct mpi3_sas_expander_page1 expander_pg1; > + u16 ioc_status, parent_handle, temp_handle; > + u64 sas_address, sas_address_parent = 0; > + int i; > + unsigned long flags; > + u8 port_id, link_rate; > + struct mpi3mr_sas_port *mr_sas_port = NULL; > + struct mpi3mr_hba_port *hba_port; > + u32 phynum_handle; > + int rc = 0; > + > + if (!handle) > + return -1; > + > + if (mrioc->reset_in_progress) > + return -1; > + > + if ((mpi3mr_cfg_get_sas_exp_pg0(mrioc, &ioc_status, &expander_pg0, > + sizeof(expander_pg0), MPI3_SAS_EXPAND_PGAD_FORM_HANDLE, handle))) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + return -1; > + } > + > + if (ioc_status != MPI3_IOCSTATUS_SUCCESS) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + return -1; > + } > + > + parent_handle = le16_to_cpu(expander_pg0.parent_dev_handle); > + if (mpi3mr_get_sas_address(mrioc, parent_handle, &sas_address_parent) > + != 0) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + return -1; > + } > + > + port_id = expander_pg0.io_unit_port; > + hba_port = mpi3mr_get_hba_port_by_id(mrioc, port_id); > + if (!hba_port) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + return -1; > + } > + > + if (sas_address_parent != mrioc->sas_hba.sas_address) { > + spin_lock_irqsave(&mrioc->sas_node_lock, flags); > + sas_expander = > + mpi3mr_expander_find_by_sas_address(mrioc, > + sas_address_parent, hba_port); > + spin_unlock_irqrestore(&mrioc->sas_node_lock, flags); > + if (!sas_expander) { > + rc = mpi3mr_expander_add(mrioc, parent_handle); > + if (rc != 0) > + return rc; > + } else { > + /* > + * When there is a parent expander present, update it's > + * phys where child expander is connected with the link > + * speed, attached dev handle and sas address. > + */ > + for (i = 0 ; i < sas_expander->num_phys ; i++) { > + phynum_handle = > + (i << MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | > + parent_handle; > + if (mpi3mr_cfg_get_sas_exp_pg1(mrioc, > + &ioc_status, &expander_pg1, > + sizeof(expander_pg1), > + MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM, > + phynum_handle)) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + rc = -1; > + return rc; > + } > + if (ioc_status != MPI3_IOCSTATUS_SUCCESS) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + rc = -1; > + return rc; > + } > + temp_handle = le16_to_cpu( > + expander_pg1.attached_dev_handle); > + if (temp_handle != handle) > + continue; > + link_rate = (expander_pg1.negotiated_link_rate & > + MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >> > + MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT; > + mpi3mr_update_links(mrioc, sas_address_parent, > + handle, i, link_rate, hba_port); > + } > + } > + } > + > + spin_lock_irqsave(&mrioc->sas_node_lock, flags); > + sas_address = le64_to_cpu(expander_pg0.sas_address); > + sas_expander = mpi3mr_expander_find_by_sas_address(mrioc, > + sas_address, hba_port); > + spin_unlock_irqrestore(&mrioc->sas_node_lock, flags); > + > + if (sas_expander) > + return 0; > + > + sas_expander = kzalloc(sizeof(struct mpi3mr_sas_node), > + GFP_KERNEL); > + if (!sas_expander) > + return -1; > + > + sas_expander->handle = handle; > + sas_expander->num_phys = expander_pg0.num_phys; > + sas_expander->sas_address_parent = sas_address_parent; > + sas_expander->sas_address = sas_address; > + sas_expander->hba_port = hba_port; > + > + ioc_info(mrioc, > + "expander_add: handle(0x%04x), parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", > + handle, parent_handle, (unsigned long long) > + sas_expander->sas_address, sas_expander->num_phys); > + > + if (!sas_expander->num_phys) { > + rc = -1; > + goto out_fail; > + } > + sas_expander->phy = kcalloc(sas_expander->num_phys, > + sizeof(struct mpi3mr_sas_phy), GFP_KERNEL); > + if (!sas_expander->phy) { > + rc = -1; > + goto out_fail; > + } > + > + INIT_LIST_HEAD(&sas_expander->sas_port_list); > + mr_sas_port = mpi3mr_sas_port_add(mrioc, handle, sas_address_parent, > + sas_expander->hba_port); > + if (!mr_sas_port) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + rc = -1; > + goto out_fail; > + } > + sas_expander->parent_dev = &mr_sas_port->rphy->dev; > + sas_expander->rphy = mr_sas_port->rphy; > + > + for (i = 0 ; i < sas_expander->num_phys ; i++) { > + phynum_handle = (i << MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | > + handle; > + if (mpi3mr_cfg_get_sas_exp_pg1(mrioc, &ioc_status, > + &expander_pg1, sizeof(expander_pg1), > + MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM, > + phynum_handle)) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + rc = -1; > + goto out_fail; > + } > + if (ioc_status != MPI3_IOCSTATUS_SUCCESS) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + rc = -1; > + goto out_fail; > + } > + > + sas_expander->phy[i].handle = handle; > + sas_expander->phy[i].phy_id = i; > + sas_expander->phy[i].hba_port = hba_port; > + > + if ((mpi3mr_add_expander_phy(mrioc, &sas_expander->phy[i], > + expander_pg1, sas_expander->parent_dev))) { > + ioc_err(mrioc, "failure at %s:%d/%s()!\n", > + __FILE__, __LINE__, __func__); > + rc = -1; > + goto out_fail; > + } > + } > + > + if (sas_expander->enclosure_handle) { > + enclosure_dev = > + mpi3mr_enclosure_find_by_handle(mrioc, > + sas_expander->enclosure_handle); > + if (enclosure_dev) > + sas_expander->enclosure_logical_id = le64_to_cpu( > + enclosure_dev->pg0.enclosure_logical_id); > + } > + > + mpi3mr_expander_node_add(mrioc, sas_expander); > + return 0; > + > +out_fail: > + > + if (mr_sas_port) > + mpi3mr_sas_port_remove(mrioc, > + sas_expander->sas_address, > + sas_address_parent, sas_expander->hba_port); > + kfree(sas_expander->phy); > + kfree(sas_expander); > + return rc; > +} > + > +/** > + * mpi3mr_expander_node_remove - recursive removal of expander. > + * @mrioc: Adapter instance reference > + * @sas_expander: Expander device object > + * > + * Removes expander object and freeing associated memory from > + * the sas_expander_list and removes the same from SAS TL, if > + * one of the attached device is an expander then it recursively > + * removes the expander device too. > + * > + * Return nothing. > + */ > +static void mpi3mr_expander_node_remove(struct mpi3mr_ioc *mrioc, > + struct mpi3mr_sas_node *sas_expander) > +{ > + struct mpi3mr_sas_port *mr_sas_port, *next; > + unsigned long flags; > + u8 port_id; > + > + /* remove sibling ports attached to this expander */ > + list_for_each_entry_safe(mr_sas_port, next, > + &sas_expander->sas_port_list, port_list) { > + if (mrioc->reset_in_progress) > + return; > + if (mr_sas_port->remote_identify.device_type == > + SAS_END_DEVICE) > + mpi3mr_remove_device_by_sas_address(mrioc, > + mr_sas_port->remote_identify.sas_address, > + mr_sas_port->hba_port); > + else if (mr_sas_port->remote_identify.device_type == > + SAS_EDGE_EXPANDER_DEVICE || > + mr_sas_port->remote_identify.device_type == > + SAS_FANOUT_EXPANDER_DEVICE) > + mpi3mr_expander_remove(mrioc, > + mr_sas_port->remote_identify.sas_address, > + mr_sas_port->hba_port); > + } > + > + port_id = sas_expander->hba_port->port_id; > + mpi3mr_sas_port_remove(mrioc, sas_expander->sas_address, > + sas_expander->sas_address_parent, sas_expander->hba_port); > + > + ioc_info(mrioc, "expander_remove: handle(0x%04x), sas_addr(0x%016llx), port:%d\n", > + sas_expander->handle, (unsigned long long) > + sas_expander->sas_address, port_id); > + > + spin_lock_irqsave(&mrioc->sas_node_lock, flags); > + list_del(&sas_expander->list); > + spin_unlock_irqrestore(&mrioc->sas_node_lock, flags); > + > + kfree(sas_expander->phy); > + kfree(sas_expander); > +} > + > +/** > + * mpi3mr_expander_remove - Remove expander object > + * @mrioc: Adapter instance reference > + * @sas_address: Remove expander sas_address > + * @hba_port: HBA port reference > + * > + * This function remove expander object, stored in > + * mrioc->sas_expander_list and removes it from the SAS TL by > + * calling mpi3mr_expander_node_remove(). > + * > + * Return: None > + */ > +void mpi3mr_expander_remove(struct mpi3mr_ioc *mrioc, u64 sas_address, > + struct mpi3mr_hba_port *hba_port) > +{ > + struct mpi3mr_sas_node *sas_expander; > + unsigned long flags; > + > + if (mrioc->reset_in_progress) > + return; > + > + if (!hba_port) > + return; > + > + spin_lock_irqsave(&mrioc->sas_node_lock, flags); > + sas_expander = mpi3mr_expander_find_by_sas_address(mrioc, sas_address, > + hba_port); > + spin_unlock_irqrestore(&mrioc->sas_node_lock, flags); > + if (sas_expander) > + mpi3mr_expander_node_remove(mrioc, sas_expander); > + > +} > -- > 2.27.0 > Reviewed-by: Himanshu Madhani <himanshu.madhani@xxxxxxxxxx> — Himanshu Madhani Oracle Linux Engineering