All of lore.kernel.org
 help / color / mirror / Atom feed
From: James Smart <jsmart2021@gmail.com>
To: linux-scsi@vger.kernel.org
Cc: James Smart <jsmart2021@gmail.com>,
	Ram Vegesna <ram.vegesna@broadcom.com>
Subject: [PATCH 27/32] elx: efct: xport and hardware teardown routines
Date: Wed, 23 Oct 2019 14:55:52 -0700	[thread overview]
Message-ID: <20191023215557.12581-28-jsmart2021@gmail.com> (raw)
In-Reply-To: <20191023215557.12581-1-jsmart2021@gmail.com>

This patch continues the efct driver population.

This patch adds driver definitions for:
Routines to detach xport and hardware objects.

Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
---
 drivers/scsi/elx/efct/efct_hw.c    | 499 +++++++++++++++++++++++++++++++++++++
 drivers/scsi/elx/efct/efct_hw.h    |  31 +++
 drivers/scsi/elx/efct/efct_xport.c | 483 +++++++++++++++++++++++++++++++++++
 3 files changed, 1013 insertions(+)

diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c
index f01a54d874b1..48cdbeebd058 100644
--- a/drivers/scsi/elx/efct/efct_hw.c
+++ b/drivers/scsi/elx/efct/efct_hw.c
@@ -5217,3 +5217,502 @@ efct_hw_cb_host_stat(struct efct_hw_s *hw, int status,
 
 	return 0;
 }
+
+/**
+ * @brief Called when the port control command completes.
+ *
+ * @par Description
+ * We only need to free the mailbox command buffer.
+ *
+ * @param hw Hardware context.
+ * @param status Status field from the mbox completion.
+ * @param mqe Mailbox response structure.
+ * @param arg Pointer to a callback function that signals the caller that the
+ * command is done.
+ *
+ * @return Returns 0.
+ */
+static int
+efct_hw_cb_port_control(struct efct_hw_s *hw, int status, u8 *mqe,
+			void  *arg)
+{
+	kfree(mqe);
+	return 0;
+}
+
+/**
+ * @ingroup port
+ * @brief Control a port (initialize, shutdown, or set link configuration).
+ *
+ * @par Description
+ * This function controls a port depending on the @c ctrl parameter:
+ * - @b EFCT_HW_PORT_INIT -
+ * Issues the CONFIG_LINK and INIT_LINK commands for the specified port.
+ * The HW generates an EFC_HW_DOMAIN_FOUND event when the link comes up.
+ * .
+ * - @b EFCT_HW_PORT_SHUTDOWN -
+ * Issues the DOWN_LINK command for the specified port.
+ * The HW generates an EFC_HW_DOMAIN_LOST event when the link is down.
+ * .
+ * - @b EFCT_HW_PORT_SET_LINK_CONFIG -
+ * Sets the link configuration.
+ *
+ * @param hw Hardware context.
+ * @param ctrl Specifies the operation:
+ * - EFCT_HW_PORT_INIT
+ * - EFCT_HW_PORT_SHUTDOWN
+ * - EFCT_HW_PORT_SET_LINK_CONFIG
+ *
+ * @param value Operation-specific value.
+ * - EFCT_HW_PORT_INIT - Selective reset AL_PA
+ * - EFCT_HW_PORT_SHUTDOWN - N/A
+ *
+ * @param cb Callback function to invoke the following operation.
+ * - EFCT_HW_PORT_INIT/EFCT_HW_PORT_SHUTDOWN - NULL (link events
+ * are handled by the EFCT_HW_CB_DOMAIN callbacks).
+ *
+ * @param arg Callback argument invoked after the command completes.
+ * - EFCT_HW_PORT_INIT/EFCT_HW_PORT_SHUTDOWN - NULL (link events
+ * are handled by the EFCT_HW_CB_DOMAIN callbacks).
+ *
+ * @return Returns 0 on success, or a non-zero value on failure.
+ */
+enum efct_hw_rtn_e
+efct_hw_port_control(struct efct_hw_s *hw, enum efct_hw_port_e ctrl,
+		     uintptr_t value,
+		void (*cb)(int status, uintptr_t value, void *arg),
+		void *arg)
+{
+	enum efct_hw_rtn_e rc = EFCT_HW_RTN_ERROR;
+
+	switch (ctrl) {
+	case EFCT_HW_PORT_INIT:
+	{
+		u8	*init_link;
+		u32 speed = 0;
+		u8 reset_alpa = 0;
+
+		u8	*cfg_link;
+
+		cfg_link = kmalloc(SLI4_BMBX_SIZE, GFP_ATOMIC);
+		if (!cfg_link)
+			return EFCT_HW_RTN_NO_MEMORY;
+
+		if (!sli_cmd_config_link(&hw->sli, cfg_link,
+					SLI4_BMBX_SIZE))
+			rc = efct_hw_command(hw, cfg_link,
+					     EFCT_CMD_NOWAIT,
+					     efct_hw_cb_port_control,
+					     NULL);
+
+		if (rc != EFCT_HW_RTN_SUCCESS) {
+			kfree(cfg_link);
+			efc_log_err(hw->os, "CONFIG_LINK failed\n");
+			break;
+		}
+		speed = hw->config.speed;
+		reset_alpa = (u8)(value & 0xff);
+
+		/* Allocate a new buffer for the init_link command */
+		init_link = kmalloc(SLI4_BMBX_SIZE, GFP_ATOMIC);
+		if (!init_link)
+			return EFCT_HW_RTN_NO_MEMORY;
+
+		rc = EFCT_HW_RTN_ERROR;
+		if (!sli_cmd_init_link(&hw->sli, init_link, SLI4_BMBX_SIZE,
+				      speed, reset_alpa))
+			rc = efct_hw_command(hw, init_link, EFCT_CMD_NOWAIT,
+					     efct_hw_cb_port_control, NULL);
+		/* Free buffer on error, since no callback is coming */
+		if (rc != EFCT_HW_RTN_SUCCESS) {
+			kfree(init_link);
+			efc_log_err(hw->os, "INIT_LINK failed\n");
+		}
+		break;
+	}
+	case EFCT_HW_PORT_SHUTDOWN:
+	{
+		u8	*down_link;
+
+		down_link = kmalloc(SLI4_BMBX_SIZE, GFP_ATOMIC);
+		if (!down_link)
+			return EFCT_HW_RTN_NO_MEMORY;
+
+		if (!sli_cmd_down_link(&hw->sli, down_link, SLI4_BMBX_SIZE))
+			rc = efct_hw_command(hw, down_link, EFCT_CMD_NOWAIT,
+					     efct_hw_cb_port_control, NULL);
+		/* Free buffer on error, since no callback is coming */
+		if (rc != EFCT_HW_RTN_SUCCESS) {
+			kfree(down_link);
+			efc_log_err(hw->os, "DOWN_LINK failed\n");
+		}
+		break;
+	}
+	default:
+		efc_log_test(hw->os, "unhandled control %#x\n", ctrl);
+		break;
+	}
+
+	return rc;
+}
+
+/**
+ * @ingroup devInitShutdown
+ * @brief Tear down the Hardware Abstraction Layer module.
+ *
+ * @par Description
+ * Frees memory structures needed by the device, and shuts down the device.
+ * Does not free the HW context memory (which is done by the caller).
+ *
+ * @param hw Hardware context allocated by the caller.
+ *
+ * @return Returns 0 on success, or a non-zero value on failure.
+ */
+enum efct_hw_rtn_e
+efct_hw_teardown(struct efct_hw_s *hw)
+{
+	u32	i = 0;
+	u32	iters = 10;
+	u32	max_rpi;
+	u32 destroy_queues;
+	u32 free_memory;
+	struct efc_dma_s *dma;
+	struct efct_s *efct = hw->os;
+
+	destroy_queues = (hw->state == EFCT_HW_STATE_ACTIVE);
+	free_memory = (hw->state != EFCT_HW_STATE_UNINITIALIZED);
+
+	/* shutdown target wqe timer */
+	shutdown_target_wqe_timer(hw);
+
+	/* Cancel watchdog timer if enabled */
+	if (hw->watchdog_timeout) {
+		hw->watchdog_timeout = 0;
+		efct_hw_config_watchdog_timer(hw);
+	}
+
+	/* 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 there are outstanding commands, wait for them to complete
+		 */
+		while (!list_empty(&hw->cmd_head) && iters) {
+			mdelay(10);
+			efct_hw_flush(hw);
+			iters--;
+		}
+
+		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;
+	}
+
+	max_rpi = hw->sli.qinfo.max_qcount[SLI_RSRC_RPI];
+	if (hw->rpi_ref) {
+		for (i = 0; i < max_rpi; i++) {
+			u32 count;
+
+			count = atomic_read(&hw->rpi_ref[i].rpi_count);
+			if (count)
+				efc_log_debug(hw->os,
+					       "non-zero ref [%d]=%d\n",
+					       i, count);
+		}
+		kfree(hw->rpi_ref);
+		hw->rpi_ref = NULL;
+	}
+
+	dma_free_coherent(&efct->pcidev->dev,
+			  hw->rnode_mem.size, hw->rnode_mem.virt,
+			  hw->rnode_mem.phys);
+	memset(&hw->rnode_mem, 0, sizeof(struct efc_dma_s));
+
+	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->pcidev->dev,
+						  hw->io[i]->sgl->size,
+						  hw->io[i]->sgl->virt,
+						  hw->io[i]->sgl->phys);
+				memset(&hw->io[i]->sgl, 0,
+				       sizeof(struct efc_dma_s));
+			}
+			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->pcidev->dev,
+			  dma->size, dma->virt, dma->phys);
+	memset(dma, 0, sizeof(struct efc_dma_s));
+
+	dma = &hw->dump_sges;
+	dma_free_coherent(&efct->pcidev->dev,
+			  dma->size, dma->virt, dma->phys);
+	memset(dma, 0, sizeof(struct efc_dma_s));
+
+	dma = &hw->loop_map;
+	dma_free_coherent(&efct->pcidev->dev,
+			  dma->size, dma->virt, dma->phys);
+	memset(dma, 0, sizeof(struct efc_dma_s));
+
+	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);
+
+	efct_hw_qtop_free(hw->qtop);
+
+	/* Free rq buffers */
+	efct_hw_rx_free(hw);
+
+	efct_hw_queue_teardown(hw);
+
+	if (sli_teardown(&hw->sli))
+		efc_log_err(hw->os, "SLI teardown failed\n");
+
+	/* record the fact that the queues are non-functional */
+	hw->state = EFCT_HW_STATE_UNINITIALIZED;
+
+	/* free sequence free pool */
+	efct_array_free(hw->seq_pool);
+	hw->seq_pool = NULL;
+
+	/* free hw_wq_callback pool */
+	efct_pool_free(hw->wq_reqtag_pool);
+
+	/* Mark HW setup as not having been called */
+	hw->hw_setup_called = false;
+
+	return EFCT_HW_RTN_SUCCESS;
+}
+
+static enum efct_hw_rtn_e
+efct_hw_sli_reset(struct efct_hw_s *hw, enum efct_hw_reset_e reset,
+		  enum efct_hw_state_e prev_state)
+{
+	enum efct_hw_rtn_e rc = EFCT_HW_RTN_SUCCESS;
+
+	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 = EFCT_HW_RTN_ERROR;
+		}
+		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 = EFCT_HW_RTN_ERROR;
+		}
+		/*
+		 * 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 = EFCT_HW_RTN_ERROR;
+		}
+		break;
+	default:
+		efc_log_err(hw->os,
+			     "unknown reset type - no reset performed\n");
+		hw->state = prev_state;
+		rc = EFCT_HW_RTN_INVALID_ARG;
+		break;
+	}
+
+	return rc;
+}
+
+enum efct_hw_rtn_e
+efct_hw_reset(struct efct_hw_s *hw, enum efct_hw_reset_e reset)
+{
+	u32	i;
+	enum efct_hw_rtn_e rc = EFCT_HW_RTN_SUCCESS;
+	u32	iters;
+	enum efct_hw_state_e prev_state = hw->state;
+	unsigned long flags = 0;
+	struct efct_hw_io_s *temp;
+	u32 destroy_queues;
+	u32 free_memory;
+
+	if (hw->state != EFCT_HW_STATE_ACTIVE)
+		efc_log_test(hw->os,
+			      "HW state %d is not active\n", hw->state);
+
+	destroy_queues = (hw->state == EFCT_HW_STATE_ACTIVE);
+	free_memory = (hw->state != EFCT_HW_STATE_UNINITIALIZED);
+	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);
+
+	/* shutdown target wqe timer */
+	shutdown_target_wqe_timer(hw);
+
+	if (prev_state != EFCT_HW_STATE_UNINITIALIZED) {
+		efct_hw_flush(hw);
+
+		/*
+		 * If an mailbox command requiring a DMA is outstanding
+		 * (SFP/DDM), then the FW will UE when the reset is issued.
+		 * So attempt to complete all mailbox commands.
+		 */
+		iters = 10;
+		while (!list_empty(&hw->cmd_head) && iters) {
+			mdelay(10);
+			efct_hw_flush(hw);
+			iters--;
+		}
+
+		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 commands still pending on MQ queue\n");
+	}
+
+	/* Reset the chip */
+	rc = efct_hw_sli_reset(hw, reset, prev_state);
+	if (rc == EFCT_HW_RTN_INVALID_ARG)
+		return EFCT_HW_RTN_ERROR;
+
+	/* Not safe to walk command/io lists unless they've been initialized */
+	if (prev_state != EFCT_HW_STATE_UNINITIALIZED) {
+		efct_hw_command_cancel(hw);
+
+		/* Try to clean up the io_inuse list */
+		efct_hw_io_cancel(hw);
+
+		efct_hw_link_event_init(hw);
+
+		spin_lock_irqsave(&hw->io_lock, flags);
+			/*
+			 * The io lists should be empty, but remove any that
+			 * didn't get cleaned up.
+			 */
+			while (!list_empty(&hw->io_timed_wqe)) {
+				temp = list_first_entry(&hw->io_timed_wqe,
+							struct efct_hw_io_s,
+							list_entry);
+				list_del(&temp->wqe_link);
+			}
+
+			while (!list_empty(&hw->io_free)) {
+				temp = list_first_entry(&hw->io_free,
+							struct efct_hw_io_s,
+							list_entry);
+				list_del(&temp->list_entry);
+			}
+
+			while (!list_empty(&hw->io_wait_free)) {
+				temp = list_first_entry(&hw->io_wait_free,
+							struct efct_hw_io_s,
+							list_entry);
+				list_del(&temp->list_entry);
+			}
+		spin_unlock_irqrestore(&hw->io_lock, flags);
+
+		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->hw_rq_count; i++) {
+			struct hw_rq_s *rq = hw->hw_rq[i];
+
+			if (rq->rq_tracker) {
+				u32 j;
+
+				for (j = 0; j < rq->entry_count; j++)
+					rq->rq_tracker[j] = NULL;
+			}
+		}
+
+		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);
+
+		/* Teardown the HW queue topology */
+		efct_hw_queue_teardown(hw);
+
+		/*
+		 * Reset the request tag pool, the HW IO request tags
+		 * are reassigned in efct_hw_setup_io()
+		 */
+		efct_hw_reqtag_reset(hw);
+	} else {
+		/* Free rq buffers */
+		efct_hw_rx_free(hw);
+	}
+
+	return rc;
+}
+
+int
+efct_hw_get_num_eq(struct efct_hw_s *hw)
+{
+	return hw->eq_count;
+}
diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h
index b372250c4408..6910dca917a4 100644
--- a/drivers/scsi/elx/efct/efct_hw.h
+++ b/drivers/scsi/elx/efct/efct_hw.h
@@ -1169,5 +1169,36 @@ efct_hw_get_host_stats(struct efct_hw_s *hw,
 			void *arg),
 		void *arg);
 
+struct hw_eq_s *efct_hw_new_eq(struct efct_hw_s *hw, u32 entry_count);
+struct hw_cq_s *efct_hw_new_cq(struct hw_eq_s *eq, u32 entry_count);
+extern u32
+efct_hw_new_cq_set(struct hw_eq_s *eqs[], struct hw_cq_s *cqs[],
+		   u32 num_cqs, u32 entry_count);
+struct hw_mq_s *efct_hw_new_mq(struct hw_cq_s *cq, u32 entry_count);
+extern struct hw_wq_s
+*efct_hw_new_wq(struct hw_cq_s *cq, u32 entry_count,
+		u32 class, u32 ulp);
+extern struct hw_rq_s
+*efct_hw_new_rq(struct hw_cq_s *cq, u32 entry_count, u32 ulp);
+extern u32
+efct_hw_new_rq_set(struct hw_cq_s *cqs[], struct hw_rq_s *rqs[],
+		   u32 num_rq_pairs, u32 entry_count);
+void efct_hw_del_eq(struct hw_eq_s *eq);
+void efct_hw_del_cq(struct hw_cq_s *cq);
+void efct_hw_del_mq(struct hw_mq_s *mq);
+void efct_hw_del_wq(struct hw_wq_s *wq);
+void efct_hw_del_rq(struct hw_rq_s *rq);
+void efct_hw_queue_dump(struct efct_hw_s *hw);
+void efct_hw_queue_teardown(struct efct_hw_s *hw);
+enum efct_hw_rtn_e efct_hw_teardown(struct efct_hw_s *hw);
+enum efct_hw_rtn_e
+efct_hw_reset(struct efct_hw_s *hw, enum efct_hw_reset_e reset);
+int efct_hw_get_num_eq(struct efct_hw_s *hw);
+
+extern enum efct_hw_rtn_e
+efct_hw_port_control(struct efct_hw_s *hw, enum efct_hw_port_e ctrl,
+		     uintptr_t value,
+		void (*cb)(int status, uintptr_t value, void *arg),
+		void *arg);
 
 #endif /* __EFCT_H__ */
diff --git a/drivers/scsi/elx/efct/efct_xport.c b/drivers/scsi/elx/efct/efct_xport.c
index 83782794225f..d43027c57732 100644
--- a/drivers/scsi/elx/efct/efct_xport.c
+++ b/drivers/scsi/elx/efct/efct_xport.c
@@ -663,3 +663,486 @@ efct_scsi_release_fc_transport(void)
 
 	return 0;
 }
+
+/**
+ * @brief Detaches the transport from the device.
+ *
+ * @par Description
+ * Performs the functions required to shut down a device.
+ *
+ * @param xport Pointer to transport object.
+ *
+ * @return Returns 0 on success or a non-zero value on failure.
+ */
+int
+efct_xport_detach(struct efct_xport_s *xport)
+{
+	struct efct_s *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*/
+	del_timer(&efct->xport->stats_timer);
+
+	efct_hw_teardown(&efct->hw);
+
+	efct_xport_delete_debugfs(efct);
+
+	return 0;
+}
+
+/**
+ * @brief domain list empty callback
+ *
+ * @par Description
+ * Function is invoked when the domain is freed. By convention
+ * @c arg points to an struct completion instance, that is incremented.
+ *
+ * @param efct Pointer to device object.
+ * @param arg Pointer to completion instance.
+ *
+ * @return None.
+ */
+static void
+efct_xport_domain_free_cb(struct efc_lport *efc, void *arg)
+{
+	struct completion *done = arg;
+
+	complete(done);
+}
+
+/**
+ * @brief post node event callback
+ *
+ * @par Description
+ * This function is called from the mailbox completion interrupt context to
+ * post an event to a node object. By doing this in the interrupt context,
+ * it has the benefit of only posting events in the interrupt context,
+ * deferring the need to create a per event node lock.
+ *
+ * @param hw Pointer to HW structure.
+ * @param status Completion status for mailbox command.
+ * @param mqe Mailbox queue completion entry.
+ * @param arg Callback argument.
+ *
+ * @return Returns 0 on success, a negative error code value on failure.
+ */
+
+static int
+efct_xport_post_node_event_cb(struct efct_hw_s *hw, int status,
+			      u8 *mqe, void *arg)
+{
+	struct efct_xport_post_node_event_s *payload = arg;
+
+	if (payload) {
+		efc_node_post_shutdown(payload->node, payload->evt,
+				       payload->context);
+		complete(&payload->done);
+		if (atomic_sub_and_test(1, &payload->refcnt))
+			kfree(payload);
+	}
+	return 0;
+}
+
+/**
+ * @brief Initiate force free.
+ *
+ * @par Description
+ * Perform force free of EFCT.
+ *
+ * @param xport Pointer to transport object.
+ *
+ * @return None.
+ */
+
+static void
+efct_xport_force_free(struct efct_xport_s *xport)
+{
+	struct efct_s *efct = xport->efct;
+	struct efc_lport *efc = efct->efcport;
+
+	efc_log_debug(efct, "reset required, do force shutdown\n");
+
+	if (!efc->domain) {
+		efc_log_err(efct, "Domain is already freed\n");
+		return;
+	}
+
+	efc_domain_force_free(efc->domain);
+}
+
+/**
+ * @brief Perform transport attach function.
+ *
+ * @par Description
+ * Perform the attach function, which for the FC transport makes a HW call
+ * to bring up the link.
+ *
+ * @param xport pointer to transport object.
+ * @param cmd command to execute.
+ *
+ * efct_xport_control(struct efct_xport_s *xport, EFCT_XPORT_PORT_ONLINE)
+ * efct_xport_control(struct efct_xport_s *xport, EFCT_XPORT_PORT_OFFLINE)
+ * efct_xport_control(struct efct_xport_s *xport, EFCT_XPORT_PORT_SHUTDOWN)
+ * efct_xport_control(struct efct_xport_s *xport, EFCT_XPORT_POST_NODE_EVENT,
+ *		     struct efct_node_s *node, efc_sm_event_e, void *context)
+ *
+ * @return Returns 0 on success, or a negative error code value on failure.
+ */
+
+int
+efct_xport_control(struct efct_xport_s *xport, enum efct_xport_ctrl_e cmd, ...)
+{
+	u32 rc = 0;
+	struct efct_s *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;
+		u32 reset_required;
+		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 (efct_hw_get(&efct->hw, EFCT_HW_RESET_REQUIRED,
+				&reset_required) != EFCT_HW_RTN_SUCCESS)
+			reset_required = 0;
+
+		if (reset_required) {
+			efc_log_debug(efct,
+				       "reset required, do force shutdown\n");
+			efct_xport_force_free(xport);
+			break;
+		}
+		init_completion(&done);
+
+		efc_register_domain_free_cb(efct->efcport,
+					efct_xport_domain_free_cb, &done);
+
+		if (efct_hw_port_control(&efct->hw, EFCT_HW_PORT_SHUTDOWN, 0,
+					 NULL, NULL)) {
+			efc_log_debug(efct,
+				       "port shutdown failed, do force shutdown\n");
+			efct_xport_force_free(xport);
+		} else {
+			efc_log_debug(efct,
+				       "Waiting %d seconds for domain shutdown.\n",
+			(EFCT_FC_DOMAIN_SHUTDOWN_TIMEOUT_USEC / 1000000));
+
+			timeout = usecs_to_jiffies(
+					EFCT_FC_DOMAIN_SHUTDOWN_TIMEOUT_USEC);
+			if (!wait_for_completion_timeout(&done, timeout)) {
+				efc_log_debug(efct,
+					       "Domain shutdown timed out!!\n");
+				efct_xport_force_free(xport);
+			}
+		}
+
+		efc_register_domain_free_cb(efct->efcport, NULL, NULL);
+
+		/* Free up any saved virtual ports */
+		efc_vport_del_all(efct->efcport);
+		break;
+	}
+
+	/*
+	 * POST_NODE_EVENT:  post an event to a node object
+	 *
+	 * This transport function is used to post an event to a node object.
+	 * It does this by submitting a NOP mailbox command to defer execution
+	 * to the interrupt context (thereby enforcing the serialized execution
+	 * of event posting to the node state machine instances)
+	 */
+	case EFCT_XPORT_POST_NODE_EVENT: {
+		struct efc_node_s *node;
+		u32	evt;
+		void *context;
+		struct efct_xport_post_node_event_s *payload = NULL;
+		struct efct_s *efct;
+		struct efct_hw_s *hw;
+
+		/* Retrieve arguments */
+		va_start(argp, cmd);
+		node = va_arg(argp, struct efc_node_s *);
+		evt = va_arg(argp, u32);
+		context = va_arg(argp, void *);
+		va_end(argp);
+
+		payload = kmalloc(sizeof(*payload), GFP_KERNEL);
+		if (!payload)
+			return -1;
+
+		memset(payload, 0, sizeof(*payload));
+
+		efct = node->efc->base;
+		hw = &efct->hw;
+
+		/* if node's state machine is disabled,
+		 * don't bother continuing
+		 */
+		if (!node->sm.current_state) {
+			efc_log_test(efct, "node %p state machine disabled\n",
+				      node);
+			kfree(payload);
+			rc = -1;
+			break;
+		}
+
+		/* Setup payload */
+		init_completion(&payload->done);
+
+		/* one for self and one for callback */
+		atomic_set(&payload->refcnt, 2);
+		payload->node = node;
+		payload->evt = evt;
+		payload->context = context;
+
+		if (efct_hw_async_call(hw, efct_xport_post_node_event_cb,
+				       payload)) {
+			efc_log_test(efct, "efct_hw_async_call failed\n");
+			kfree(payload);
+			rc = -1;
+			break;
+		}
+
+		/* Wait for completion */
+		if (wait_for_completion_interruptible(&payload->done)) {
+			efc_log_test(efct,
+				      "POST_NODE_EVENT: completion failed\n");
+			rc = -1;
+		}
+		if (atomic_sub_and_test(1, &payload->refcnt))
+			kfree(payload);
+
+		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;
+}
+
+
+
+static void
+efct_xport_link_stats_cb(int status, u32 num_counters,
+			 struct efct_hw_link_stat_counts_s *counters, void *arg)
+{
+	union efct_xport_stats_u *result = arg;
+
+	result->stats.link_stats.link_failure_error_count =
+		counters[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter;
+	result->stats.link_stats.loss_of_sync_error_count =
+		counters[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter;
+	result->stats.link_stats.primitive_sequence_error_count =
+		counters[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter;
+	result->stats.link_stats.invalid_transmission_word_error_count =
+		counters[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter;
+	result->stats.link_stats.crc_error_count =
+		counters[EFCT_HW_LINK_STAT_CRC_COUNT].counter;
+
+	complete(&result->stats.done);
+}
+
+static void
+efct_xport_host_stats_cb(int status, u32 num_counters,
+			 struct efct_hw_host_stat_counts_s *counters, void *arg)
+{
+	union efct_xport_stats_u *result = arg;
+
+	result->stats.host_stats.transmit_kbyte_count =
+		counters[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter;
+	result->stats.host_stats.receive_kbyte_count =
+		counters[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter;
+	result->stats.host_stats.transmit_frame_count =
+		counters[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter;
+	result->stats.host_stats.receive_frame_count =
+		counters[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter;
+
+	complete(&result->stats.done);
+}
+
+static void
+efct_xport_async_link_stats_cb(int status, u32 num_counters,
+			       struct efct_hw_link_stat_counts_s *counters,
+			       void *arg)
+{
+	union efct_xport_stats_u *result = arg;
+
+	result->stats.link_stats.link_failure_error_count =
+		counters[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter;
+	result->stats.link_stats.loss_of_sync_error_count =
+		counters[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter;
+	result->stats.link_stats.primitive_sequence_error_count =
+		counters[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter;
+	result->stats.link_stats.invalid_transmission_word_error_count =
+		counters[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter;
+	result->stats.link_stats.crc_error_count =
+		counters[EFCT_HW_LINK_STAT_CRC_COUNT].counter;
+}
+
+static void
+efct_xport_async_host_stats_cb(int status, u32 num_counters,
+			       struct efct_hw_host_stat_counts_s *counters,
+			       void *arg)
+{
+	union efct_xport_stats_u *result = arg;
+
+	result->stats.host_stats.transmit_kbyte_count =
+		counters[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter;
+	result->stats.host_stats.receive_kbyte_count =
+		counters[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter;
+	result->stats.host_stats.transmit_frame_count =
+		counters[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter;
+	result->stats.host_stats.receive_frame_count =
+		counters[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter;
+}
+
+
+
+/**
+ * @brief Free a transport object.
+ *
+ * @par Description
+ * The transport object is freed.
+ *
+ * @param xport Pointer to transport object.
+ *
+ * @return None.
+ */
+
+void
+efct_xport_free(struct efct_xport_s *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);
+}
+
+/**
+ * @ingroup scsi_api_initiator
+ * @brief Remove host from transport.
+ *
+ * @par Description
+ * Function called by the SCSI mid-layer module to terminate any
+ * transport-related elements for a SCSI host.
+ *
+ * @return None.
+ */
+static void
+efct_xport_remove_host(struct Scsi_Host *shost)
+{
+	/*
+	 * Remove host from FC Transport layer
+	 *
+	 * 1. fc_remove_host()
+	 * a. for each vport: queue vport_delete_work (fc_vport_sched_delete())
+	 *	b. for each rport: queue rport_delete_work
+	 *		(fc_rport_final_delete())
+	 *	c. scsi_flush_work()
+	 * 2. fc_rport_final_delete()
+	 * a. fc_terminate_rport_io
+	 *		i. call LLDD's terminate_rport_io()
+	 *		ii. scsi_target_unblock()
+	 *	b. fc_starget_delete()
+	 *		i. fc_terminate_rport_io()
+	 *			1. call LLDD's terminate_rport_io()
+	 *			2. scsi_target_unblock()
+	 *		ii. scsi_remove_target()
+	 *      c. invoke LLDD devloss callback
+	 *      d. transport_remove_device(&rport->dev)
+	 *      e. device_del(&rport->dev)
+	 *      f. transport_destroy_device(&rport->dev)
+	 *      g. put_device(&shost->shost_gendev) (for fc_host->rport list)
+	 *      h. put_device(&rport->dev)
+	 */
+	fc_remove_host(shost);
+}
+
+int efct_scsi_del_device(struct efct_s *efct)
+{
+	if (efct->shost) {
+		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;
+	}
+	return 0;
+}
-- 
2.13.7


  parent reply	other threads:[~2019-10-23 21:56 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-23 21:55 [PATCH 00/32] [NEW] efct: Broadcom (Emulex) FC Target driver James Smart
2019-10-23 21:55 ` [PATCH 01/32] elx: libefc_sli: SLI-4 register offsets and field definitions James Smart
2019-10-24 16:22   ` Daniel Wagner
2019-10-25 23:04     ` James Smart
2019-10-23 21:55 ` [PATCH 02/32] elx: libefc_sli: SLI Descriptors and Queue entries James Smart
2019-10-25  9:59   ` Daniel Wagner
2019-10-25 23:00     ` James Smart
2019-10-23 21:55 ` [PATCH 03/32] elx: libefc_sli: Data structures and defines for mbox commands James Smart
2019-10-25 11:19   ` Daniel Wagner
2019-10-25 12:20     ` Steffen Maier
2019-10-25 22:10       ` James Smart
2019-10-25 22:42     ` James Smart
2019-10-23 21:55 ` [PATCH 04/32] elx: libefc_sli: queue create/destroy/parse routines James Smart
2019-10-25 15:35   ` Daniel Wagner
2019-10-25 22:24     ` James Smart
2019-10-23 21:55 ` [PATCH 05/32] elx: libefc_sli: Populate and post different WQEs James Smart
2019-10-23 21:55 ` [PATCH 06/32] elx: libefc_sli: bmbx routines and SLI config commands James Smart
2019-10-23 21:55 ` [PATCH 07/32] elx: libefc_sli: APIs to setup SLI library James Smart
2019-10-23 21:55 ` [PATCH 08/32] elx: libefc: Generic state machine framework James Smart
2019-10-23 21:55 ` [PATCH 09/32] elx: libefc: Emulex FC discovery library APIs and definitions James Smart
2019-10-23 21:55 ` [PATCH 10/32] elx: libefc: FC Domain state machine interfaces James Smart
2019-10-23 21:55 ` [PATCH 11/32] elx: libefc: SLI and FC PORT " James Smart
2019-10-23 21:55 ` [PATCH 12/32] elx: libefc: Remote node " James Smart
2019-10-23 21:55 ` [PATCH 13/32] elx: libefc: Fabric " James Smart
2019-10-23 21:55 ` [PATCH 14/32] elx: libefc: FC node ELS and state handling James Smart
2019-10-23 21:55 ` [PATCH 15/32] elx: efct: Data structures and defines for hw operations James Smart
2019-10-23 21:55 ` [PATCH 16/32] elx: efct: Driver initialization routines James Smart
2019-10-23 21:55 ` [PATCH 17/32] elx: efct: Hardware queues creation and deletion James Smart
2019-10-23 21:55 ` [PATCH 18/32] elx: efct: RQ buffer, memory pool allocation and deallocation APIs James Smart
2019-10-23 21:55 ` [PATCH 19/32] elx: efct: Hardware IO and SGL initialization James Smart
2019-10-23 21:55 ` [PATCH 20/32] elx: efct: Hardware queues processing James Smart
2019-10-23 21:55 ` [PATCH 21/32] elx: efct: Unsolicited FC frame processing routines James Smart
2019-10-23 21:55 ` [PATCH 22/32] elx: efct: Extended link Service IO handling James Smart
2019-10-23 21:55 ` [PATCH 23/32] elx: efct: SCSI IO handling routines James Smart
2019-10-23 21:55 ` [PATCH 24/32] elx: efct: LIO backend interface routines James Smart
2019-10-24 22:27   ` Bart Van Assche
2019-10-28 17:49     ` James Smart
2019-10-28 18:31       ` Bart Van Assche
2019-10-23 21:55 ` [PATCH 25/32] elx: efct: Hardware IO submission routines James Smart
2019-10-23 21:55 ` [PATCH 26/32] elx: efct: link statistics and SFP data James Smart
2019-10-23 21:55 ` James Smart [this message]
2019-10-23 21:55 ` [PATCH 28/32] elx: efct: IO timeout handling routines James Smart
2019-10-23 21:55 ` [PATCH 29/32] elx: efct: Firmware update, async link processing James Smart
2019-10-23 21:55 ` [PATCH 30/32] elx: efct: scsi_transport_fc host interface support James Smart
2019-10-23 21:55 ` [PATCH 31/32] elx: efct: Add Makefile and Kconfig for efct driver James Smart
2019-10-25 15:55   ` Daniel Wagner
2019-10-25 22:47     ` James Smart
2019-10-23 21:55 ` [PATCH 32/32] elx: efct: Tie into kernel Kconfig and build process James Smart
2019-10-26  0:34   ` kbuild test robot
2019-10-26  0:34     ` kbuild test robot
2019-10-26  0:39     ` Randy Dunlap
2019-10-26  0:39       ` Randy Dunlap
2019-10-26 14:13   ` kbuild test robot
2019-10-26 14:13     ` kbuild test robot
2019-10-26 14:13   ` [RFC PATCH] elx: efct: efct_libefc_templ can be static kbuild test robot
2019-10-26 14:13     ` kbuild test robot
2019-10-25 15:56 ` [PATCH 00/32] [NEW] efct: Broadcom (Emulex) FC Target driver Daniel Wagner
2019-10-25 22:31   ` James Smart

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191023215557.12581-28-jsmart2021@gmail.com \
    --to=jsmart2021@gmail.com \
    --cc=linux-scsi@vger.kernel.org \
    --cc=ram.vegesna@broadcom.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.