[PATCH 12/32] elx: libefc: Remote node state machine interfaces

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch continues the libefc library population.

This patch adds library interface definitions for:
- Remote node (aka remote port) allocation, initializaion and
  destroy routines.

Signed-off-by: Ram Vegesna <ram.vegesna@xxxxxxxxxxxx>
Signed-off-by: James Smart <jsmart2021@xxxxxxxxx>
---
 drivers/scsi/elx/libefc/efc_node.c | 1878 ++++++++++++++++++++++++++++++++++++
 drivers/scsi/elx/libefc/efc_node.h |  196 ++++
 2 files changed, 2074 insertions(+)
 create mode 100644 drivers/scsi/elx/libefc/efc_node.c
 create mode 100644 drivers/scsi/elx/libefc/efc_node.h

diff --git a/drivers/scsi/elx/libefc/efc_node.c b/drivers/scsi/elx/libefc/efc_node.c
new file mode 100644
index 000000000000..dcb63b1515b2
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_node.c
@@ -0,0 +1,1878 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * EFC driver remote node handler.  This file contains code that is shared
+ * between fabric (efc_fabric.c) and device (efc_device.c) nodes.
+ */
+
+#include "efc.h"
+#include "efc_device.h"
+
+#define SCSI_IOFMT "[%04x][i:%0*x t:%0*x h:%04x]"
+#define SCSI_ITT_SIZE(efc)	4
+
+#define SCSI_IOFMT_ARGS(io) \
+	(io->instance_index, SCSI_ITT_SIZE(io->efc), \
+	 io->init_task_tag, SCSI_ITT_SIZE(io->efc), \
+	 io->tgt_task_tag, io->hw_tag)
+
+#define scsi_io_printf(io, fmt, ...) \
+	efc_log_debug(io->efc, "[%s]" SCSI_IOFMT fmt, \
+	io->node->display_name, SCSI_IOFMT_ARGS(io), ##__VA_ARGS__)
+
+#define MAX_ACC_REJECT_PAYLOAD	sizeof(struct fc_els_ls_rjt)
+
+/**
+ * @ingroup node_common
+ * @brief Handle remote node events from HW
+ *
+ * Handle remote node events from HW.
+ * Essentially the HW event is translated into
+ * a node state machine event that is posted to the affected node.
+ *
+ * @param arg pointer to efc
+ * @param event HW event to proceoss
+ * @param data application specific data (pointer to the affected node)
+ *
+ * @return returns 0 for success, a negative error code value for failure.
+ */
+int
+efc_remote_node_cb(void *arg, int event,
+		   void *data)
+{
+	struct efc_lport *efc = arg;
+	enum efc_sm_event_e	sm_event = EFC_EVT_LAST;
+	struct efc_remote_node_s *rnode = data;
+	struct efc_node_s *node = rnode->node;
+	unsigned long flags = 0;
+
+	switch (event) {
+	case EFC_HW_NODE_ATTACH_OK:
+		sm_event = EFC_EVT_NODE_ATTACH_OK;
+		break;
+
+	case EFC_HW_NODE_ATTACH_FAIL:
+		sm_event = EFC_EVT_NODE_ATTACH_FAIL;
+		break;
+
+	case EFC_HW_NODE_FREE_OK:
+		sm_event = EFC_EVT_NODE_FREE_OK;
+		break;
+
+	case EFC_HW_NODE_FREE_FAIL:
+		sm_event = EFC_EVT_NODE_FREE_FAIL;
+		break;
+
+	default:
+		efc_log_test(efc, "unhandled event %#x\n", event);
+		return -1;
+	}
+
+	spin_lock_irqsave(&efc->lock, flags);
+	efc_node_post_event(node, sm_event, NULL);
+	spin_unlock_irqrestore(&efc->lock, flags);
+
+	return 0;
+}
+
+/**
+ * @ingroup node_alloc
+ * @brief Find an FC node structure given the FC port ID
+ *
+ * @param sport the SPORT to search
+ * @param port_id FC port ID
+ *
+ * @return pointer to the object or NULL if not found
+ */
+struct efc_node_s *
+efc_node_find(struct efc_sli_port_s *sport, u32 port_id)
+{
+	struct efc_node_s *node;
+
+	node = efc_spv_get(sport->lookup, port_id);
+	return node;
+}
+
+/**
+ * @ingroup node_alloc
+ * @brief allocate node object pool
+ *
+ * A pool of struct efc_node_s objects is allocated.
+ *
+ * @param efc pointer to driver instance context
+ * @param node_count count of nodes to allocate
+ *
+ * @return returns 0 for success, a negative error code value for failure.
+ */
+
+int
+efc_node_create_pool(struct efc_lport *efc, u32 node_count)
+{
+	u32 i;
+	struct efc_node_s *node;
+	u64 max_xfer_size;
+	struct efc_dma_s *dma;
+
+	efc->nodes_count = node_count;
+
+	efc->nodes = kmalloc_array(node_count, sizeof(struct efc_node_s *),
+				   GFP_ATOMIC);
+	if (!efc->nodes)
+		return -1;
+
+	memset(efc->nodes, 0, node_count * sizeof(struct efc_node_s *));
+
+	if (efc->max_xfer_size)
+		max_xfer_size = efc->max_xfer_size;
+	else
+		max_xfer_size = 65536;
+
+	INIT_LIST_HEAD(&efc->nodes_free_list);
+
+	for (i = 0; i < node_count; i++) {
+		dma = NULL;
+		node = kzalloc(sizeof(*node), GFP_ATOMIC);
+		if (!node) {
+			efc_log_err(efc, "node allocation failed");
+			goto error;
+		}
+		/* Assign any persistent field values */
+		node->instance_index = i;
+		node->max_wr_xfer_size = max_xfer_size;
+		node->rnode.indicator = U32_MAX;
+
+		dma = &node->sparm_dma_buf;
+		dma->size = 256;
+		dma->virt = dma_alloc_coherent(&efc->pcidev->dev, dma->size,
+					       &dma->phys, GFP_DMA);
+		if (!dma->virt) {
+			kfree(node);
+			efc_log_err(efc, "efc_dma_alloc failed");
+			goto error;
+		}
+
+		efc->nodes[i] = node;
+		INIT_LIST_HEAD(&node->list_entry);
+		list_add_tail(&node->list_entry, &efc->nodes_free_list);
+	}
+	return 0;
+
+error:
+	efc_node_free_pool(efc);
+	return -1;
+}
+
+/**
+ * @ingroup node_alloc
+ * @brief free node object pool
+ *
+ * The pool of previously allocated node objects is freed
+ *
+ * @param efc pointer to driver instance context
+ *
+ * @return none
+ */
+
+void
+efc_node_free_pool(struct efc_lport *efc)
+{
+	struct efc_node_s *node;
+	u32 i;
+	struct efc_dma_s *dma;
+
+	if (!efc->nodes)
+		return;
+
+	for (i = 0; i < efc->nodes_count; i++) {
+		node = efc->nodes[i];
+		if (node) {
+			/* free sparam_dma_buf */
+			dma = &node->sparm_dma_buf;
+			dma_free_coherent(&efc->pcidev->dev, dma->size,
+					  dma->virt, dma->phys);
+
+			kfree(node);
+		}
+		efc->nodes[i] = NULL;
+	}
+}
+
+/**
+ * @ingroup node_alloc
+ * @brief return pointer to node object given instance index
+ *
+ * A pointer to the node object given by an instance index is returned.
+ *
+ * @param efc pointer to driver instance context
+ * @param index instance index
+ *
+ * @return returns pointer to node object, or NULL
+ */
+
+struct efc_node_s *
+efc_node_get_instance(struct efc_lport *efc, u32 index)
+{
+	struct efc_node_s *node = NULL;
+
+	if (index >= efc->nodes_count) {
+		efc_log_test(efc, "invalid index: %d\n", index);
+		return NULL;
+	}
+	node = efc->nodes[index];
+	return node->attached ? node : NULL;
+}
+
+/**
+ * @ingroup node_alloc
+ * @brief Allocate an fc node structure and add to node list
+ *
+ * @param sport pointer to the SPORT from which this node is allocated
+ * @param port_id FC port ID of new node
+ * @param init Port is an inititiator (sent a plogi)
+ * @param targ Port is potentially a target
+ *
+ * @return pointer to the object or NULL if none available
+ */
+
+struct efc_node_s *efc_node_alloc(struct efc_sli_port_s *sport,
+				  u32 port_id, bool init, bool targ)
+{
+	int rc;
+	struct efc_node_s *node = NULL;
+	u32 instance_index;
+	u64 max_wr_xfer_size;
+	struct efc_lport *efc = sport->efc;
+	struct efc_dma_s sparm_dma_buf;
+
+	if (sport->shutting_down) {
+		efc_log_debug(efc, "node allocation when shutting down %06x",
+			      port_id);
+		return NULL;
+	}
+
+	if (!list_empty(&efc->nodes_free_list)) {
+		node = list_first_entry(&efc->nodes_free_list,
+					struct efc_node_s, list_entry);
+		list_del(&node->list_entry);
+	}
+
+	if (!node) {
+		efc_log_err(efc, "node allocation failed %06x", port_id);
+		return NULL;
+	}
+
+	/* Save persistent values across memset zero */
+	instance_index = node->instance_index;
+	max_wr_xfer_size = node->max_wr_xfer_size;
+	sparm_dma_buf = node->sparm_dma_buf;
+
+	memset(node, 0, sizeof(*node));
+	node->instance_index = instance_index;
+	node->max_wr_xfer_size = max_wr_xfer_size;
+	node->sparm_dma_buf = sparm_dma_buf;
+	node->rnode.indicator = U32_MAX;
+
+	node->sport = sport;
+	INIT_LIST_HEAD(&node->list_entry);
+	list_add_tail(&node->list_entry, &sport->node_list);
+
+	node->efc = efc;
+	node->init = init;
+	node->targ = targ;
+
+	spin_lock_init(&node->pend_frames_lock);
+	INIT_LIST_HEAD(&node->pend_frames);
+	spin_lock_init(&node->active_ios_lock);
+	INIT_LIST_HEAD(&node->active_ios);
+	INIT_LIST_HEAD(&node->els_io_pend_list);
+	INIT_LIST_HEAD(&node->els_io_active_list);
+	efc->tt.scsi_io_alloc_enable(efc, node);
+
+	rc = efc->tt.hw_node_alloc(efc, &node->rnode, port_id, sport);
+	if (rc) {
+		efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc);
+		return NULL;
+	}
+	/* zero the service parameters */
+	memset(node->sparm_dma_buf.virt, 0, node->sparm_dma_buf.size);
+
+	node->rnode.node = node;
+	node->sm.app = node;
+	node->evtdepth = 0;
+
+	efc_node_update_display_name(node);
+
+	efc_spv_set(sport->lookup, port_id, node);
+
+	return node;
+}
+
+/**
+ * @ingroup node_alloc
+ * @brief free a node structure
+ *
+ * The node structure given by 'node' is free'd
+ *
+ * @param node the node to free
+ *
+ * @return returns 0 for success, a negative error code value for failure.
+ */
+
+int
+efc_node_free(struct efc_node_s *node)
+{
+	struct efc_sli_port_s *sport;
+	struct efc_lport *efc;
+	int rc = 0;
+	struct efc_node_s *ns = NULL;
+	bool post_all_free = false;
+
+	sport = node->sport;
+	efc = node->efc;
+
+	node_printf(node, "Free'd\n");
+
+	if (node->refound) {
+		/*
+		 * Save the name server node. We will send fake RSCN event at
+		 * the end to handle ignored RSCN event during node deletion
+		 */
+		ns = efc_node_find(node->sport, FC_FID_DIR_SERV);
+	}
+
+	/* Remove from node list */
+	list_del(&node->list_entry);
+
+	/* Free HW resources */
+	rc = efc->tt.hw_node_free_resources(efc, &node->rnode);
+	if (EFC_HW_RTN_IS_ERROR(rc)) {
+		efc_log_test(efc, "efc_hw_node_free failed: %d\n", rc);
+		rc = -1;
+	}
+
+	/* if the gidpt_delay_timer is still running, then delete it */
+	if (timer_pending(&node->gidpt_delay_timer))
+		del_timer(&node->gidpt_delay_timer);
+
+	/* remove entry from sparse vector list */
+	if (!sport->lookup) {
+		efc_log_test(node->efc,
+			     "assertion failed: sport lookup is NULL\n");
+		return -1;
+	}
+
+	efc_spv_set(sport->lookup, node->rnode.fc_id, NULL);
+
+	/*
+	 * If the node_list is empty,
+	 * then post a ALL_CHILD_NODES_FREE event to the sport,
+	 * after the lock is released.
+	 * The sport may be free'd as a result of the event.
+	 */
+	if (list_empty(&sport->node_list))
+		post_all_free = true;
+
+	if (post_all_free) {
+		efc_sm_post_event(&sport->sm, EFC_EVT_ALL_CHILD_NODES_FREE,
+				  NULL);
+	}
+
+	node->sport = NULL;
+	node->sm.current_state = NULL;
+
+	/* return to free list */
+	INIT_LIST_HEAD(&node->list_entry);
+	list_add_tail(&node->list_entry, &efc->nodes_free_list);
+
+	if (ns) {
+		/* sending fake RSCN event to name server node */
+		efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL);
+	}
+
+	return rc;
+}
+
+/**
+ * @brief free memory resources of a node object
+ *
+ * The node object's child objects are freed after which the
+ * node object is freed.
+ *
+ * @param node pointer to a node object
+ *
+ * @return none
+ */
+
+void
+efc_node_force_free(struct efc_node_s *node)
+{
+	struct efc_lport *efc = node->efc;
+	/* shutdown sm processing */
+	efc_sm_disable(&node->sm);
+
+	strncpy(node->prev_state_name, node->current_state_name,
+		sizeof(node->prev_state_name));
+	strncpy(node->current_state_name, "disabled",
+		sizeof(node->current_state_name));
+
+	efc->tt.node_io_cleanup(efc, node, true);
+	efc->tt.node_els_cleanup(efc, node, true);
+
+	/* manually purge pending frames (if any) */
+	efc->tt.node_purge_pending(efc, node);
+
+	efc_node_free(node);
+}
+
+/**
+ * @ingroup node_common
+ * @brief Perform HW call to attach a remote node
+ *
+ * @param node pointer to node object
+ *
+ * @return 0 on success, non-zero otherwise
+ */
+int
+efc_node_attach(struct efc_node_s *node)
+{
+	int rc = 0;
+	struct efc_sli_port_s *sport = node->sport;
+	struct efc_domain_s *domain = sport->domain;
+	struct efc_lport *efc = node->efc;
+
+	if (!domain->attached) {
+		efc_log_test(efc,
+			     "Warning: unattached domain\n");
+		return -1;
+	}
+	/* Update node->wwpn/wwnn */
+
+	efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn),
+				efc_node_get_wwpn(node));
+	efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn),
+				efc_node_get_wwnn(node));
+
+	efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4,
+			sizeof(node->service_params) - 4);
+
+	/* take lock to protect node->rnode.attached */
+	rc = efc->tt.hw_node_attach(efc, &node->rnode, &node->sparm_dma_buf);
+	if (EFC_HW_RTN_IS_ERROR(rc))
+		efc_log_test(efc, "efc_hw_node_attach failed: %d\n", rc);
+
+	return rc;
+}
+
+/**
+ * @ingroup node_common
+ * @brief Generate text for a node's fc_id
+ *
+ * The text for a nodes fc_id is generated,
+ * either as a well known name, or a 6 digit
+ * hex value.
+ *
+ * @param fc_id fc_id
+ * @param buffer text buffer
+ * @param buffer_length text buffer length in bytes
+ *
+ * @return none
+ */
+
+void
+efc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length)
+{
+	switch (fc_id) {
+	case FC_FID_FLOGI:
+		snprintf(buffer, buffer_length, "fabric");
+		break;
+	case FC_FID_FCTRL:
+		snprintf(buffer, buffer_length, "fabctl");
+		break;
+	case FC_FID_DIR_SERV:
+		snprintf(buffer, buffer_length, "nserve");
+		break;
+	default:
+		if (fc_id == FC_FID_DOM_MGR) {
+			snprintf(buffer, buffer_length, "dctl%02x",
+				 (fc_id & 0x0000ff));
+		} else {
+			snprintf(buffer, buffer_length, "%06x", fc_id);
+		}
+		break;
+	}
+}
+
+/**
+ * @brief update the node's display name
+ *
+ * The node's display name is updated, sometimes needed because the sport part
+ * is updated after the node is allocated.
+ *
+ * @param node pointer to the node object
+ *
+ * @return none
+ */
+
+void
+efc_node_update_display_name(struct efc_node_s *node)
+{
+	u32 port_id = node->rnode.fc_id;
+	struct efc_sli_port_s *sport = node->sport;
+	char portid_display[16];
+
+	efc_node_fcid_display(port_id, portid_display, sizeof(portid_display));
+
+	snprintf(node->display_name, sizeof(node->display_name), "%s.%s",
+		 sport->display_name, portid_display);
+}
+
+/**
+ * @brief cleans up an XRI for the pending link services accept by aborting the
+ *         XRI if required.
+ *
+ * <h3 class="desc">Description</h3>
+ * This function is called when the LS accept is not sent.
+ *
+ * @param node Node for which should be cleaned up
+ */
+
+void
+efc_node_send_ls_io_cleanup(struct efc_node_s *node)
+{
+	struct efc_lport *efc = node->efc;
+
+	if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) {
+		efc_log_debug(efc, "[%s] cleaning up LS_ACC oxid=0x%x\n",
+			      node->display_name, node->ls_acc_oxid);
+
+		node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
+		node->ls_acc_io = NULL;
+	}
+}
+
+/**
+ * @ingroup node_common
+ * @brief state: shutdown a node
+ *
+ * A node is shutdown,
+ *
+ * @param ctx remote node sm context
+ * @param evt event to process
+ * @param arg per event optional argument
+ *
+ * @return returns NULL
+ *
+ * @note
+ */
+
+void *
+__efc_node_shutdown(struct efc_sm_ctx_s *ctx,
+		    enum efc_sm_event_e evt, void *arg)
+{
+	int rc;
+	unsigned long flags = 0;
+	struct efc_node_s *node = ctx->app;
+	struct efc_lport *efc = node->efc;
+
+	efc_node_evt_set(ctx, evt, __func__);
+
+	node_sm_trace();
+
+	switch (evt) {
+	case EFC_EVT_ENTER: {
+		efc_node_hold_frames(node);
+		efc_assert(efc_node_active_ios_empty(node), NULL);
+		efc_assert(efc_els_io_list_empty(node,
+						 &node->els_io_active_list),
+			   NULL);
+
+		/* by default, we will be freeing node after we unwind */
+		node->req_free = true;
+
+		switch (node->shutdown_reason) {
+		case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO:
+			/*
+			 * sm: if shutdown reason is
+			 * implicit logout / efc_node_attach
+			 */
+			/* Node shutdown b/c of PLOGI received when node
+			 * already logged in. We have PLOGI service
+			 * parameters, so submit node attach; we won't be
+			 * freeing this node
+			 */
+
+			/* currently, only case for implicit logo is PLOGI
+			 * recvd. Thus, node's ELS IO pending list won't be
+			 * empty (PLOGI will be on it)
+			 */
+			efc_assert(node->send_ls_acc ==
+				   EFC_NODE_SEND_LS_ACC_PLOGI, NULL);
+			node_printf(node,
+				    "Shutdown reason: implicit logout, re-authenticate\n");
+
+			efc->tt.scsi_io_alloc_enable(efc, node);
+
+			/* Re-attach node with the same HW node resources */
+			node->req_free = false;
+			rc = efc_node_attach(node);
+			efc_node_transition(node, __efc_d_wait_node_attach,
+					    NULL);
+			if (rc == EFC_HW_RTN_SUCCESS_SYNC) {
+				efc_node_post_event(node,
+						    EFC_EVT_NODE_ATTACH_OK,
+						    NULL);
+			}
+			break;
+		case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO: {
+			s8 pend_frames_empty;
+			struct list_head *list;
+
+			/* cleanup any pending LS_ACC ELSs */
+			efc_node_send_ls_io_cleanup(node);
+			list = &node->els_io_pend_list;
+			efc_assert(efc_els_io_list_empty(node, list), NULL);
+
+			spin_lock_irqsave(&node->pend_frames_lock, flags);
+			pend_frames_empty = list_empty(&node->pend_frames);
+			spin_unlock_irqrestore(&node->pend_frames_lock, flags);
+
+			/*
+			 * there are two scenarios where we want to keep
+			 * this node alive:
+			 * 1. there are pending frames that need to be
+			 *    processed or
+			 * 2. we're an initiator and the remote node is
+			 *    a target and we need to re-authenticate
+			 */
+			node_printf(node,
+				    "Shutdown: explicit logo pend=%d ",
+					!pend_frames_empty);
+			 node_printf(node,
+				     "sport.ini=%d node.tgt=%d\n",
+				    node->sport->enable_ini, node->targ);
+
+			if (!pend_frames_empty ||
+			    (node->sport->enable_ini && node->targ)) {
+				u8 send_plogi = false;
+
+				if (node->sport->enable_ini && node->targ) {
+					/*
+					 * we're an initiator and
+					 * node shutting down is a target;
+					 * we'll need to re-authenticate in
+					 * initial state
+					 */
+					send_plogi = true;
+				}
+
+				/*
+				 * transition to __efc_d_init
+				 * (will retain HW node resources)
+				 */
+				efc->tt.scsi_io_alloc_enable(efc, node);
+				node->req_free = false;
+
+				/*
+				 * either pending frames exist,
+				 * or we're re-authenticating with PLOGI
+				 * (or both); in either case,
+				 * return to initial state
+				 */
+				efc_node_init_device(node, send_plogi);
+			}
+			/* else: let node shutdown occur */
+			break;
+		}
+		case EFC_NODE_SHUTDOWN_DEFAULT:
+		default: {
+			struct list_head *list;
+
+			/*
+			 * shutdown due to link down,
+			 * node going away (xport event) or
+			 * sport shutdown, purge pending and
+			 * proceed to cleanup node
+			 */
+
+			/* cleanup any pending LS_ACC ELSs */
+			efc_node_send_ls_io_cleanup(node);
+			list = &node->els_io_pend_list;
+			efc_assert(efc_els_io_list_empty(node, list), NULL);
+
+			node_printf(node,
+				    "Shutdown reason: default, purge pending\n");
+			efc->tt.node_purge_pending(efc, node);
+			break;
+		}
+		}
+
+		break;
+	}
+	case EFC_EVT_EXIT:
+		efc_node_accept_frames(node);
+		break;
+
+	default:
+		__efc_node_common(__func__, ctx, evt, arg);
+		return NULL;
+	}
+
+	return NULL;
+}
+
+/**
+ * @ingroup common_node
+ * @brief Checks to see if ELS's have been quiesced
+ *
+ * Check if ELS's have been quiesced. If so, transition to the
+ * next state in the shutdown process.
+ *
+ * @param node Node for which ELS's are checked
+ *
+ * @return Returns 1 if ELS's have been quiesced, 0 otherwise.
+ */
+static int
+efc_node_check_els_quiesced(struct efc_node_s *node)
+{
+	/* check to see if ELS requests, completions are quiesced */
+	if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 &&
+	    efc_els_io_list_empty(node, &node->els_io_active_list)) {
+		if (!node->attached) {
+			/* hw node detach already completed, proceed */
+			node_printf(node, "HW node not attached\n");
+			efc_node_transition(node,
+					    __efc_node_wait_ios_shutdown,
+					     NULL);
+		} else {
+			/*
+			 * hw node detach hasn't completed,
+			 * transition and wait
+			 */
+			node_printf(node, "HW node still attached\n");
+			efc_node_transition(node, __efc_node_wait_node_free,
+					    NULL);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * @ingroup common_node
+ * @brief Initiate node IO cleanup.
+ *
+ * Note: this function must be called with a non-attached node
+ * or a node for which the node detach (efc_hw_node_detach())
+ * has already been initiated.
+ *
+ * @param node Node for which shutdown is initiated
+ *
+ * @return Returns None.
+ */
+
+void
+efc_node_initiate_cleanup(struct efc_node_s *node)
+{
+	struct efc_lport *efc;
+
+	efc = node->efc;
+	efc->tt.node_els_cleanup(efc, node, false);
+
+	/*
+	 * if ELS's have already been quiesced, will move to next state
+	 * if ELS's have not been quiesced, abort them
+	 */
+	if (efc_node_check_els_quiesced(node) == 0) {
+		/*
+		 * Abort all ELS's since ELS's won't be aborted by HW
+		 * node free.
+		 */
+		efc_node_hold_frames(node);
+		efc->tt.node_abort_all_els(efc, node);
+		efc_node_transition(node, __efc_node_wait_els_shutdown, NULL);
+	}
+}
+
+/**
+ * @ingroup node_common
+ * @brief Node state machine: Wait for all ELSs to complete.
+ *
+ * <h3 class="desc">Description</h3>
+ * State waits for all ELSs to complete after aborting all
+ * outstanding .
+ *
+ * @param ctx Remote node state machine context.
+ * @param evt Event to process.
+ * @param arg Per event optional argument.
+ *
+ * @return Returns NULL.
+ */
+
+void *
+__efc_node_wait_els_shutdown(struct efc_sm_ctx_s *ctx,
+			     enum efc_sm_event_e evt, void *arg)
+{
+	bool check_quiesce = false;
+	struct efc_node_s *node = ctx->app;
+
+	efc_node_evt_set(ctx, evt, __func__);
+
+	node_sm_trace();
+
+	switch (evt) {
+	case EFC_EVT_ENTER:
+		efc_node_hold_frames(node);
+		if (efc_els_io_list_empty(node, &node->els_io_active_list)) {
+			node_printf(node, "All ELS IOs complete\n");
+			check_quiesce = true;
+		}
+		break;
+	case EFC_EVT_EXIT:
+		efc_node_accept_frames(node);
+		break;
+
+	case EFC_EVT_SRRS_ELS_REQ_OK:
+	case EFC_EVT_SRRS_ELS_REQ_FAIL:
+	case EFC_EVT_SRRS_ELS_REQ_RJT:
+	case EFC_EVT_ELS_REQ_ABORTED:
+		efc_assert(node->els_req_cnt, NULL);
+		node->els_req_cnt--;
+		check_quiesce = true;
+		break;
+
+	case EFC_EVT_SRRS_ELS_CMPL_OK:
+	case EFC_EVT_SRRS_ELS_CMPL_FAIL:
+		efc_assert(node->els_cmpl_cnt, NULL);
+		node->els_cmpl_cnt--;
+		check_quiesce = true;
+		break;
+
+	case EFC_EVT_ALL_CHILD_NODES_FREE:
+		/* all ELS IO's complete */
+		node_printf(node, "All ELS IOs complete\n");
+		efc_assert(efc_els_io_list_empty(node,
+						 &node->els_io_active_list),
+			   NULL);
+		check_quiesce = true;
+		break;
+
+	case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+		check_quiesce = true;
+		break;
+
+	case EFC_EVT_DOMAIN_ATTACH_OK:
+		/* don't care about domain_attach_ok */
+		break;
+
+	/* ignore shutdown events as we're already in shutdown path */
+	case EFC_EVT_SHUTDOWN:
+		/* have default shutdown event take precedence */
+		node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+		/* fall through */
+	case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+	case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+		node_printf(node, "%s received\n", efc_sm_event_name(evt));
+		break;
+
+	default:
+		__efc_node_common(__func__, ctx, evt, arg);
+		return NULL;
+	}
+
+	if (check_quiesce)
+		efc_node_check_els_quiesced(node);
+
+	return NULL;
+}
+
+/**
+ * @ingroup node_command
+ * @brief Node state machine: Wait for a HW node free event to
+ * complete.
+ *
+ * <h3 class="desc">Description</h3>
+ * State waits for the node free event to be received from the HW.
+ *
+ * @param ctx Remote node state machine context.
+ * @param evt Event to process.
+ * @param arg Per event optional argument.
+ *
+ * @return Returns NULL.
+ */
+
+void *
+__efc_node_wait_node_free(struct efc_sm_ctx_s *ctx,
+			  enum efc_sm_event_e evt, void *arg)
+{
+	struct efc_node_s *node = ctx->app;
+
+	efc_node_evt_set(ctx, evt, __func__);
+
+	node_sm_trace();
+
+	switch (evt) {
+	case EFC_EVT_ENTER:
+		efc_node_hold_frames(node);
+		break;
+
+	case EFC_EVT_EXIT:
+		efc_node_accept_frames(node);
+		break;
+
+	case EFC_EVT_NODE_FREE_OK:
+		/* node is officially no longer attached */
+		node->attached = false;
+		efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL);
+		break;
+
+	case EFC_EVT_ALL_CHILD_NODES_FREE:
+	case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+		/* As IOs and ELS IO's complete we expect to get these events */
+		break;
+
+	case EFC_EVT_DOMAIN_ATTACH_OK:
+		/* don't care about domain_attach_ok */
+		break;
+
+	/* ignore shutdown events as we're already in shutdown path */
+	case EFC_EVT_SHUTDOWN:
+		/* have default shutdown event take precedence */
+		node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+		/* Fall through */
+	case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+	case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+		node_printf(node, "%s received\n", efc_sm_event_name(evt));
+		break;
+	default:
+		__efc_node_common(__func__, ctx, evt, arg);
+		return NULL;
+	}
+
+	return NULL;
+}
+
+/**
+ * @ingroup node_common
+ * @brief state: initiate node shutdown
+ *
+ * State is entered when a node receives a shutdown event, and it's waiting
+ * for all the active IOs and ELS IOs associated with the node to complete.
+ *
+ * @param ctx remote node sm context
+ * @param evt event to process
+ * @param arg per event optional argument
+ *
+ * @return returns NULL
+ */
+
+void *
+__efc_node_wait_ios_shutdown(struct efc_sm_ctx_s *ctx,
+			     enum efc_sm_event_e evt, void *arg)
+{
+	struct efc_node_s *node = ctx->app;
+	struct efc_lport *efc = node->efc;
+
+	efc_node_evt_set(ctx, evt, __func__);
+
+	node_sm_trace();
+
+	switch (evt) {
+	case EFC_EVT_ENTER:
+		efc_node_hold_frames(node);
+
+		/* first check to see if no ELS IOs are outstanding */
+		if (efc_els_io_list_empty(node, &node->els_io_active_list)) {
+			/* If there are any active IOS, Free them. */
+			efc_node_transition(node, __efc_node_shutdown, NULL);
+		}
+		break;
+
+	case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+	case EFC_EVT_ALL_CHILD_NODES_FREE: {
+		if (efc_node_active_ios_empty(node) &&
+		    efc_els_io_list_empty(node, &node->els_io_active_list)) {
+			efc_node_transition(node, __efc_node_shutdown, NULL);
+		}
+		break;
+	}
+
+	case EFC_EVT_EXIT:
+		efc_node_accept_frames(node);
+		break;
+
+	case EFC_EVT_SRRS_ELS_REQ_FAIL:
+		/* Can happen as ELS IO IO's complete */
+		efc_assert(node->els_req_cnt, NULL);
+		node->els_req_cnt--;
+		break;
+
+	/* ignore shutdown events as we're already in shutdown path */
+	case EFC_EVT_SHUTDOWN:
+		/* have default shutdown event take precedence */
+		node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+		/* fall through */
+	case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+	case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+		efc_log_debug(efc, "[%s] %-20s\n", node->display_name,
+			      efc_sm_event_name(evt));
+		break;
+	case EFC_EVT_DOMAIN_ATTACH_OK:
+		/* don't care about domain_attach_ok */
+		break;
+	default:
+		__efc_node_common(__func__, ctx, evt, arg);
+		return NULL;
+	}
+
+	return NULL;
+}
+
+/**
+ * @ingroup node_common
+ * @brief state: common node event handler
+ *
+ * Handle common/shared node events
+ *
+ * @param funcname calling function's name
+ * @param ctx remote node sm context
+ * @param evt event to process
+ * @param arg per event optional argument
+ *
+ * @return returns NULL
+ */
+
+void *
+__efc_node_common(const char *funcname, struct efc_sm_ctx_s *ctx,
+		  enum efc_sm_event_e evt, void *arg)
+{
+	struct efc_node_s *node = NULL;
+	struct efc_lport *efc = NULL;
+	struct efc_node_cb_s *cbdata = arg;
+
+	efc_assert(ctx, NULL);
+	efc_assert(ctx->app, NULL);
+	node = ctx->app;
+	efc_assert(node->efc, NULL);
+	efc = node->efc;
+
+	switch (evt) {
+	case EFC_EVT_ENTER:
+	case EFC_EVT_REENTER:
+	case EFC_EVT_EXIT:
+	case EFC_EVT_SPORT_TOPOLOGY_NOTIFY:
+	case EFC_EVT_NODE_MISSING:
+	case EFC_EVT_FCP_CMD_RCVD:
+		break;
+
+	case EFC_EVT_NODE_REFOUND:
+		node->refound = true;
+		break;
+
+	/*
+	 * node->attached must be set appropriately
+	 * for all node attach/detach events
+	 */
+	case EFC_EVT_NODE_ATTACH_OK:
+		node->attached = true;
+		break;
+
+	case EFC_EVT_NODE_FREE_OK:
+	case EFC_EVT_NODE_ATTACH_FAIL:
+		node->attached = false;
+		break;
+
+	/*
+	 * handle any ELS completions that
+	 * other states either didn't care about
+	 * or forgot about
+	 */
+	case EFC_EVT_SRRS_ELS_CMPL_OK:
+	case EFC_EVT_SRRS_ELS_CMPL_FAIL:
+		efc_assert(node->els_cmpl_cnt, NULL);
+		node->els_cmpl_cnt--;
+		break;
+
+	/*
+	 * handle any ELS request completions that
+	 * other states either didn't care about
+	 * or forgot about
+	 */
+	case EFC_EVT_SRRS_ELS_REQ_OK:
+	case EFC_EVT_SRRS_ELS_REQ_FAIL:
+	case EFC_EVT_SRRS_ELS_REQ_RJT:
+	case EFC_EVT_ELS_REQ_ABORTED:
+		efc_assert(node->els_req_cnt, NULL);
+		node->els_req_cnt--;
+		break;
+
+	case EFC_EVT_ELS_RCVD: {
+		struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+		/*
+		 * Unsupported ELS was received,
+		 * send LS_RJT, command not supported
+		 */
+		efc_log_debug(efc,
+			      "[%s] (%s) ELS x%02x, LS_RJT not supported\n",
+			      node->display_name, funcname,
+			      ((uint8_t *)cbdata->payload->dma.virt)[0]);
+
+		efc->tt.send_ls_rjt(efc, node, be16_to_cpu(hdr->fh_ox_id),
+					ELS_RJT_UNSUP, ELS_EXPL_NONE, 0);
+		break;
+	}
+
+	case EFC_EVT_PLOGI_RCVD:
+	case EFC_EVT_FLOGI_RCVD:
+	case EFC_EVT_LOGO_RCVD:
+	case EFC_EVT_PRLI_RCVD:
+	case EFC_EVT_PRLO_RCVD:
+	case EFC_EVT_PDISC_RCVD:
+	case EFC_EVT_FDISC_RCVD:
+	case EFC_EVT_ADISC_RCVD:
+	case EFC_EVT_RSCN_RCVD:
+	case EFC_EVT_SCR_RCVD: {
+		struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+		/* sm: / send ELS_RJT */
+		efc_log_debug(efc, "[%s] (%s) %s sending ELS_RJT\n",
+			      node->display_name, funcname,
+			      efc_sm_event_name(evt));
+		/* if we didn't catch this in a state, send generic LS_RJT */
+		efc->tt.send_ls_rjt(efc, node, be16_to_cpu(hdr->fh_ox_id),
+						ELS_RJT_UNAB, ELS_EXPL_NONE, 0);
+
+		break;
+	}
+	case EFC_EVT_GID_PT_RCVD:
+	case EFC_EVT_RFT_ID_RCVD:
+	case EFC_EVT_RFF_ID_RCVD: {
+		struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+		efc_log_debug(efc, "[%s] (%s) %s sending CT_REJECT\n",
+			      node->display_name, funcname,
+			      efc_sm_event_name(evt));
+		efc->tt.send_ct_rsp(efc, node, hdr->fh_ox_id,
+				cbdata->payload->dma.virt,
+				FC_FS_RJT, FC_FS_RJT_UNSUP, 0);
+		break;
+	}
+
+	case EFC_EVT_ABTS_RCVD: {
+		efc_log_debug(efc, "[%s] (%s) %s sending BA_ACC\n",
+			      node->display_name, funcname,
+			      efc_sm_event_name(evt));
+
+		/* sm: / send BA_ACC */
+		efc->tt.bls_send_acc_hdr(efc, node, cbdata->header->dma.virt);
+		break;
+	}
+
+	default:
+		efc_log_test(node->efc, "[%s] %-20s %-20s not handled\n",
+			     node->display_name, funcname,
+			     efc_sm_event_name(evt));
+		break;
+	}
+	return NULL;
+}
+
+/**
+ * @ingroup node_common
+ * @brief save node service parameters
+ *
+ * Service parameters are copyed into the node structure
+ *
+ * @param node pointer to node structure
+ * @param payload pointer to service parameters to save
+ *
+ * @return none
+ */
+
+void
+efc_node_save_sparms(struct efc_node_s *node, void *payload)
+{
+	memcpy(node->service_params, payload, sizeof(node->service_params));
+}
+
+/**
+ * @ingroup node_common
+ * @brief Post event to node state machine context
+ *
+ * This is used by the node state machine code to post events to the nodes.
+ * Upon completion of the event posting, if the nesting depth is zero and
+ * we're not holding inbound frames, then the pending frames are processed.
+ *
+ * @param node pointer to node
+ * @param evt event to post
+ * @param arg event posting argument
+ *
+ * @return none
+ */
+
+void
+efc_node_post_event(struct efc_node_s *node,
+		    enum efc_sm_event_e evt, void *arg)
+{
+	bool free_node = false;
+
+	node->evtdepth++;
+
+	efc_sm_post_event(&node->sm, evt, arg);
+
+	/* If our event call depth is one and
+	 * we're not holding frames
+	 * then we can dispatch any pending frames.
+	 * We don't want to allow the efc_process_node_pending()
+	 * call to recurse.
+	 */
+	if (!node->hold_frames && node->evtdepth == 1)
+		efc_process_node_pending(node);
+
+	node->evtdepth--;
+
+	/*
+	 * Free the node object if so requested,
+	 * and we're at an event call depth of zero
+	 */
+	if (node->evtdepth == 0 && node->req_free)
+		free_node = true;
+
+	if (free_node)
+		efc_node_free(node);
+}
+
+/**
+ * @ingroup node_common
+ * @brief transition state of a node
+ *
+ * The node's state is transitioned to the requested state.
+ * Entry/Exit events are posted as needed.
+ *
+ * @param node pointer to node
+ * @param state state to transition to
+ * @param data transition data
+ *
+ * @return none
+ */
+
+void
+efc_node_transition(struct efc_node_s *node,
+		    void *(*state)(struct efc_sm_ctx_s *,
+				   enum efc_sm_event_e, void *), void *data)
+
+{
+	struct efc_sm_ctx_s *ctx = &node->sm;
+
+	if (ctx->current_state == state) {
+		efc_node_post_event(node, EFC_EVT_REENTER, data);
+	} else {
+		efc_node_post_event(node, EFC_EVT_EXIT, data);
+		ctx->current_state = state;
+		efc_node_post_event(node, EFC_EVT_ENTER, data);
+	}
+}
+
+/**
+ * @ingroup node_common
+ * @brief build EUI formatted WWN
+ *
+ * Build a WWN given the somewhat transport agnostic
+ * iScsi naming specification, for FC
+ * use the eui. format, an ascii string such as: "eui.10000000C9A19501"
+ *
+ * @param buffer buffer to place formatted name into
+ * @param buffer_len length in bytes of the buffer
+ * @param eui_name cpu endian 64 bit WWN value
+ *
+ * @return none
+ */
+
+void
+efc_node_build_eui_name(char *buffer, u32 buffer_len, uint64_t eui_name)
+{
+	memset(buffer, 0, buffer_len);
+
+	snprintf(buffer, buffer_len, "eui.%016llX", eui_name);
+}
+
+/**
+ * @ingroup node_common
+ * @brief return nodes' WWPN as a uint64_t
+ *
+ * The WWPN is computed from service parameters and returned as a uint64_t
+ *
+ * @param node pointer to node structure
+ *
+ * @return WWPN
+ *
+ */
+
+uint64_t
+efc_node_get_wwpn(struct efc_node_s *node)
+{
+	struct fc_els_flogi *sp =
+			(struct fc_els_flogi *)node->service_params;
+
+	return be64_to_cpu(sp->fl_wwpn);
+}
+
+/**
+ * @ingroup node_common
+ * @brief return nodes' WWNN as a uint64_t
+ *
+ * The WWNN is computed from service parameters and returned as a uint64_t
+ *
+ * @param node pointer to node structure
+ *
+ * @return WWNN
+ *
+ */
+
+uint64_t
+efc_node_get_wwnn(struct efc_node_s *node)
+{
+	struct fc_els_flogi *sp =
+			(struct fc_els_flogi *)node->service_params;
+
+	return be64_to_cpu(sp->fl_wwnn);
+}
+
+/**
+ * @brief check ELS request completion
+ *
+ * Check ELS request completion event to make sure it's for the
+ * ELS request we expect. If not, invoke given common event
+ * handler and return an error.
+ *
+ * @param ctx state machine context
+ * @param evt ELS request event
+ * @param arg event argument
+ * @param cmd ELS command expected
+ * @param node_common_func common event handler to call if ELS
+ *	doesn't match
+ * @param funcname function name that called this
+ *
+ * @return zero if ELS command matches, -1 otherwise
+ */
+int
+efc_node_check_els_req(struct efc_sm_ctx_s *ctx, enum efc_sm_event_e evt,
+		       void *arg, uint8_t cmd,
+			void *(*efc_node_common_func)(const char *,
+						      struct efc_sm_ctx_s *,
+			       enum efc_sm_event_e, void *),
+			const char *funcname)
+{
+	return 0;
+}
+
+/**
+ * @brief check NS request completion
+ *
+ * Check ELS request completion event to make sure it's for the
+ * nameserver request we expect. If not, invoke given common
+ * event handler and return an error.
+ *
+ * @param ctx state machine context
+ * @param evt ELS request event
+ * @param arg event argument
+ * @param cmd nameserver command expected
+ * @param node_common_func common event handler to call if
+ *                         nameserver cmd doesn't match
+ * @param funcname function name that called this
+ *
+ * @return zero if NS command matches, -1 otherwise
+ */
+int
+efc_node_check_ns_req(struct efc_sm_ctx_s *ctx, enum efc_sm_event_e evt,
+		      void *arg, uint16_t cmd,
+		       void *(*efc_node_common_func)(const char *,
+						     struct efc_sm_ctx_s *,
+			      enum efc_sm_event_e, void *),
+		       const char *funcname)
+{
+	return 0;
+}
+
+/**
+ * @brief Return TRUE if active ios list is empty
+ *
+ * Test if node->active_ios list is empty while
+ * holding the node->active_ios_lock.
+ *
+ * @param node pointer to node object
+ *
+ * @return TRUE if node active ios list is empty
+ */
+
+int
+efc_node_active_ios_empty(struct efc_node_s *node)
+{
+	int empty;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&node->active_ios_lock, flags);
+	empty = list_empty(&node->active_ios);
+	spin_unlock_irqrestore(&node->active_ios_lock, flags);
+	return empty;
+}
+
+int
+efc_els_io_list_empty(struct efc_node_s *node, struct list_head *list)
+{
+	int empty;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&node->active_ios_lock, flags);
+	empty = list_empty(list);
+	spin_unlock_irqrestore(&node->active_ios_lock, flags);
+	return empty;
+}
+
+/**
+ * @brief Pause a node
+ *
+ * The node is placed in the __efc_node_paused state after saving the state
+ * to return to
+ *
+ * @param node Pointer to node object
+ * @param state State to resume to
+ *
+ * @return none
+ */
+
+void
+efc_node_pause(struct efc_node_s *node,
+	       void *(*state)(struct efc_sm_ctx_s *,
+			      enum efc_sm_event_e, void *))
+
+{
+	node->nodedb_state = state;
+	efc_node_transition(node, __efc_node_paused, NULL);
+}
+
+/**
+ * @brief Paused node state
+ *
+ * This state is entered when a state is "paused". When resumed, the node
+ * is transitioned to a previously saved state (node->ndoedb_state)
+ *
+ * @param ctx Remote node state machine context.
+ * @param evt Event to process.
+ * @param arg Per event optional argument.
+ *
+ * @return returns NULL
+ */
+
+void *
+__efc_node_paused(struct efc_sm_ctx_s *ctx,
+		  enum efc_sm_event_e evt, void *arg)
+{
+	struct efc_node_s *node = ctx->app;
+
+	efc_node_evt_set(ctx, evt, __func__);
+
+	node_sm_trace();
+
+	switch (evt) {
+	case EFC_EVT_ENTER:
+		node_printf(node, "Paused\n");
+		break;
+
+	case EFC_EVT_RESUME: {
+		void *(*pf)(struct efc_sm_ctx_s *ctx,
+			    enum efc_sm_event_e evt, void *arg);
+
+		pf = node->nodedb_state;
+
+		node->nodedb_state = NULL;
+		efc_node_transition(node, pf, NULL);
+		break;
+	}
+
+	case EFC_EVT_DOMAIN_ATTACH_OK:
+		break;
+
+	case EFC_EVT_SHUTDOWN:
+		node->req_free = true;
+		break;
+
+	default:
+		__efc_node_common(__func__, ctx, evt, arg);
+		break;
+	}
+	return NULL;
+}
+
+/**
+ * @brief Resume a paused state
+ *
+ * Posts a resume event to the paused node.
+ *
+ * @param node Pointer to node object
+ *
+ * @return returns 0 for success, a negative error code value for failure.
+ */
+
+int
+efc_node_resume(struct efc_node_s *node)
+{
+	efc_node_post_event(node, EFC_EVT_RESUME, NULL);
+
+	return 0;
+}
+
+/**
+ * @ingroup node_common
+ * @brief Dispatch a ELS frame.
+ *
+ * <h3 class="desc">Description</h3>
+ * An ELS frame is dispatched to the \c node state machine.
+ * RQ Pair mode: this function is always called with a NULL hw
+ * io.
+ *
+ * @param node Node that originated the frame.
+ * @param seq header/payload sequence buffers
+ *
+ * @return Returns 0 if frame processed and RX buffers cleaned
+ * up appropriately, -1 if frame not handled and RX buffers need
+ * to be returned.
+ */
+
+int
+efc_node_recv_els_frame(struct efc_node_s *node,
+			struct efc_hw_sequence_s *seq)
+{
+	unsigned long flags = 0;
+	u32 prli_size = sizeof(struct fc_els_prli) + sizeof(struct fc_els_spp);
+	struct {
+		u32 cmd;
+		enum efc_sm_event_e evt;
+		u32 payload_size;
+	} els_cmd_list[] = {
+		{ELS_PLOGI, EFC_EVT_PLOGI_RCVD,	sizeof(struct fc_els_flogi)},
+		{ELS_FLOGI, EFC_EVT_FLOGI_RCVD,	sizeof(struct fc_els_flogi)},
+		{ELS_LOGO, EFC_EVT_LOGO_RCVD, sizeof(struct fc_els_ls_acc)},
+		{ELS_PRLI, EFC_EVT_PRLI_RCVD, prli_size},
+		{ELS_PRLO, EFC_EVT_PRLO_RCVD, prli_size},
+		{ELS_PDISC, EFC_EVT_PDISC_RCVD,	MAX_ACC_REJECT_PAYLOAD},
+		{ELS_FDISC, EFC_EVT_FDISC_RCVD,	MAX_ACC_REJECT_PAYLOAD},
+		{ELS_ADISC, EFC_EVT_ADISC_RCVD,	sizeof(struct fc_els_adisc)},
+		{ELS_RSCN, EFC_EVT_RSCN_RCVD, MAX_ACC_REJECT_PAYLOAD},
+		{ELS_SCR, EFC_EVT_SCR_RCVD, MAX_ACC_REJECT_PAYLOAD},
+	};
+	struct efc_node_cb_s cbdata;
+	u8 *buf = seq->payload->dma.virt;
+	enum efc_sm_event_e evt = EFC_EVT_ELS_RCVD;
+	u32 i;
+
+	memset(&cbdata, 0, sizeof(cbdata));
+	cbdata.header = seq->header;
+	cbdata.payload = seq->payload;
+
+	/* find a matching event for the ELS command */
+	for (i = 0; i < ARRAY_SIZE(els_cmd_list); i++) {
+		if (els_cmd_list[i].cmd == buf[0]) {
+			evt = els_cmd_list[i].evt;
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&node->efc->lock, flags);
+	efc_node_post_event(node, evt, &cbdata);
+	spin_unlock_irqrestore(&node->efc->lock, flags);
+
+	return 0;
+}
+
+/**
+ * @ingroup node_common
+ * @brief Dispatch a CT frame.
+ *
+ * <h3 class="desc">Description</h3>
+ * A CT frame is dispatched to the \c node state machine.
+ * RQ Pair mode: this function is always called with a NULL hw
+ * io.
+ *
+ * @param node Node that originated the frame.
+ * @param seq header/payload sequence buffers
+ *
+ * @return Returns 0 if frame processed and RX buffers cleaned
+ * up appropriately, -1 if frame not handled and RX buffers need
+ * to be returned.
+ */
+
+int
+efc_node_recv_ct_frame(struct efc_node_s *node,
+		       struct efc_hw_sequence_s *seq)
+{
+	struct fc_ct_hdr  *iu = seq->payload->dma.virt;
+	enum efc_sm_event_e evt = EFC_EVT_ELS_RCVD;
+	u16 gscmd = be16_to_cpu(iu->ct_cmd);
+	struct efc_node_cb_s cbdata;
+	unsigned long flags = 0;
+	u32 i;
+	struct {
+		u32 cmd;
+		enum efc_sm_event_e evt;
+		u32 payload_size;
+	} ct_cmd_list[] = {
+		{FC_NS_RFF_ID, EFC_EVT_RFF_ID_RCVD, 100},
+		{FC_NS_RFT_ID, EFC_EVT_RFT_ID_RCVD, 100},
+		{FC_NS_GNN_ID, EFC_EVT_GNN_ID_RCVD, 100},
+		{FC_NS_GPN_ID, EFC_EVT_GPN_ID_RCVD, 100},
+		{FC_NS_GID_PT, EFC_EVT_GID_PT_RCVD, 256},
+		{FC_NS_RPN_ID, EFC_EVT_RPN_ID_RCVD, 100},
+		{FC_NS_RNN_ID, EFC_EVT_RNN_ID_RCVD, 100},
+		{FC_NS_RSNN_NN, EFC_EVT_RSNN_NN_RCVD, 100},
+		{FC_NS_RSPN_ID, EFC_EVT_RSPN_ID_RCVD, 100},
+	};
+
+	memset(&cbdata, 0, sizeof(cbdata));
+	cbdata.header = seq->header;
+	cbdata.payload = seq->payload;
+
+	/* find a matching event for the ELS/GS command */
+	for (i = 0; i < ARRAY_SIZE(ct_cmd_list); i++) {
+		if (ct_cmd_list[i].cmd == gscmd) {
+			evt = ct_cmd_list[i].evt;
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&node->efc->lock, flags);
+	efc_node_post_event(node, evt, &cbdata);
+	spin_unlock_irqrestore(&node->efc->lock, flags);
+
+	return 0;
+}
+
+/*
+ *@ingroup node_common
+ * @brief Dispatch a FCP command frame when the node is not ready.
+ *
+ * <h3 class="desc">Description</h3>
+ * A frame is dispatched to the \c node state machine.
+ *
+ * @param node Node that originated the frame.
+ * @param seq header/payload sequence buffers
+ *
+ * @return Returns 0 if frame processed and RX buffers cleaned
+ * up appropriately, -1 if frame not handled.
+ */
+
+int
+efc_node_recv_fcp_cmd(struct efc_node_s *node, struct efc_hw_sequence_s *seq)
+{
+	struct efc_node_cb_s cbdata;
+	unsigned long flags = 0;
+
+	memset(&cbdata, 0, sizeof(cbdata));
+	cbdata.header = seq->header;
+	cbdata.payload = seq->payload;
+
+	spin_lock_irqsave(&node->efc->lock, flags);
+	efc_node_post_event(node, EFC_EVT_FCP_CMD_RCVD, &cbdata);
+	spin_unlock_irqrestore(&node->efc->lock, flags);
+
+	return 1;
+}
+
+/**
+ * @ingroup node_common
+ * @brief Stub handler for non-ABTS BLS frames
+ *
+ * <h3 class="desc">Description</h3>
+ * Log message and drop. Customer can plumb it to their back-end as needed
+ *
+ * @param node Node that originated the frame.
+ * @param seq header/payload sequence buffers
+ *
+ * @return Returns 0
+ */
+
+int
+efc_node_recv_bls_no_sit(struct efc_node_s *node,
+			 struct efc_hw_sequence_s *seq)
+{
+	struct fc_frame_header *hdr = seq->header->dma.virt;
+
+	node_printf(node,
+		    "Dropping frame hdr = %08x %08x %08x %08x %08x %08x\n",
+		    cpu_to_be32(((u32 *)hdr)[0]),
+		    cpu_to_be32(((u32 *)hdr)[1]),
+		    cpu_to_be32(((u32 *)hdr)[2]),
+		    cpu_to_be32(((u32 *)hdr)[3]),
+		    cpu_to_be32(((u32 *)hdr)[4]),
+		    cpu_to_be32(((u32 *)hdr)[5]));
+
+	return -1;
+}
+
+/**
+ * @ingroup unsol
+ * @brief Process pending frames queued to the given node.
+ *
+ * <h3 class="desc">Description</h3>
+ * Frames that are queued for the \c node are dispatched and returned
+ * to the RQ.
+ *
+ * @param node Node of the queued frames that are to be dispatched.
+ *
+ * @return Returns 0 on success, or a negative error value on failure.
+ */
+
+int
+efc_process_node_pending(struct efc_node_s *node)
+{
+	struct efc_lport *efc = node->efc;
+	struct efc_hw_sequence_s *seq = NULL;
+	u32 pend_frames_processed = 0;
+	unsigned long flags = 0;
+
+	for (;;) {
+		/* need to check for hold frames condition after each frame
+		 * processed because any given frame could cause a transition
+		 * to a state that holds frames
+		 */
+		if (node->hold_frames)
+			break;
+
+		/* Get next frame/sequence */
+		spin_lock_irqsave(&node->pend_frames_lock, flags);
+			if (!list_empty(&node->pend_frames)) {
+				seq = list_first_entry(&node->pend_frames,
+						       struct efc_hw_sequence_s,
+						       list_entry);
+				list_del(&seq->list_entry);
+			}
+			if (!seq) {
+				pend_frames_processed =
+						node->pend_frames_processed;
+				node->pend_frames_processed = 0;
+				spin_unlock_irqrestore(&node->pend_frames_lock,
+						       flags);
+				break;
+			}
+			node->pend_frames_processed++;
+		spin_unlock_irqrestore(&node->pend_frames_lock, flags);
+
+		/* now dispatch frame(s) to dispatch function */
+		efc_node_dispatch_frame(node, seq);
+	}
+
+	if (pend_frames_processed != 0)
+		efc_log_debug(efc, "%u node frames held and processed\n",
+			      pend_frames_processed);
+
+	return 0;
+}
+
+/*
+ * @ingroup scsi_api_base
+ * @brief Notify that delete initiator is complete.
+ *
+ * @par Description
+ * Sent by the target-server to notify the base driver that the work
+ * started from efc_scsi_del_initiator() is now complete and that it
+ * is safe for the node to release the rest of its resources.
+ *
+ * @param node Pointer to the node.
+ *
+ * @return None.
+ */
+
+void
+efc_scsi_del_initiator_complete(struct efc_lport *efc, struct efc_node_s *node)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&node->efc->lock, flags);
+	/* Notify the node to resume */
+	efc_node_post_event(node, EFC_EVT_NODE_DEL_INI_COMPLETE, NULL);
+	spin_unlock_irqrestore(&node->efc->lock, flags);
+}
+
+/*
+ * @ingroup scsi_api_base
+ * @brief Notify that delete target is complete.
+ *
+ * @par Description
+ * Sent by the initiator-client to notify the base driver that the
+ * work started from efc_scsi_del_target() is now complete and
+ * that it is safe for the node to release the rest of its resources.
+ *
+ * @param node Pointer to the node.
+ *
+ * @return None.
+ */
+void
+efc_scsi_del_target_complete(struct efc_lport *efc, struct efc_node_s *node)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&node->efc->lock, flags);
+	/* Notify the node to resume */
+	efc_node_post_event(node, EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL);
+	spin_unlock_irqrestore(&node->efc->lock, flags);
+}
+
+void
+efc_scsi_io_list_empty(struct efc_lport *efc, struct efc_node_s *node)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&node->efc->lock, flags);
+	efc_node_post_event(node, EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL);
+	spin_unlock_irqrestore(&node->efc->lock, flags);
+}
+
+void efc_node_post_els_resp(struct efc_node_s *node,
+			    enum efc_hw_node_els_event_e evt, void *arg)
+{
+	enum efc_sm_event_e	sm_event = EFC_EVT_LAST;
+	struct efc_lport	*efc = node->efc;
+	unsigned long flags = 0;
+
+	switch (evt) {
+	case EFC_HW_SRRS_ELS_REQ_OK:
+		sm_event = EFC_EVT_SRRS_ELS_REQ_OK;
+		break;
+	case EFC_HW_SRRS_ELS_CMPL_OK:
+		sm_event = EFC_EVT_SRRS_ELS_CMPL_OK;
+		break;
+	case EFC_HW_SRRS_ELS_REQ_FAIL:
+		sm_event = EFC_EVT_SRRS_ELS_REQ_FAIL;
+		break;
+	case EFC_HW_SRRS_ELS_CMPL_FAIL:
+		sm_event = EFC_EVT_SRRS_ELS_CMPL_FAIL;
+		break;
+	case EFC_HW_SRRS_ELS_REQ_RJT:
+		sm_event = EFC_EVT_SRRS_ELS_REQ_RJT;
+		break;
+	case EFC_HW_ELS_REQ_ABORTED:
+		sm_event = EFC_EVT_ELS_REQ_ABORTED;
+		break;
+	default:
+		efc_log_test(efc, "unhandled event %#x\n", evt);
+		return;
+	}
+
+	spin_lock_irqsave(&node->efc->lock, flags);
+	efc_node_post_event(node, sm_event, arg);
+	spin_unlock_irqrestore(&node->efc->lock, flags);
+}
+
+void efc_node_post_shutdown(struct efc_node_s *node,
+			    u32 evt, void *arg)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&node->efc->lock, flags);
+	efc_node_post_event(node, EFC_EVT_SHUTDOWN, arg);
+	spin_unlock_irqrestore(&node->efc->lock, flags);
+}
diff --git a/drivers/scsi/elx/libefc/efc_node.h b/drivers/scsi/elx/libefc/efc_node.h
new file mode 100644
index 000000000000..96a4964b74c1
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_node.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#if !defined(__EFC_NODE_H__)
+#define __EFC_NODE_H__
+#include "scsi/fc/fc_ns.h"
+
+#define EFC_NODEDB_PAUSE_FABRIC_LOGIN	BIT(0)
+#define EFC_NODEDB_PAUSE_NAMESERVER	BIT(1)
+#define EFC_NODEDB_PAUSE_NEW_NODES	BIT(2)
+
+static inline void
+efc_node_evt_set(struct efc_sm_ctx_s *ctx, enum efc_sm_event_e evt,
+		 const char *handler)
+{
+	struct efc_node_s *node = ctx->app;
+
+	if (evt == EFC_EVT_ENTER) {
+		strncpy(node->current_state_name, handler,
+			sizeof(node->current_state_name));
+	} else if (evt == EFC_EVT_EXIT) {
+		strncpy(node->prev_state_name, node->current_state_name,
+			sizeof(node->prev_state_name));
+		strncpy(node->current_state_name, "invalid",
+			sizeof(node->current_state_name));
+	}
+	node->prev_evt = node->current_evt;
+	node->current_evt = evt;
+}
+
+/**
+ * @brief hold frames in pending frame list
+ *
+ * Unsolicited receive frames are held on the node pending frame list,
+ * rather than being processed.
+ *
+ * @param node pointer to node structure
+ *
+ * @return none
+ */
+
+static inline void
+efc_node_hold_frames(struct efc_node_s *node)
+{
+	efc_assert(node);
+	node->hold_frames = true;
+}
+
+/**
+ * @brief accept frames
+ *
+ * Unsolicited receive frames processed rather than being held on the node
+ * pending frame list.
+ *
+ * @param node pointer to node structure
+ *
+ * @return none
+ */
+
+static inline void
+efc_node_accept_frames(struct efc_node_s *node)
+{
+	efc_assert(node);
+	node->hold_frames = false;
+}
+
+extern int
+efc_node_create_pool(struct efc_lport *efc, u32 node_count);
+extern void
+efc_node_free_pool(struct efc_lport *efc);
+extern struct efc_node_s *
+efc_node_get_instance(struct efc_lport *efc, u32 instance);
+
+/**
+ * @brief Node initiator/target enable defines
+ *
+ * All combinations of the SLI port (sport) initiator/target enable, and remote
+ * node initiator/target enable are enumerated.
+ *
+ */
+
+enum efc_node_enable_e {
+	EFC_NODE_ENABLE_x_TO_x,
+	EFC_NODE_ENABLE_x_TO_T,
+	EFC_NODE_ENABLE_x_TO_I,
+	EFC_NODE_ENABLE_x_TO_IT,
+	EFC_NODE_ENABLE_T_TO_x,
+	EFC_NODE_ENABLE_T_TO_T,
+	EFC_NODE_ENABLE_T_TO_I,
+	EFC_NODE_ENABLE_T_TO_IT,
+	EFC_NODE_ENABLE_I_TO_x,
+	EFC_NODE_ENABLE_I_TO_T,
+	EFC_NODE_ENABLE_I_TO_I,
+	EFC_NODE_ENABLE_I_TO_IT,
+	EFC_NODE_ENABLE_IT_TO_x,
+	EFC_NODE_ENABLE_IT_TO_T,
+	EFC_NODE_ENABLE_IT_TO_I,
+	EFC_NODE_ENABLE_IT_TO_IT,
+};
+
+static inline enum efc_node_enable_e
+efc_node_get_enable(struct efc_node_s *node)
+{
+	u32 retval = 0;
+
+	if (node->sport->enable_ini)
+		retval |= (1U << 3);
+	if (node->sport->enable_tgt)
+		retval |= (1U << 2);
+	if (node->init)
+		retval |= (1U << 1);
+	if (node->targ)
+		retval |= (1U << 0);
+	return (enum efc_node_enable_e)retval;
+}
+
+extern int
+efc_node_check_els_req(struct efc_sm_ctx_s *ctx,
+		       enum efc_sm_event_e evt, void *arg,
+		       u8 cmd, void *(*efc_node_common_func)(const char *,
+		       struct efc_sm_ctx_s *, enum efc_sm_event_e, void *),
+		       const char *funcname);
+extern int
+efc_node_check_ns_req(struct efc_sm_ctx_s *ctx,
+		      enum efc_sm_event_e evt, void *arg,
+		  u16 cmd, void *(*efc_node_common_func)(const char *,
+		  struct efc_sm_ctx_s *, enum efc_sm_event_e, void *),
+		  const char *funcname);
+extern int
+efc_node_attach(struct efc_node_s *node);
+extern struct efc_node_s *
+efc_node_alloc(struct efc_sli_port_s *sport, u32 port_id,
+		bool init, bool targ);
+extern int
+efc_node_free(struct efc_node_s *efc);
+extern void
+efc_node_force_free(struct efc_node_s *efc);
+extern void
+efc_node_update_display_name(struct efc_node_s *node);
+void efc_node_post_event(struct efc_node_s *node, enum efc_sm_event_e evt,
+			 void *arg);
+
+extern void *
+__efc_node_shutdown(struct efc_sm_ctx_s *ctx,
+		    enum efc_sm_event_e evt, void *arg);
+extern void *
+__efc_node_wait_node_free(struct efc_sm_ctx_s *ctx,
+			  enum efc_sm_event_e evt, void *arg);
+extern void *
+__efc_node_wait_els_shutdown(struct efc_sm_ctx_s *ctx,
+			     enum efc_sm_event_e evt, void *arg);
+extern void *
+__efc_node_wait_ios_shutdown(struct efc_sm_ctx_s *ctx,
+			     enum efc_sm_event_e evt, void *arg);
+extern void
+efc_node_save_sparms(struct efc_node_s *node, void *payload);
+extern void
+efc_node_transition(struct efc_node_s *node,
+		    void *(*state)(struct efc_sm_ctx_s *,
+		    enum efc_sm_event_e, void *), void *data);
+extern void *
+__efc_node_common(const char *funcname, struct efc_sm_ctx_s *ctx,
+		  enum efc_sm_event_e evt, void *arg);
+
+extern void
+efc_node_initiate_cleanup(struct efc_node_s *node);
+
+extern void
+efc_node_build_eui_name(char *buffer, u32 buffer_len, uint64_t eui_name);
+extern uint64_t
+efc_node_get_wwpn(struct efc_node_s *node);
+
+extern void
+efc_node_pause(struct efc_node_s *node,
+	       void *(*state)(struct efc_sm_ctx_s *ctx,
+			      enum efc_sm_event_e evt, void *arg));
+extern int
+efc_node_resume(struct efc_node_s *node);
+extern void *
+__efc_node_paused(struct efc_sm_ctx_s *ctx,
+		  enum efc_sm_event_e evt, void *arg);
+extern int
+efc_node_active_ios_empty(struct efc_node_s *node);
+extern void
+efc_node_send_ls_io_cleanup(struct efc_node_s *node);
+
+extern int
+efc_els_io_list_empty(struct efc_node_s *node, struct list_head *list);
+
+extern int
+efc_process_node_pending(struct efc_node_s *domain);
+
+#endif /* __EFC_NODE_H__ */
-- 
2.13.7





[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux