scsi: elx: efct: Transport and hardware teardown routines
authorJames Smart <jsmart2021@gmail.com>
Tue, 1 Jun 2021 23:55:09 +0000 (16:55 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 16 Jun 2021 03:39:32 +0000 (23:39 -0400)
Implement routines to detach transport and hardware objects.

Link: https://lore.kernel.org/r/20210601235512.20104-29-jsmart2021@gmail.com
Reviewed-by: Daniel Wagner <dwagner@suse.de>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Co-developed-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/elx/efct/efct_hw.c
drivers/scsi/elx/efct/efct_hw.h
drivers/scsi/elx/efct/efct_xport.c

index bcd2b52..ce4736c 100644 (file)
@@ -3329,3 +3329,254 @@ efct_hw_firmware_write(struct efct_hw *hw, struct efc_dma *dma, u32 size,
 
        return rc;
 }
+
+static int
+efct_hw_cb_port_control(struct efct_hw *hw, int status, u8 *mqe,
+                       void  *arg)
+{
+       return 0;
+}
+
+int
+efct_hw_port_control(struct efct_hw *hw, enum efct_hw_port ctrl,
+                    uintptr_t value,
+                    void (*cb)(int status, uintptr_t value, void *arg),
+                    void *arg)
+{
+       int rc = -EIO;
+       u8 link[SLI4_BMBX_SIZE];
+       u32 speed = 0;
+       u8 reset_alpa = 0;
+
+       switch (ctrl) {
+       case EFCT_HW_PORT_INIT:
+               if (!sli_cmd_config_link(&hw->sli, link))
+                       rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
+                                            efct_hw_cb_port_control, NULL);
+
+               if (rc != 0) {
+                       efc_log_err(hw->os, "CONFIG_LINK failed\n");
+                       break;
+               }
+               speed = hw->config.speed;
+               reset_alpa = (u8)(value & 0xff);
+
+               rc = -EIO;
+               if (!sli_cmd_init_link(&hw->sli, link, speed, reset_alpa))
+                       rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
+                                            efct_hw_cb_port_control, NULL);
+               /* Free buffer on error, since no callback is coming */
+               if (rc)
+                       efc_log_err(hw->os, "INIT_LINK failed\n");
+               break;
+
+       case EFCT_HW_PORT_SHUTDOWN:
+               if (!sli_cmd_down_link(&hw->sli, link))
+                       rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
+                                            efct_hw_cb_port_control, NULL);
+               /* Free buffer on error, since no callback is coming */
+               if (rc)
+                       efc_log_err(hw->os, "DOWN_LINK failed\n");
+               break;
+
+       default:
+               efc_log_debug(hw->os, "unhandled control %#x\n", ctrl);
+               break;
+       }
+
+       return rc;
+}
+
+void
+efct_hw_teardown(struct efct_hw *hw)
+{
+       u32 i = 0;
+       u32 destroy_queues;
+       u32 free_memory;
+       struct efc_dma *dma;
+       struct efct *efct = hw->os;
+
+       destroy_queues = (hw->state == EFCT_HW_STATE_ACTIVE);
+       free_memory = (hw->state != EFCT_HW_STATE_UNINITIALIZED);
+
+       /* Cancel Sliport Healthcheck */
+       if (hw->sliport_healthcheck) {
+               hw->sliport_healthcheck = 0;
+               efct_hw_config_sli_port_health_check(hw, 0, 0);
+       }
+
+       if (hw->state != EFCT_HW_STATE_QUEUES_ALLOCATED) {
+               hw->state = EFCT_HW_STATE_TEARDOWN_IN_PROGRESS;
+
+               efct_hw_flush(hw);
+
+               if (list_empty(&hw->cmd_head))
+                       efc_log_debug(hw->os,
+                                     "All commands completed on MQ queue\n");
+               else
+                       efc_log_debug(hw->os,
+                                     "Some cmds still pending on MQ queue\n");
+
+               /* Cancel any remaining commands */
+               efct_hw_command_cancel(hw);
+       } else {
+               hw->state = EFCT_HW_STATE_TEARDOWN_IN_PROGRESS;
+       }
+
+       dma_free_coherent(&efct->pci->dev,
+                         hw->rnode_mem.size, hw->rnode_mem.virt,
+                         hw->rnode_mem.phys);
+       memset(&hw->rnode_mem, 0, sizeof(struct efc_dma));
+
+       if (hw->io) {
+               for (i = 0; i < hw->config.n_io; i++) {
+                       if (hw->io[i] && hw->io[i]->sgl &&
+                           hw->io[i]->sgl->virt) {
+                               dma_free_coherent(&efct->pci->dev,
+                                                 hw->io[i]->sgl->size,
+                                                 hw->io[i]->sgl->virt,
+                                                 hw->io[i]->sgl->phys);
+                       }
+                       kfree(hw->io[i]);
+                       hw->io[i] = NULL;
+               }
+               kfree(hw->io);
+               hw->io = NULL;
+               kfree(hw->wqe_buffs);
+               hw->wqe_buffs = NULL;
+       }
+
+       dma = &hw->xfer_rdy;
+       dma_free_coherent(&efct->pci->dev,
+                         dma->size, dma->virt, dma->phys);
+       memset(dma, 0, sizeof(struct efc_dma));
+
+       dma = &hw->loop_map;
+       dma_free_coherent(&efct->pci->dev,
+                         dma->size, dma->virt, dma->phys);
+       memset(dma, 0, sizeof(struct efc_dma));
+
+       for (i = 0; i < hw->wq_count; i++)
+               sli_queue_free(&hw->sli, &hw->wq[i], destroy_queues,
+                              free_memory);
+
+       for (i = 0; i < hw->rq_count; i++)
+               sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues,
+                              free_memory);
+
+       for (i = 0; i < hw->mq_count; i++)
+               sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues,
+                              free_memory);
+
+       for (i = 0; i < hw->cq_count; i++)
+               sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues,
+                              free_memory);
+
+       for (i = 0; i < hw->eq_count; i++)
+               sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues,
+                              free_memory);
+
+       /* Free rq buffers */
+       efct_hw_rx_free(hw);
+
+       efct_hw_queue_teardown(hw);
+
+       kfree(hw->wq_cpu_array);
+
+       sli_teardown(&hw->sli);
+
+       /* record the fact that the queues are non-functional */
+       hw->state = EFCT_HW_STATE_UNINITIALIZED;
+
+       /* free sequence free pool */
+       kfree(hw->seq_pool);
+       hw->seq_pool = NULL;
+
+       /* free hw_wq_callback pool */
+       efct_hw_reqtag_pool_free(hw);
+
+       mempool_destroy(hw->cmd_ctx_pool);
+       mempool_destroy(hw->mbox_rqst_pool);
+
+       /* Mark HW setup as not having been called */
+       hw->hw_setup_called = false;
+}
+
+static int
+efct_hw_sli_reset(struct efct_hw *hw, enum efct_hw_reset reset,
+                 enum efct_hw_state prev_state)
+{
+       int rc = 0;
+
+       switch (reset) {
+       case EFCT_HW_RESET_FUNCTION:
+               efc_log_debug(hw->os, "issuing function level reset\n");
+               if (sli_reset(&hw->sli)) {
+                       efc_log_err(hw->os, "sli_reset failed\n");
+                       rc = -EIO;
+               }
+               break;
+       case EFCT_HW_RESET_FIRMWARE:
+               efc_log_debug(hw->os, "issuing firmware reset\n");
+               if (sli_fw_reset(&hw->sli)) {
+                       efc_log_err(hw->os, "sli_soft_reset failed\n");
+                       rc = -EIO;
+               }
+               /*
+                * Because the FW reset leaves the FW in a non-running state,
+                * follow that with a regular reset.
+                */
+               efc_log_debug(hw->os, "issuing function level reset\n");
+               if (sli_reset(&hw->sli)) {
+                       efc_log_err(hw->os, "sli_reset failed\n");
+                       rc = -EIO;
+               }
+               break;
+       default:
+               efc_log_err(hw->os, "unknown type - no reset performed\n");
+               hw->state = prev_state;
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+int
+efct_hw_reset(struct efct_hw *hw, enum efct_hw_reset reset)
+{
+       int rc = 0;
+       enum efct_hw_state prev_state = hw->state;
+
+       if (hw->state != EFCT_HW_STATE_ACTIVE)
+               efc_log_debug(hw->os,
+                             "HW state %d is not active\n", hw->state);
+
+       hw->state = EFCT_HW_STATE_RESET_IN_PROGRESS;
+
+       /*
+        * If the prev_state is already reset/teardown in progress,
+        * don't continue further
+        */
+       if (prev_state == EFCT_HW_STATE_RESET_IN_PROGRESS ||
+           prev_state == EFCT_HW_STATE_TEARDOWN_IN_PROGRESS)
+               return efct_hw_sli_reset(hw, reset, prev_state);
+
+       if (prev_state != EFCT_HW_STATE_UNINITIALIZED) {
+               efct_hw_flush(hw);
+
+               if (list_empty(&hw->cmd_head))
+                       efc_log_debug(hw->os,
+                                     "All commands completed on MQ queue\n");
+               else
+                       efc_log_err(hw->os,
+                                   "Some commands still pending on MQ queue\n");
+       }
+
+       /* Reset the chip */
+       rc = efct_hw_sli_reset(hw, reset, prev_state);
+       if (rc == -EINVAL)
+               return -EIO;
+
+       return rc;
+}
index 016ba6a..32cff55 100644 (file)
@@ -734,4 +734,31 @@ typedef void (*efct_hw_async_cb_t)(struct efct_hw *hw, int status,
 int
 efct_hw_async_call(struct efct_hw *hw, efct_hw_async_cb_t callback, void *arg);
 
+struct hw_eq *efct_hw_new_eq(struct efct_hw *hw, u32 entry_count);
+struct hw_cq *efct_hw_new_cq(struct hw_eq *eq, u32 entry_count);
+u32
+efct_hw_new_cq_set(struct hw_eq *eqs[], struct hw_cq *cqs[],
+                  u32 num_cqs, u32 entry_count);
+struct hw_mq *efct_hw_new_mq(struct hw_cq *cq, u32 entry_count);
+struct hw_wq
+*efct_hw_new_wq(struct hw_cq *cq, u32 entry_count);
+u32
+efct_hw_new_rq_set(struct hw_cq *cqs[], struct hw_rq *rqs[],
+                  u32 num_rq_pairs, u32 entry_count);
+void efct_hw_del_eq(struct hw_eq *eq);
+void efct_hw_del_cq(struct hw_cq *cq);
+void efct_hw_del_mq(struct hw_mq *mq);
+void efct_hw_del_wq(struct hw_wq *wq);
+void efct_hw_del_rq(struct hw_rq *rq);
+void efct_hw_queue_teardown(struct efct_hw *hw);
+void efct_hw_teardown(struct efct_hw *hw);
+int
+efct_hw_reset(struct efct_hw *hw, enum efct_hw_reset reset);
+
+int
+efct_hw_port_control(struct efct_hw *hw, enum efct_hw_port ctrl,
+                    uintptr_t value,
+               void (*cb)(int status, uintptr_t value, void *arg),
+               void *arg);
+
 #endif /* __EFCT_H__ */
index 58dad8a..e1040d5 100644 (file)
@@ -498,3 +498,181 @@ efct_scsi_release_fc_transport(void)
 
        efct_vport_fc_tt = NULL;
 }
+
+void
+efct_xport_detach(struct efct_xport *xport)
+{
+       struct efct *efct = xport->efct;
+
+       /* free resources associated with target-server and initiator-client */
+       efct_scsi_tgt_del_device(efct);
+
+       efct_scsi_del_device(efct);
+
+       /*Shutdown FC Statistics timer*/
+       if (timer_pending(&xport->stats_timer))
+               del_timer(&xport->stats_timer);
+
+       efct_hw_teardown(&efct->hw);
+
+       efct_xport_delete_debugfs(efct);
+}
+
+static void
+efct_xport_domain_free_cb(struct efc *efc, void *arg)
+{
+       struct completion *done = arg;
+
+       complete(done);
+}
+
+int
+efct_xport_control(struct efct_xport *xport, enum efct_xport_ctrl cmd, ...)
+{
+       u32 rc = 0;
+       struct efct *efct = NULL;
+       va_list argp;
+
+       efct = xport->efct;
+
+       switch (cmd) {
+       case EFCT_XPORT_PORT_ONLINE: {
+               /* Bring the port on-line */
+               rc = efct_hw_port_control(&efct->hw, EFCT_HW_PORT_INIT, 0,
+                                         NULL, NULL);
+               if (rc)
+                       efc_log_err(efct,
+                                   "%s: Can't init port\n", efct->desc);
+               else
+                       xport->configured_link_state = cmd;
+               break;
+       }
+       case EFCT_XPORT_PORT_OFFLINE: {
+               if (efct_hw_port_control(&efct->hw, EFCT_HW_PORT_SHUTDOWN, 0,
+                                        NULL, NULL))
+                       efc_log_err(efct, "port shutdown failed\n");
+               else
+                       xport->configured_link_state = cmd;
+               break;
+       }
+
+       case EFCT_XPORT_SHUTDOWN: {
+               struct completion done;
+               unsigned long timeout;
+
+               /* if a PHYSDEV reset was performed (e.g. hw dump), will affect
+                * all PCI functions; orderly shutdown won't work,
+                * just force free
+                */
+               if (sli_reset_required(&efct->hw.sli)) {
+                       struct efc_domain *domain = efct->efcport->domain;
+
+                       if (domain)
+                               efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_LOST,
+                                             domain);
+               } else {
+                       efct_hw_port_control(&efct->hw, EFCT_HW_PORT_SHUTDOWN,
+                                            0, NULL, NULL);
+               }
+
+               init_completion(&done);
+
+               efc_register_domain_free_cb(efct->efcport,
+                                           efct_xport_domain_free_cb, &done);
+
+               efc_log_debug(efct, "Waiting %d seconds for domain shutdown\n",
+                             (EFC_SHUTDOWN_TIMEOUT_USEC / 1000000));
+
+               timeout = usecs_to_jiffies(EFC_SHUTDOWN_TIMEOUT_USEC);
+               if (!wait_for_completion_timeout(&done, timeout)) {
+                       efc_log_err(efct, "Domain shutdown timed out!!\n");
+                       WARN_ON(1);
+               }
+
+               efc_register_domain_free_cb(efct->efcport, NULL, NULL);
+
+               /* Free up any saved virtual ports */
+               efc_vport_del_all(efct->efcport);
+               break;
+       }
+
+       /*
+        * Set wwnn for the port. This will be used instead of the default
+        * provided by FW.
+        */
+       case EFCT_XPORT_WWNN_SET: {
+               u64 wwnn;
+
+               /* Retrieve arguments */
+               va_start(argp, cmd);
+               wwnn = va_arg(argp, uint64_t);
+               va_end(argp);
+
+               efc_log_debug(efct, " WWNN %016llx\n", wwnn);
+               xport->req_wwnn = wwnn;
+
+               break;
+       }
+       /*
+        * Set wwpn for the port. This will be used instead of the default
+        * provided by FW.
+        */
+       case EFCT_XPORT_WWPN_SET: {
+               u64 wwpn;
+
+               /* Retrieve arguments */
+               va_start(argp, cmd);
+               wwpn = va_arg(argp, uint64_t);
+               va_end(argp);
+
+               efc_log_debug(efct, " WWPN %016llx\n", wwpn);
+               xport->req_wwpn = wwpn;
+
+               break;
+       }
+
+       default:
+               break;
+       }
+       return rc;
+}
+
+void
+efct_xport_free(struct efct_xport *xport)
+{
+       if (xport) {
+               efct_io_pool_free(xport->io_pool);
+
+               kfree(xport);
+       }
+}
+
+void
+efct_release_fc_transport(struct scsi_transport_template *transport_template)
+{
+       if (transport_template)
+               pr_err("releasing transport layer\n");
+
+       /* Releasing FC transport */
+       fc_release_transport(transport_template);
+}
+
+static void
+efct_xport_remove_host(struct Scsi_Host *shost)
+{
+       fc_remove_host(shost);
+}
+
+void
+efct_scsi_del_device(struct efct *efct)
+{
+       if (!efct->shost)
+               return;
+
+       efc_log_debug(efct, "Unregistering with Transport Layer\n");
+       efct_xport_remove_host(efct->shost);
+       efc_log_debug(efct, "Unregistering with SCSI Midlayer\n");
+       scsi_remove_host(efct->shost);
+       scsi_host_put(efct->shost);
+       efct->shost = NULL;
+}