OSDN Git Service

scsi: mpi3mr: Add expander devices to STL
authorSreekanth Reddy <sreekanth.reddy@broadcom.com>
Thu, 4 Aug 2022 13:12:20 +0000 (18:42 +0530)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 23 Aug 2022 03:34:05 +0000 (23:34 -0400)
Register/unregister the expander devices to SCSI Transport Layer(STL)
whenever the corresponding expander is added/removed from topology.

Link: https://lore.kernel.org/r/20220804131226.16653-10-sreekanth.reddy@broadcom.com
Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
Signed-off-by: Sreekanth Reddy <sreekanth.reddy@broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/mpi3mr/mpi3mr.h
drivers/scsi/mpi3mr/mpi3mr_os.c
drivers/scsi/mpi3mr/mpi3mr_transport.c

index 8c8703e..9cd5f88 100644 (file)
@@ -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*/
index 533fdf9..4619d42 100644 (file)
@@ -816,7 +816,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;
@@ -1486,7 +1486,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);
 
@@ -1516,6 +1519,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);
+       }
 }
 
 /**
@@ -1736,7 +1746,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;
@@ -1750,10 +1761,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:
index 3de9fa0..ff85cf3 100644 (file)
@@ -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;
@@ -160,6 +160,45 @@ out:
 }
 
 /**
+ * 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
  * @sas_address: SAS address of expander
@@ -379,6 +418,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
  * @mr_sas_node: Internal sas node object (expander or host)
@@ -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);
+
+}