diff mbox series

[v3,22/31] elx: efct: Extended link Service IO handling

Message ID 20200412033303.29574-23-jsmart2021@gmail.com (mailing list archive)
State Changes Requested
Headers show
Series efct: Broadcom (Emulex) FC Target driver | expand

Commit Message

James Smart April 12, 2020, 3:32 a.m. UTC
This patch continues the efct driver population.

This patch adds driver definitions for:
Functions to build and send ELS/CT/BLS commands and responses.

Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>

---
v3:
  Unified log message using cmd_name
  Return and drop else, for better indentation and consistency.
  Changed assertion log messages.
---
 drivers/scsi/elx/efct/efct_els.c | 1928 ++++++++++++++++++++++++++++++++++++++
 drivers/scsi/elx/efct/efct_els.h |  133 +++
 2 files changed, 2061 insertions(+)
 create mode 100644 drivers/scsi/elx/efct/efct_els.c
 create mode 100644 drivers/scsi/elx/efct/efct_els.h

Comments

Hannes Reinecke April 16, 2020, 7:58 a.m. UTC | #1
On 4/12/20 5:32 AM, James Smart wrote:
> This patch continues the efct driver population.
> 
> This patch adds driver definitions for:
> Functions to build and send ELS/CT/BLS commands and responses.
> 
> Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
> Signed-off-by: James Smart <jsmart2021@gmail.com>
> 
> ---
> v3:
>    Unified log message using cmd_name
>    Return and drop else, for better indentation and consistency.
>    Changed assertion log messages.
> ---
>   drivers/scsi/elx/efct/efct_els.c | 1928 ++++++++++++++++++++++++++++++++++++++
>   drivers/scsi/elx/efct/efct_els.h |  133 +++
>   2 files changed, 2061 insertions(+)
>   create mode 100644 drivers/scsi/elx/efct/efct_els.c
>   create mode 100644 drivers/scsi/elx/efct/efct_els.h
> 
> diff --git a/drivers/scsi/elx/efct/efct_els.c b/drivers/scsi/elx/efct/efct_els.c
> new file mode 100644
> index 000000000000..8a2598a83445
> --- /dev/null
> +++ b/drivers/scsi/elx/efct/efct_els.c
> @@ -0,0 +1,1928 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019 Broadcom. All Rights Reserved. The term
> + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
> + */
> +
> +/*
> + * Functions to build and send ELS/CT/BLS commands and responses.
> + */
> +
> +#include "efct_driver.h"
> +#include "efct_els.h"
> +
> +#define ELS_IOFMT "[i:%04x t:%04x h:%04x]"
> +
> +#define EFCT_LOG_ENABLE_ELS_TRACE(efct)		\
> +		(((efct) != NULL) ? (((efct)->logmask & (1U << 1)) != 0) : 0)
> +
> +#define node_els_trace()  \
> +	do { \
> +		if (EFCT_LOG_ENABLE_ELS_TRACE(efct)) \
> +			efc_log_info(efct, "[%s] %-20s\n", \
> +				node->display_name, __func__); \
> +	} while (0)
> +
> +#define els_io_printf(els, fmt, ...) \
> +	efc_log_debug((struct efct *)els->node->efc->base,\
> +		      "[%s]" ELS_IOFMT " %-8s " fmt, \
> +		      els->node->display_name,\
> +		      els->init_task_tag, els->tgt_task_tag, els->hw_tag,\
> +		      els->display_name, ##__VA_ARGS__)
> +

Why not simply use dyndebug here?

> +#define EFCT_ELS_RSP_LEN		1024
> +#define EFCT_ELS_GID_PT_RSP_LEN		8096
> +
> +static char *cmd_name[] = FC_ELS_CMDS_INIT;
> +
> +void *
> +efct_els_req_send(struct efc *efc, struct efc_node *node, u32 cmd,
> +		  u32 timeout_sec, u32 retries)
> +{
> +	struct efct *efct = efc->base;
> +
> +	efc_log_debug(efct, "send %s\n", cmd_name[cmd]);
> +
> +	switch (cmd) {
> +	case ELS_PLOGI:
> +		return efct_send_plogi(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_FLOGI:
> +		return efct_send_flogi(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_FDISC:
> +		return efct_send_fdisc(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_LOGO:
> +		return efct_send_logo(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_PRLI:
> +		return efct_send_prli(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_ADISC:
> +		return efct_send_adisc(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_SCR:
> +		return efct_send_scr(node, timeout_sec, retries, NULL, NULL);
> +	default:
> +		efc_log_err(efct, "Unhandled command cmd: %x\n", cmd);
> +	}
> +
> +	return NULL;
> +}
> +

You and your 'void *' functions always returning NULL ...
Please make them normal void functions.

> +void *
> +efct_els_resp_send(struct efc *efc, struct efc_node *node,
> +		   u32 cmd, u16 ox_id)
> +{
> +	struct efct *efct = efc->base;
> +
> +	switch (cmd) {
> +	case ELS_PLOGI:
> +		efct_send_plogi_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_FLOGI:
> +		efct_send_flogi_acc(node, ox_id, 0, NULL, NULL);
> +		break;
> +	case ELS_LOGO:
> +		efct_send_logo_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_PRLI:
> +		efct_send_prli_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_PRLO:
> +		efct_send_prlo_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_ADISC:
> +		efct_send_adisc_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_LS_ACC:
> +		efct_send_ls_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_PDISC:
> +	case ELS_FDISC:
> +	case ELS_RSCN:
> +	case ELS_SCR:
> +		efct_send_ls_rjt(efc, node, ox_id, ELS_RJT_UNAB,
> +				 ELS_EXPL_NONE, 0);
> +		break;
> +	default:
> +		efc_log_err(efct, "Unhandled command cmd: %x\n", cmd);
> +	}
> +
> +	return NULL;
> +}
> +

Same here.

> +struct efct_io *
> +efct_els_io_alloc(struct efc_node *node, u32 reqlen,
> +		  enum efct_els_role role)
> +{
> +	return efct_els_io_alloc_size(node, reqlen, EFCT_ELS_RSP_LEN, role);
> +}
> +
> +struct efct_io *
> +efct_els_io_alloc_size(struct efc_node *node, u32 reqlen,
> +		       u32 rsplen, enum efct_els_role role)
> +{
> +	struct efct *efct;
> +	struct efct_xport *xport;
> +	struct efct_io *els;
> +	unsigned long flags = 0;
> +
> +	efct = node->efc->base;
> +
> +	xport = efct->xport;
> +
> +	spin_lock_irqsave(&node->active_ios_lock, flags);
> +
> +	if (!node->io_alloc_enabled) {
> +		efc_log_debug(efct,
> +			       "called with io_alloc_enabled = FALSE\n");
> +		spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +		return NULL;
> +	}
> +
> +	els = efct_io_pool_io_alloc(efct->xport->io_pool);
> +	if (!els) {
> +		atomic_add_return(1, &xport->io_alloc_failed_count);
> +		spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +		return NULL;
> +	}
> +
> +	/* initialize refcount */
> +	kref_init(&els->ref);
> +	els->release = _efct_els_io_free;
> +
> +	switch (role) {
> +	case EFCT_ELS_ROLE_ORIGINATOR:
> +		els->cmd_ini = true;
> +		els->cmd_tgt = false;
> +		break;
> +	case EFCT_ELS_ROLE_RESPONDER:
> +		els->cmd_ini = false;
> +		els->cmd_tgt = true;
> +		break;
> +	}
> +
> +	/* IO should not have an associated HW IO yet.
> +	 * Assigned below.
> +	 */
> +	if (els->hio) {
> +		efc_log_err(efct, "Error: HW io not null hio:%p\n", els->hio);
> +		efct_io_pool_io_free(efct->xport->io_pool, els);
> +		spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +		return NULL;
> +	}
> +
> +	/* populate generic io fields */
> +	els->efct = efct;
> +	els->node = node;
> +
> +	/* set type and ELS-specific fields */
> +	els->io_type = EFCT_IO_TYPE_ELS;
> +	els->display_name = "pending";
> +
> +	/* now allocate DMA for request and response */
> +	els->els_req.size = reqlen;
> +	els->els_req.virt = dma_alloc_coherent(&efct->pcidev->dev,
> +					       els->els_req.size,
> +					       &els->els_req.phys,
> +					       GFP_DMA);
> +	if (els->els_req.virt) {
> +		els->els_rsp.size = rsplen;
> +		els->els_rsp.virt = dma_alloc_coherent(&efct->pcidev->dev,
> +						       els->els_rsp.size,
> +						       &els->els_rsp.phys,
> +						       GFP_DMA);
> +		if (!els->els_rsp.virt) {
> +			efc_log_err(efct, "dma_alloc rsp\n");
> +			dma_free_coherent(&efct->pcidev->dev,
> +					  els->els_req.size,
> +				els->els_req.virt, els->els_req.phys);
> +			memset(&els->els_req, 0, sizeof(struct efc_dma));
> +			efct_io_pool_io_free(efct->xport->io_pool, els);
> +			els = NULL;
> +		}
> +	} else {
> +		efc_log_err(efct, "dma_alloc req\n");
> +		efct_io_pool_io_free(efct->xport->io_pool, els);
> +		els = NULL;
> +	}
> +
> +	if (els) {
> +		/* initialize fields */
> +		els->els_retries_remaining =
> +					EFCT_FC_ELS_DEFAULT_RETRIES;
> +		els->els_pend = false;
> +		els->els_active = false;
> +
> +		/* add els structure to ELS IO list */
> +		INIT_LIST_HEAD(&els->list_entry);
> +		list_add_tail(&els->list_entry,
> +			      &node->els_io_pend_list);
> +		els->els_pend = true;
> +	}
> +
> +	spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +	return els;
> +}
> +
> +void
> +efct_els_io_free(struct efct_io *els)
> +{
> +	kref_put(&els->ref, els->release);
> +}
> +
> +void
> +_efct_els_io_free(struct kref *arg)
> +{
> +	struct efct_io *els = container_of(arg, struct efct_io, ref);
> +	struct efct *efct;
> +	struct efc_node *node;
> +	int send_empty_event = false;
> +	unsigned long flags = 0;
> +
> +	node = els->node;
> +	efct = node->efc->base;
> +
> +	spin_lock_irqsave(&node->active_ios_lock, flags);
> +		if (els->els_active) {
> +			/* if active, remove from active list and check empty */
> +			list_del(&els->list_entry);
> +			/* Send list empty event if the IO allocator
> +			 * is disabled, and the list is empty
> +			 * If node->io_alloc_enabled was not checked,
> +			 * the event would be posted continually
> +			 */
> +			send_empty_event = (!node->io_alloc_enabled) &&
> +				list_empty(&node->els_io_active_list);
> +			els->els_active = false;
> +		} else if (els->els_pend) {
> +			/* if pending, remove from pending list;
> +			 * node shutdown isn't gated off the
> +			 * pending list (only the active list),
> +			 * so no need to check if pending list is empty
> +			 */
> +			list_del(&els->list_entry);
> +			els->els_pend = 0;
> +		} else {
> +			efc_log_err(efct,
> +				"Error: els not in pending or active set\n");
> +			spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +			return;
> +		}
> +
> +	spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +
> +	/* free ELS request and response buffers */
> +	dma_free_coherent(&efct->pcidev->dev, els->els_rsp.size,
> +			  els->els_rsp.virt, els->els_rsp.phys);
> +	dma_free_coherent(&efct->pcidev->dev, els->els_req.size,
> +			  els->els_req.virt, els->els_req.phys);
> +
> +	memset(&els->els_rsp, 0, sizeof(struct efc_dma));
> +	memset(&els->els_req, 0, sizeof(struct efc_dma));
> +	efct_io_pool_io_free(efct->xport->io_pool, els);
> +
> +	if (send_empty_event)
> +		efc_scsi_io_list_empty(node->efc, node);
> +
> +	efct_scsi_check_pending(efct);
> +}
> +
> +static void
> +efct_els_make_active(struct efct_io *els)
> +{
> +	struct efc_node *node = els->node;
> +	unsigned long flags = 0;
> +
> +	/* move ELS from pending list to active list */
> +	spin_lock_irqsave(&node->active_ios_lock, flags);
> +		if (els->els_pend) {
> +			if (els->els_active) {
> +				efc_log_err(node->efc,
> +					"Error: els in both pend and active\n");
> +				spin_unlock_irqrestore(&node->active_ios_lock,
> +						       flags);
> +				return;
> +			}
> +			/* remove from pending list */
> +			list_del(&els->list_entry);
> +			els->els_pend = false;
> +
> +			/* add els structure to ELS IO list */
> +			INIT_LIST_HEAD(&els->list_entry);
> +			list_add_tail(&els->list_entry,
> +				      &node->els_io_active_list);
> +			els->els_active = true;
> +		} else {
> +			/* must be retrying; make sure it's already active */
> +			if (!els->els_active) {
> +				efc_log_err(node->efc,
> +					"Error: els not in pending or active set\n");
> +			}
> +		}
> +	spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +}
> +
> +static int efct_els_send(struct efct_io *els, u32 reqlen,
> +			 u32 timeout_sec, efct_hw_srrs_cb_t cb)
> +{
> +	struct efc_node *node = els->node;
> +
> +	/* update ELS request counter */
> +	node->els_req_cnt++;
> +
> +	/* move ELS from pending list to active list */
> +	efct_els_make_active(els);
> +
> +	els->wire_len = reqlen;
> +	return efct_scsi_io_dispatch(els, cb);
> +}
> +
> +static void
> +efct_els_retry(struct efct_io *els);
> +
> +static void
> +efct_els_delay_timer_cb(struct timer_list *t)
> +{
> +	struct efct_io *els = from_timer(els, t, delay_timer);
> +	struct efc_node *node = els->node;
> +
> +	/* Retry delay timer expired, retry the ELS request,
> +	 * Free the HW IO so that a new oxid is used.
> +	 */
> +	if (els->state == EFCT_ELS_REQUEST_DELAY_ABORT) {
> +		node->els_req_cnt++;
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +					    NULL);
> +	} else {
> +		efct_els_retry(els);
> +	}
> +}
> +
> +static void
> +efct_els_abort_cleanup(struct efct_io *els)
> +{
> +	/* handle event for ABORT_WQE
> +	 * whatever state ELS happened to be in, propagate aborted even
> +	 * up to node state machine in lieu of EFC_HW_SRRS_ELS_* event
> +	 */
> +	struct efc_node_cb cbdata;
> +
> +	cbdata.status = 0;
> +	cbdata.ext_status = 0;
> +	cbdata.els_rsp = els->els_rsp;
> +	els_io_printf(els, "Request aborted\n");
> +	efct_els_io_cleanup(els, EFC_HW_ELS_REQ_ABORTED, &cbdata);
> +}
> +
> +static int
> +efct_els_req_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
> +		u32 length, int status, u32 ext_status, void *arg)
> +{
> +	struct efct_io *els;
> +	struct efc_node *node;
> +	struct efct *efct;
> +	struct efc_node_cb cbdata;
> +	u32 reason_code;
> +
> +	els = arg;
> +	node = els->node;
> +	efct = node->efc->base;
> +
> +	if (status != 0)
> +		els_io_printf(els, "status x%x ext x%x\n", status, ext_status);
> +
> +	/* set the response len element of els->rsp */
> +	els->els_rsp.len = length;
> +
> +	cbdata.status = status;
> +	cbdata.ext_status = ext_status;
> +	cbdata.header = NULL;
> +	cbdata.els_rsp = els->els_rsp;
> +
> +	/* FW returns the number of bytes received on the link in
> +	 * the WCQE, not the amount placed in the buffer; use this info to
> +	 * check if there was an overrun.
> +	 */
> +	if (length > els->els_rsp.size) {
> +		efc_log_warn(efct,
> +			      "ELS response returned len=%d > buflen=%zu\n",
> +			     length, els->els_rsp.size);
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL, &cbdata);
> +		return EFC_SUCCESS;
> +	}
> +
> +	/* Post event to ELS IO object */
> +	switch (status) {
> +	case SLI4_FC_WCQE_STATUS_SUCCESS:
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_OK, &cbdata);
> +		break;
> +
> +	case SLI4_FC_WCQE_STATUS_LS_RJT:
> +		reason_code = (ext_status >> 16) & 0xff;
> +
> +		/* delay and retry if reason code is Logical Busy */
> +		switch (reason_code) {
> +		case ELS_RJT_BUSY:
> +			els->node->els_req_cnt--;
> +			els_io_printf(els,
> +				      "LS_RJT Logical Busy response,delay and retry\n");
> +			timer_setup(&els->delay_timer,
> +				    efct_els_delay_timer_cb, 0);
> +			mod_timer(&els->delay_timer,
> +				  jiffies + msecs_to_jiffies(5000));
> +			els->state = EFCT_ELS_REQUEST_DELAYED;
> +			break;
> +		default:
> +			efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_RJT,
> +					    &cbdata);
> +			break;
> +		}
> +		break;
> +
> +	case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
> +		switch (ext_status) {
> +		case SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT:
> +			efct_els_retry(els);
> +			break;
> +
> +		case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
> +			if (els->state == EFCT_ELS_ABORT_IO_COMPL) {
> +				/* completion for ELS that was aborted */
> +				efct_els_abort_cleanup(els);
> +			} else {
> +				/* completion for ELS received first,
> +				 * transition to wait for abort cmpl
> +				 */
> +				els->state = EFCT_ELS_REQ_ABORTED;
> +			}
> +
> +			break;
> +		default:
> +			efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +					    &cbdata);
> +			break;
> +		}
> +		break;
> +	default:	/* Other error */
> +		efc_log_warn(efct,
> +			      "els req failed status x%x, ext_status, x%x\n",
> +					status, ext_status);
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL, &cbdata);
> +		break;
> +	}
> +
> +	return EFC_SUCCESS;
> +}
> +
> +static void efct_els_send_req(struct efc_node *node, struct efct_io *els)
> +{
> +	int rc = 0;
> +	struct efct *efct;
> +
> +	efct = node->efc->base;
> +	rc = efct_els_send(els, els->els_req.size,
> +			   els->els_timeout_sec, efct_els_req_cb);
> +
> +	if (rc) {
> +		struct efc_node_cb cbdata;
> +
> +		cbdata.status = INT_MAX;
> +		cbdata.ext_status = INT_MAX;
> +		cbdata.els_rsp = els->els_rsp;
> +		efc_log_err(efct, "efct_els_send failed: %d\n", rc);
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +				    &cbdata);
> +	}
> +}
> +
> +static void
> +efct_els_retry(struct efct_io *els)
> +{
> +	struct efct *efct;
> +	struct efc_node_cb cbdata;
> +
> +	efct = els->node->efc->base;
> +	cbdata.status = INT_MAX;
> +	cbdata.ext_status = INT_MAX;
> +	cbdata.els_rsp = els->els_rsp;
> +
> +	if (!els->els_retries_remaining) {
> +		efc_log_err(efct, "ELS retries exhausted\n");
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +				    &cbdata);
> +		return;
> +	}
> +
> +	els->els_retries_remaining--;
> +	 /* Free the HW IO so that a new oxid is used.*/
> +	if (els->hio) {
> +		efct_hw_io_free(&efct->hw, els->hio);
> +		els->hio = NULL;
> +	}
> +
> +	efct_els_send_req(els->node, els);
> +}
> +
> +static int
> +efct_els_acc_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
> +		u32 length, int status, u32 ext_status, void *arg)
> +{
> +	struct efct_io *els;
> +	struct efc_node *node;
> +	struct efct *efct;
> +	struct efc_node_cb cbdata;
> +
> +	els = arg;
> +	node = els->node;
> +	efct = node->efc->base;
> +
> +	cbdata.status = status;
> +	cbdata.ext_status = ext_status;
> +	cbdata.header = NULL;
> +	cbdata.els_rsp = els->els_rsp;
> +
> +	/* Post node event */
> +	switch (status) {
> +	case SLI4_FC_WCQE_STATUS_SUCCESS:
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_CMPL_OK, &cbdata);
> +		break;
> +
> +	default:	/* Other error */
> +		efc_log_warn(efct,
> +			      "[%s] %-8s failed status x%x, ext_status x%x\n",
> +			    node->display_name, els->display_name,
> +			    status, ext_status);
> +		efc_log_warn(efct,
> +			      "els acc complete: failed status x%x, ext_status, x%x\n",
> +		     status, ext_status);
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_CMPL_FAIL, &cbdata);
> +		break;
> +	}
> +
> +	return EFC_SUCCESS;
> +}
> +
> +static int
> +efct_els_send_rsp(struct efct_io *els, u32 rsplen)
> +{
> +	struct efc_node *node = els->node;
> +
> +	/* increment ELS completion counter */
> +	node->els_cmpl_cnt++;
> +
> +	/* move ELS from pending list to active list */
> +	efct_els_make_active(els);
> +
> +	els->wire_len = rsplen;
> +	return efct_scsi_io_dispatch(els, efct_els_acc_cb);
> +}
> +
> +struct efct_io *
> +efct_send_plogi(struct efc_node *node, u32 timeout_sec,
> +		u32 retries,
> +	      void (*cb)(struct efc_node *node,
> +			 struct efc_node_cb *cbdata, void *arg), void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_flogi  *plogi;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*plogi), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "plogi";
> +
> +	/* Build PLOGI request */
> +	plogi = els->els_req.virt;
> +
> +	memcpy(plogi, node->sport->service_params, sizeof(*plogi));
> +
> +	plogi->fl_cmd = ELS_PLOGI;
> +	memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_flogi(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct;
> +	struct fc_els_flogi  *flogi;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*flogi), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "flogi";
> +
> +	/* Build FLOGI request */
> +	flogi = els->els_req.virt;
> +
> +	memcpy(flogi, node->sport->service_params, sizeof(*flogi));
> +	flogi->fl_cmd = ELS_FLOGI;
> +	memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_fdisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct;
> +	struct fc_els_flogi *fdisc;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*fdisc), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "fdisc";
> +
> +	/* Build FDISC request */
> +	fdisc = els->els_req.virt;
> +
> +	memcpy(fdisc, node->sport->service_params, sizeof(*fdisc));
> +	fdisc->fl_cmd = ELS_FDISC;
> +	memset(fdisc->_fl_resvd, 0, sizeof(fdisc->_fl_resvd));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_prli(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	       els_cb_t cb, void *cbarg)
> +{
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *els;
> +	struct {
> +		struct fc_els_prli prli;
> +		struct fc_els_spp spp;
> +	} *pp;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "prli";
> +
> +	/* Build PRLI request */
> +	pp = els->els_req.virt;
> +
> +	memset(pp, 0, sizeof(*pp));
> +
> +	pp->prli.prli_cmd = ELS_PRLI;
> +	pp->prli.prli_spp_len = 16;
> +	pp->prli.prli_len = cpu_to_be16(sizeof(*pp));
> +	pp->spp.spp_type = FC_TYPE_FCP;
> +	pp->spp.spp_type_ext = 0;
> +	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
> +	pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS |
> +			       (node->sport->enable_ini ?
> +			       FCP_SPPF_INIT_FCN : 0) |
> +			       (node->sport->enable_tgt ?
> +			       FCP_SPPF_TARG_FCN : 0));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_prlo(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	       els_cb_t cb, void *cbarg)
> +{
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *els;
> +	struct {
> +		struct fc_els_prlo prlo;
> +		struct fc_els_spp spp;
> +	} *pp;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "prlo";
> +
> +	/* Build PRLO request */
> +	pp = els->els_req.virt;
> +
> +	memset(pp, 0, sizeof(*pp));
> +	pp->prlo.prlo_cmd = ELS_PRLO;
> +	pp->prlo.prlo_obs = 0x10;
> +	pp->prlo.prlo_len = cpu_to_be16(sizeof(*pp));
> +
> +	pp->spp.spp_type = FC_TYPE_FCP;
> +	pp->spp.spp_type_ext = 0;
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_logo(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	       els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct;
> +	struct fc_els_logo *logo;
> +	struct fc_els_flogi  *sparams;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	sparams = (struct fc_els_flogi *)node->sport->service_params;
> +
> +	els = efct_els_io_alloc(node, sizeof(*logo), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "logo";
> +
> +	/* Build LOGO request */
> +
> +	logo = els->els_req.virt;
> +
> +	memset(logo, 0, sizeof(*logo));
> +	logo->fl_cmd = ELS_LOGO;
> +	hton24(logo->fl_n_port_id, node->rnode.sport->fc_id);
> +	logo->fl_n_port_wwn = sparams->fl_wwpn;
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_adisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct;
> +	struct fc_els_adisc *adisc;
> +	struct fc_els_flogi  *sparams;
> +	struct efc_sli_port *sport = node->sport;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	sparams = (struct fc_els_flogi *)node->sport->service_params;
> +
> +	els = efct_els_io_alloc(node, sizeof(*adisc), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "adisc";
> +
> +	/* Build ADISC request */
> +
> +	adisc = els->els_req.virt;
> +
> +	memset(adisc, 0, sizeof(*adisc));
> +	adisc->adisc_cmd = ELS_ADISC;
> +	hton24(adisc->adisc_hard_addr, sport->fc_id);
> +	adisc->adisc_wwpn = sparams->fl_wwpn;
> +	adisc->adisc_wwnn = sparams->fl_wwnn;
> +	hton24(adisc->adisc_port_id, node->rnode.sport->fc_id);
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_pdisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_flogi  *pdisc;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*pdisc), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "pdisc";
> +
> +	pdisc = els->els_req.virt;
> +
> +	memcpy(pdisc, node->sport->service_params, sizeof(*pdisc));
> +
> +	pdisc->fl_cmd = ELS_PDISC;
> +	memset(pdisc->_fl_resvd, 0, sizeof(pdisc->_fl_resvd));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_scr(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	      els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_scr *req;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*req), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "scr";
> +
> +	req = els->els_req.virt;
> +
> +	memset(req, 0, sizeof(*req));
> +	req->scr_cmd = ELS_SCR;
> +	req->scr_reg_func = ELS_SCRF_FULL;
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_rscn(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	       void *port_ids, u32 port_ids_count, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_rscn *req;
> +	struct fc_els_rscn_page *rscn_page;
> +	u32 length = sizeof(*rscn_page) * port_ids_count;
> +
> +	length += sizeof(*req);
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, length, EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "rscn";
> +
> +	req = els->els_req.virt;
> +
> +	req->rscn_cmd = ELS_RSCN;
> +	req->rscn_page_len = sizeof(struct fc_els_rscn_page);
> +	req->rscn_plen = cpu_to_be16(length);
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	/* copy in the payload */
> +	rscn_page = els->els_req.virt + sizeof(*req);
> +	memcpy(rscn_page, port_ids,
> +	       port_ids_count * sizeof(*rscn_page));
> +
> +	efct_els_send_req(node, els);
> + > +	return els;
> +}
> +
> +void *
> +efct_send_ls_rjt(struct efc *efc, struct efc_node *node,
> +		 u32 ox_id, u32 reason_code,
> +		u32 reason_code_expl, u32 vendor_unique)
> +{
> +	struct efct_io *io = NULL;
> +	int rc;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_ls_rjt *rjt;
> +
> +	io = efct_els_io_alloc(node, sizeof(*rjt), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	node_els_trace();
> +
> +	io->els_callback = NULL;
> +	io->els_callback_arg = NULL;
> +	io->display_name = "ls_rjt";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +
> +	rjt = io->els_req.virt;
> +	memset(rjt, 0, sizeof(*rjt));
> +
> +	rjt->er_cmd = ELS_LS_RJT;
> +	rjt->er_reason = reason_code;
> +	rjt->er_explan = reason_code_expl;
> +
> +	io->hio_type = EFCT_HW_ELS_RSP;
> +	rc = efct_els_send_rsp(io, sizeof(*rjt));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
That is a bit strange.
Sending a response can fail, but (apparently) sending a request cannot; 
at the very least you don't have (or check) the return value from the 
send request.

Some intricate scheme I've missed?


Cheers,

Hannes
Daniel Wagner April 16, 2020, 9:49 a.m. UTC | #2
On Sat, Apr 11, 2020 at 08:32:54PM -0700, James Smart wrote:
> This patch continues the efct driver population.
> 
> This patch adds driver definitions for:
> Functions to build and send ELS/CT/BLS commands and responses.
> 
> Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
> Signed-off-by: James Smart <jsmart2021@gmail.com>
> 
> ---
> v3:
>   Unified log message using cmd_name
>   Return and drop else, for better indentation and consistency.
>   Changed assertion log messages.
> ---
>  drivers/scsi/elx/efct/efct_els.c | 1928 ++++++++++++++++++++++++++++++++++++++
>  drivers/scsi/elx/efct/efct_els.h |  133 +++
>  2 files changed, 2061 insertions(+)
>  create mode 100644 drivers/scsi/elx/efct/efct_els.c
>  create mode 100644 drivers/scsi/elx/efct/efct_els.h
> 
> diff --git a/drivers/scsi/elx/efct/efct_els.c b/drivers/scsi/elx/efct/efct_els.c
> new file mode 100644
> index 000000000000..8a2598a83445
> --- /dev/null
> +++ b/drivers/scsi/elx/efct/efct_els.c
> @@ -0,0 +1,1928 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019 Broadcom. All Rights Reserved. The term
> + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
> + */
> +
> +/*
> + * Functions to build and send ELS/CT/BLS commands and responses.
> + */
> +
> +#include "efct_driver.h"
> +#include "efct_els.h"
> +
> +#define ELS_IOFMT "[i:%04x t:%04x h:%04x]"
> +
> +#define EFCT_LOG_ENABLE_ELS_TRACE(efct)		\
> +		(((efct) != NULL) ? (((efct)->logmask & (1U << 1)) != 0) : 0)
> +
> +#define node_els_trace()  \
> +	do { \
> +		if (EFCT_LOG_ENABLE_ELS_TRACE(efct)) \
> +			efc_log_info(efct, "[%s] %-20s\n", \
> +				node->display_name, __func__); \
> +	} while (0)
> +
> +#define els_io_printf(els, fmt, ...) \
> +	efc_log_debug((struct efct *)els->node->efc->base,\
> +		      "[%s]" ELS_IOFMT " %-8s " fmt, \
> +		      els->node->display_name,\
> +		      els->init_task_tag, els->tgt_task_tag, els->hw_tag,\
> +		      els->display_name, ##__VA_ARGS__)
> +
> +#define EFCT_ELS_RSP_LEN		1024
> +#define EFCT_ELS_GID_PT_RSP_LEN		8096
> +
> +static char *cmd_name[] = FC_ELS_CMDS_INIT;
> +
> +void *
> +efct_els_req_send(struct efc *efc, struct efc_node *node, u32 cmd,
> +		  u32 timeout_sec, u32 retries)
> +{
> +	struct efct *efct = efc->base;
> +
> +	efc_log_debug(efct, "send %s\n", cmd_name[cmd]);
> +
> +	switch (cmd) {
> +	case ELS_PLOGI:
> +		return efct_send_plogi(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_FLOGI:
> +		return efct_send_flogi(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_FDISC:
> +		return efct_send_fdisc(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_LOGO:
> +		return efct_send_logo(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_PRLI:
> +		return efct_send_prli(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_ADISC:
> +		return efct_send_adisc(node, timeout_sec, retries, NULL, NULL);
> +	case ELS_SCR:
> +		return efct_send_scr(node, timeout_sec, retries, NULL, NULL);
> +	default:
> +		efc_log_err(efct, "Unhandled command cmd: %x\n", cmd);
> +	}
> +
> +	return NULL;
> +}
> +
> +void *
> +efct_els_resp_send(struct efc *efc, struct efc_node *node,
> +		   u32 cmd, u16 ox_id)
> +{
> +	struct efct *efct = efc->base;
> +
> +	switch (cmd) {
> +	case ELS_PLOGI:
> +		efct_send_plogi_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_FLOGI:
> +		efct_send_flogi_acc(node, ox_id, 0, NULL, NULL);
> +		break;
> +	case ELS_LOGO:
> +		efct_send_logo_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_PRLI:
> +		efct_send_prli_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_PRLO:
> +		efct_send_prlo_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_ADISC:
> +		efct_send_adisc_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_LS_ACC:
> +		efct_send_ls_acc(node, ox_id, NULL, NULL);
> +		break;
> +	case ELS_PDISC:
> +	case ELS_FDISC:
> +	case ELS_RSCN:
> +	case ELS_SCR:
> +		efct_send_ls_rjt(efc, node, ox_id, ELS_RJT_UNAB,
> +				 ELS_EXPL_NONE, 0);
> +		break;
> +	default:
> +		efc_log_err(efct, "Unhandled command cmd: %x\n", cmd);
> +	}
> +
> +	return NULL;
> +}
> +
> +struct efct_io *
> +efct_els_io_alloc(struct efc_node *node, u32 reqlen,
> +		  enum efct_els_role role)
> +{
> +	return efct_els_io_alloc_size(node, reqlen, EFCT_ELS_RSP_LEN, role);
> +}
> +
> +struct efct_io *
> +efct_els_io_alloc_size(struct efc_node *node, u32 reqlen,
> +		       u32 rsplen, enum efct_els_role role)
> +{
> +	struct efct *efct;
> +	struct efct_xport *xport;
> +	struct efct_io *els;
> +	unsigned long flags = 0;
> +
> +	efct = node->efc->base;
> +
> +	xport = efct->xport;
> +
> +	spin_lock_irqsave(&node->active_ios_lock, flags);
> +
> +	if (!node->io_alloc_enabled) {
> +		efc_log_debug(efct,
> +			       "called with io_alloc_enabled = FALSE\n");
> +		spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +		return NULL;
> +	}
> +
> +	els = efct_io_pool_io_alloc(efct->xport->io_pool);
> +	if (!els) {
> +		atomic_add_return(1, &xport->io_alloc_failed_count);
> +		spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +		return NULL;
> +	}
> +
> +	/* initialize refcount */
> +	kref_init(&els->ref);
> +	els->release = _efct_els_io_free;
> +
> +	switch (role) {
> +	case EFCT_ELS_ROLE_ORIGINATOR:
> +		els->cmd_ini = true;
> +		els->cmd_tgt = false;
> +		break;
> +	case EFCT_ELS_ROLE_RESPONDER:
> +		els->cmd_ini = false;
> +		els->cmd_tgt = true;
> +		break;
> +	}
> +
> +	/* IO should not have an associated HW IO yet.
> +	 * Assigned below.
> +	 */
> +	if (els->hio) {
> +		efc_log_err(efct, "Error: HW io not null hio:%p\n", els->hio);
> +		efct_io_pool_io_free(efct->xport->io_pool, els);
> +		spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +		return NULL;
> +	}
> +
> +	/* populate generic io fields */
> +	els->efct = efct;
> +	els->node = node;
> +
> +	/* set type and ELS-specific fields */
> +	els->io_type = EFCT_IO_TYPE_ELS;
> +	els->display_name = "pending";
> +
> +	/* now allocate DMA for request and response */
> +	els->els_req.size = reqlen;
> +	els->els_req.virt = dma_alloc_coherent(&efct->pcidev->dev,
> +					       els->els_req.size,
> +					       &els->els_req.phys,
> +					       GFP_DMA);

GFP_KERNEL

> +	if (els->els_req.virt) {
> +		els->els_rsp.size = rsplen;
> +		els->els_rsp.virt = dma_alloc_coherent(&efct->pcidev->dev,
> +						       els->els_rsp.size,
> +						       &els->els_rsp.phys,
> +						       GFP_DMA);

GFP_KERNEL

> +		if (!els->els_rsp.virt) {
> +			efc_log_err(efct, "dma_alloc rsp\n");
> +			dma_free_coherent(&efct->pcidev->dev,
> +					  els->els_req.size,
> +				els->els_req.virt, els->els_req.phys);
> +			memset(&els->els_req, 0, sizeof(struct efc_dma));
> +			efct_io_pool_io_free(efct->xport->io_pool, els);
> +			els = NULL;
> +		}
> +	} else {
> +		efc_log_err(efct, "dma_alloc req\n");
> +		efct_io_pool_io_free(efct->xport->io_pool, els);
> +		els = NULL;
> +	}
> +
> +	if (els) {
> +		/* initialize fields */
> +		els->els_retries_remaining =
> +					EFCT_FC_ELS_DEFAULT_RETRIES;
> +		els->els_pend = false;
> +		els->els_active = false;
> +
> +		/* add els structure to ELS IO list */
> +		INIT_LIST_HEAD(&els->list_entry);
> +		list_add_tail(&els->list_entry,
> +			      &node->els_io_pend_list);
> +		els->els_pend = true;
> +	}
> +
> +	spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +	return els;
> +}
> +
> +void
> +efct_els_io_free(struct efct_io *els)
> +{
> +	kref_put(&els->ref, els->release);
> +}
> +
> +void
> +_efct_els_io_free(struct kref *arg)
> +{
> +	struct efct_io *els = container_of(arg, struct efct_io, ref);
> +	struct efct *efct;
> +	struct efc_node *node;
> +	int send_empty_event = false;
> +	unsigned long flags = 0;
> +
> +	node = els->node;
> +	efct = node->efc->base;
> +
> +	spin_lock_irqsave(&node->active_ios_lock, flags);
> +		if (els->els_active) {
> +			/* if active, remove from active list and check empty */
> +			list_del(&els->list_entry);
> +			/* Send list empty event if the IO allocator
> +			 * is disabled, and the list is empty
> +			 * If node->io_alloc_enabled was not checked,
> +			 * the event would be posted continually
> +			 */
> +			send_empty_event = (!node->io_alloc_enabled) &&
> +				list_empty(&node->els_io_active_list);
> +			els->els_active = false;
> +		} else if (els->els_pend) {
> +			/* if pending, remove from pending list;
> +			 * node shutdown isn't gated off the
> +			 * pending list (only the active list),
> +			 * so no need to check if pending list is empty
> +			 */
> +			list_del(&els->list_entry);
> +			els->els_pend = 0;
> +		} else {
> +			efc_log_err(efct,
> +				"Error: els not in pending or active set\n");
> +			spin_unlock_irqrestore(&node->active_ios_lock, flags);

unlock first then log

> +			return;
> +		}

no need to indent

> +
> +	spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +
> +	/* free ELS request and response buffers */
> +	dma_free_coherent(&efct->pcidev->dev, els->els_rsp.size,
> +			  els->els_rsp.virt, els->els_rsp.phys);
> +	dma_free_coherent(&efct->pcidev->dev, els->els_req.size,
> +			  els->els_req.virt, els->els_req.phys);
> +
> +	memset(&els->els_rsp, 0, sizeof(struct efc_dma));
> +	memset(&els->els_req, 0, sizeof(struct efc_dma));
> +	efct_io_pool_io_free(efct->xport->io_pool, els);
> +
> +	if (send_empty_event)
> +		efc_scsi_io_list_empty(node->efc, node);
> +
> +	efct_scsi_check_pending(efct);
> +}
> +
> +static void
> +efct_els_make_active(struct efct_io *els)
> +{
> +	struct efc_node *node = els->node;
> +	unsigned long flags = 0;
> +
> +	/* move ELS from pending list to active list */
> +	spin_lock_irqsave(&node->active_ios_lock, flags);
> +		if (els->els_pend) {
> +			if (els->els_active) {
> +				efc_log_err(node->efc,
> +					"Error: els in both pend and active\n");
> +				spin_unlock_irqrestore(&node->active_ios_lock,
> +						       flags);

unlock then log

> +				return;
> +			}
> +			/* remove from pending list */
> +			list_del(&els->list_entry);
> +			els->els_pend = false;
> +
> +			/* add els structure to ELS IO list */
> +			INIT_LIST_HEAD(&els->list_entry);
> +			list_add_tail(&els->list_entry,
> +				      &node->els_io_active_list);
> +			els->els_active = true;
> +		} else {
> +			/* must be retrying; make sure it's already active */
> +			if (!els->els_active) {

		} else if (!els->els_active) {

> +				efc_log_err(node->efc,
> +					"Error: els not in pending or active set\n");
> +			}
> +		}

no need to indent

> +	spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +}
> +
> +static int efct_els_send(struct efct_io *els, u32 reqlen,
> +			 u32 timeout_sec, efct_hw_srrs_cb_t cb)
> +{
> +	struct efc_node *node = els->node;
> +
> +	/* update ELS request counter */
> +	node->els_req_cnt++;
> +
> +	/* move ELS from pending list to active list */
> +	efct_els_make_active(els);
> +
> +	els->wire_len = reqlen;
> +	return efct_scsi_io_dispatch(els, cb);
> +}
> +
> +static void
> +efct_els_retry(struct efct_io *els);
> +
> +static void
> +efct_els_delay_timer_cb(struct timer_list *t)
> +{
> +	struct efct_io *els = from_timer(els, t, delay_timer);
> +	struct efc_node *node = els->node;
> +
> +	/* Retry delay timer expired, retry the ELS request,
> +	 * Free the HW IO so that a new oxid is used.
> +	 */
> +	if (els->state == EFCT_ELS_REQUEST_DELAY_ABORT) {
> +		node->els_req_cnt++;
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +					    NULL);

fits on one line

> +	} else {
> +		efct_els_retry(els);
> +	}
> +}
> +
> +static void
> +efct_els_abort_cleanup(struct efct_io *els)
> +{
> +	/* handle event for ABORT_WQE
> +	 * whatever state ELS happened to be in, propagate aborted even
> +	 * up to node state machine in lieu of EFC_HW_SRRS_ELS_* event
> +	 */
> +	struct efc_node_cb cbdata;
> +
> +	cbdata.status = 0;
> +	cbdata.ext_status = 0;
> +	cbdata.els_rsp = els->els_rsp;
> +	els_io_printf(els, "Request aborted\n");
> +	efct_els_io_cleanup(els, EFC_HW_ELS_REQ_ABORTED, &cbdata);
> +}
> +
> +static int
> +efct_els_req_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
> +		u32 length, int status, u32 ext_status, void *arg)
> +{
> +	struct efct_io *els;
> +	struct efc_node *node;
> +	struct efct *efct;
> +	struct efc_node_cb cbdata;
> +	u32 reason_code;
> +
> +	els = arg;
> +	node = els->node;
> +	efct = node->efc->base;
> +
> +	if (status != 0)
> +		els_io_printf(els, "status x%x ext x%x\n", status, ext_status);
> +
> +	/* set the response len element of els->rsp */
> +	els->els_rsp.len = length;
> +
> +	cbdata.status = status;
> +	cbdata.ext_status = ext_status;
> +	cbdata.header = NULL;
> +	cbdata.els_rsp = els->els_rsp;
> +
> +	/* FW returns the number of bytes received on the link in
> +	 * the WCQE, not the amount placed in the buffer; use this info to
> +	 * check if there was an overrun.
> +	 */
> +	if (length > els->els_rsp.size) {
> +		efc_log_warn(efct,
> +			      "ELS response returned len=%d > buflen=%zu\n",
> +			     length, els->els_rsp.size);
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL, &cbdata);
> +		return EFC_SUCCESS;
> +	}
> +
> +	/* Post event to ELS IO object */
> +	switch (status) {
> +	case SLI4_FC_WCQE_STATUS_SUCCESS:
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_OK, &cbdata);
> +		break;
> +
> +	case SLI4_FC_WCQE_STATUS_LS_RJT:
> +		reason_code = (ext_status >> 16) & 0xff;
> +
> +		/* delay and retry if reason code is Logical Busy */
> +		switch (reason_code) {
> +		case ELS_RJT_BUSY:
> +			els->node->els_req_cnt--;
> +			els_io_printf(els,
> +				      "LS_RJT Logical Busy response,delay and retry\n");
> +			timer_setup(&els->delay_timer,
> +				    efct_els_delay_timer_cb, 0);
> +			mod_timer(&els->delay_timer,
> +				  jiffies + msecs_to_jiffies(5000));
> +			els->state = EFCT_ELS_REQUEST_DELAYED;
> +			break;
> +		default:
> +			efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_RJT,
> +					    &cbdata);
> +			break;
> +		}
> +		break;
> +
> +	case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
> +		switch (ext_status) {
> +		case SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT:
> +			efct_els_retry(els);
> +			break;
> +
> +		case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
> +			if (els->state == EFCT_ELS_ABORT_IO_COMPL) {
> +				/* completion for ELS that was aborted */
> +				efct_els_abort_cleanup(els);
> +			} else {
> +				/* completion for ELS received first,
> +				 * transition to wait for abort cmpl
> +				 */
> +				els->state = EFCT_ELS_REQ_ABORTED;
> +			}
> +
> +			break;
> +		default:
> +			efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +					    &cbdata);
> +			break;
> +		}
> +		break;
> +	default:	/* Other error */
> +		efc_log_warn(efct,
> +			      "els req failed status x%x, ext_status, x%x\n",
> +					status, ext_status);
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL, &cbdata);
> +		break;
> +	}
> +
> +	return EFC_SUCCESS;
> +}
> +
> +static void efct_els_send_req(struct efc_node *node, struct efct_io *els)
> +{
> +	int rc = 0;
> +	struct efct *efct;
> +
> +	efct = node->efc->base;
> +	rc = efct_els_send(els, els->els_req.size,
> +			   els->els_timeout_sec, efct_els_req_cb);
> +
> +	if (rc) {

	if (!rc)
		return;

> +		struct efc_node_cb cbdata;
> +
> +		cbdata.status = INT_MAX;
> +		cbdata.ext_status = INT_MAX;
> +		cbdata.els_rsp = els->els_rsp;
> +		efc_log_err(efct, "efct_els_send failed: %d\n", rc);
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +				    &cbdata);
> +	}
> +}
> +
> +static void
> +efct_els_retry(struct efct_io *els)
> +{
> +	struct efct *efct;
> +	struct efc_node_cb cbdata;
> +
> +	efct = els->node->efc->base;
> +	cbdata.status = INT_MAX;
> +	cbdata.ext_status = INT_MAX;
> +	cbdata.els_rsp = els->els_rsp;
> +
> +	if (!els->els_retries_remaining) {
> +		efc_log_err(efct, "ELS retries exhausted\n");
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +				    &cbdata);
> +		return;
> +	}
> +
> +	els->els_retries_remaining--;
> +	 /* Free the HW IO so that a new oxid is used.*/
> +	if (els->hio) {
> +		efct_hw_io_free(&efct->hw, els->hio);
> +		els->hio = NULL;
> +	}
> +
> +	efct_els_send_req(els->node, els);
> +}
> +
> +static int
> +efct_els_acc_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
> +		u32 length, int status, u32 ext_status, void *arg)
> +{
> +	struct efct_io *els;
> +	struct efc_node *node;
> +	struct efct *efct;
> +	struct efc_node_cb cbdata;
> +
> +	els = arg;
> +	node = els->node;
> +	efct = node->efc->base;
> +
> +	cbdata.status = status;
> +	cbdata.ext_status = ext_status;
> +	cbdata.header = NULL;
> +	cbdata.els_rsp = els->els_rsp;
> +
> +	/* Post node event */
> +	switch (status) {
> +	case SLI4_FC_WCQE_STATUS_SUCCESS:
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_CMPL_OK, &cbdata);
> +		break;
> +
> +	default:	/* Other error */
> +		efc_log_warn(efct,
> +			      "[%s] %-8s failed status x%x, ext_status x%x\n",
> +			    node->display_name, els->display_name,
> +			    status, ext_status);
> +		efc_log_warn(efct,
> +			      "els acc complete: failed status x%x, ext_status, x%x\n",
> +		     status, ext_status);
> +		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_CMPL_FAIL, &cbdata);
> +		break;
> +	}
> +
> +	return EFC_SUCCESS;
> +}
> +
> +static int
> +efct_els_send_rsp(struct efct_io *els, u32 rsplen)
> +{
> +	struct efc_node *node = els->node;
> +
> +	/* increment ELS completion counter */
> +	node->els_cmpl_cnt++;
> +
> +	/* move ELS from pending list to active list */
> +	efct_els_make_active(els);
> +
> +	els->wire_len = rsplen;
> +	return efct_scsi_io_dispatch(els, efct_els_acc_cb);
> +}
> +
> +struct efct_io *
> +efct_send_plogi(struct efc_node *node, u32 timeout_sec,
> +		u32 retries,
> +	      void (*cb)(struct efc_node *node,
> +			 struct efc_node_cb *cbdata, void *arg), void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_flogi  *plogi;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*plogi), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "plogi";
> +
> +	/* Build PLOGI request */
> +	plogi = els->els_req.virt;
> +
> +	memcpy(plogi, node->sport->service_params, sizeof(*plogi));
> +
> +	plogi->fl_cmd = ELS_PLOGI;
> +	memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_flogi(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct;
> +	struct fc_els_flogi  *flogi;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*flogi), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "flogi";
> +
> +	/* Build FLOGI request */
> +	flogi = els->els_req.virt;
> +
> +	memcpy(flogi, node->sport->service_params, sizeof(*flogi));
> +	flogi->fl_cmd = ELS_FLOGI;
> +	memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_fdisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct;
> +	struct fc_els_flogi *fdisc;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*fdisc), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "fdisc";
> +
> +	/* Build FDISC request */
> +	fdisc = els->els_req.virt;
> +
> +	memcpy(fdisc, node->sport->service_params, sizeof(*fdisc));
> +	fdisc->fl_cmd = ELS_FDISC;
> +	memset(fdisc->_fl_resvd, 0, sizeof(fdisc->_fl_resvd));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_prli(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	       els_cb_t cb, void *cbarg)
> +{
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *els;
> +	struct {
> +		struct fc_els_prli prli;
> +		struct fc_els_spp spp;
> +	} *pp;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "prli";
> +
> +	/* Build PRLI request */
> +	pp = els->els_req.virt;
> +
> +	memset(pp, 0, sizeof(*pp));
> +
> +	pp->prli.prli_cmd = ELS_PRLI;
> +	pp->prli.prli_spp_len = 16;
> +	pp->prli.prli_len = cpu_to_be16(sizeof(*pp));
> +	pp->spp.spp_type = FC_TYPE_FCP;
> +	pp->spp.spp_type_ext = 0;
> +	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
> +	pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS |
> +			       (node->sport->enable_ini ?
> +			       FCP_SPPF_INIT_FCN : 0) |
> +			       (node->sport->enable_tgt ?
> +			       FCP_SPPF_TARG_FCN : 0));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_prlo(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	       els_cb_t cb, void *cbarg)
> +{
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *els;
> +	struct {
> +		struct fc_els_prlo prlo;
> +		struct fc_els_spp spp;
> +	} *pp;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "prlo";
> +
> +	/* Build PRLO request */
> +	pp = els->els_req.virt;
> +
> +	memset(pp, 0, sizeof(*pp));
> +	pp->prlo.prlo_cmd = ELS_PRLO;
> +	pp->prlo.prlo_obs = 0x10;
> +	pp->prlo.prlo_len = cpu_to_be16(sizeof(*pp));
> +
> +	pp->spp.spp_type = FC_TYPE_FCP;
> +	pp->spp.spp_type_ext = 0;
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_logo(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	       els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct;
> +	struct fc_els_logo *logo;
> +	struct fc_els_flogi  *sparams;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	sparams = (struct fc_els_flogi *)node->sport->service_params;
> +
> +	els = efct_els_io_alloc(node, sizeof(*logo), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "logo";
> +
> +	/* Build LOGO request */
> +
> +	logo = els->els_req.virt;
> +
> +	memset(logo, 0, sizeof(*logo));
> +	logo->fl_cmd = ELS_LOGO;
> +	hton24(logo->fl_n_port_id, node->rnode.sport->fc_id);
> +	logo->fl_n_port_wwn = sparams->fl_wwpn;
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_adisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct;
> +	struct fc_els_adisc *adisc;
> +	struct fc_els_flogi  *sparams;
> +	struct efc_sli_port *sport = node->sport;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	sparams = (struct fc_els_flogi *)node->sport->service_params;
> +
> +	els = efct_els_io_alloc(node, sizeof(*adisc), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "adisc";
> +
> +	/* Build ADISC request */
> +
> +	adisc = els->els_req.virt;
> +
> +	memset(adisc, 0, sizeof(*adisc));
> +	adisc->adisc_cmd = ELS_ADISC;
> +	hton24(adisc->adisc_hard_addr, sport->fc_id);
> +	adisc->adisc_wwpn = sparams->fl_wwpn;
> +	adisc->adisc_wwnn = sparams->fl_wwnn;
> +	hton24(adisc->adisc_port_id, node->rnode.sport->fc_id);
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_pdisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_flogi  *pdisc;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*pdisc), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "pdisc";
> +
> +	pdisc = els->els_req.virt;
> +
> +	memcpy(pdisc, node->sport->service_params, sizeof(*pdisc));
> +
> +	pdisc->fl_cmd = ELS_PDISC;
> +	memset(pdisc->_fl_resvd, 0, sizeof(pdisc->_fl_resvd));
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_scr(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	      els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_scr *req;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*req), EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "scr";
> +
> +	req = els->els_req.virt;
> +
> +	memset(req, 0, sizeof(*req));
> +	req->scr_cmd = ELS_SCR;
> +	req->scr_reg_func = ELS_SCRF_FULL;
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_send_rscn(struct efc_node *node, u32 timeout_sec, u32 retries,
> +	       void *port_ids, u32 port_ids_count, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_rscn *req;
> +	struct fc_els_rscn_page *rscn_page;
> +	u32 length = sizeof(*rscn_page) * port_ids_count;
> +
> +	length += sizeof(*req);
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, length, EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->els_timeout_sec = timeout_sec;
> +	els->els_retries_remaining = retries;
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "rscn";
> +
> +	req = els->els_req.virt;
> +
> +	req->rscn_cmd = ELS_RSCN;
> +	req->rscn_page_len = sizeof(struct fc_els_rscn_page);
> +	req->rscn_plen = cpu_to_be16(length);
> +
> +	els->hio_type = EFCT_HW_ELS_REQ;
> +	els->iparam.els.timeout = timeout_sec;
> +
> +	/* copy in the payload */
> +	rscn_page = els->els_req.virt + sizeof(*req);
> +	memcpy(rscn_page, port_ids,
> +	       port_ids_count * sizeof(*rscn_page));
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +void *
> +efct_send_ls_rjt(struct efc *efc, struct efc_node *node,
> +		 u32 ox_id, u32 reason_code,
> +		u32 reason_code_expl, u32 vendor_unique)
> +{
> +	struct efct_io *io = NULL;
> +	int rc;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_ls_rjt *rjt;
> +
> +	io = efct_els_io_alloc(node, sizeof(*rjt), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	node_els_trace();
> +
> +	io->els_callback = NULL;
> +	io->els_callback_arg = NULL;
> +	io->display_name = "ls_rjt";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +
> +	rjt = io->els_req.virt;
> +	memset(rjt, 0, sizeof(*rjt));
> +
> +	rjt->er_cmd = ELS_LS_RJT;
> +	rjt->er_reason = reason_code;
> +	rjt->er_explan = reason_code_expl;
> +
> +	io->hio_type = EFCT_HW_ELS_RSP;
> +	rc = efct_els_send_rsp(io, sizeof(*rjt));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
> +struct efct_io *
> +efct_send_plogi_acc(struct efc_node *node, u32 ox_id,
> +		    els_cb_t cb, void *cbarg)
> +{
> +	int rc;
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *io = NULL;
> +	struct fc_els_flogi  *plogi;
> +	struct fc_els_flogi  *req = (struct fc_els_flogi *)node->service_params;
> +
> +	node_els_trace();
> +
> +	io = efct_els_io_alloc(node, sizeof(*plogi), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	io->els_callback = cb;
> +	io->els_callback_arg = cbarg;
> +	io->display_name = "plogi_acc";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +
> +	plogi = io->els_req.virt;
> +
> +	/* copy our port's service parameters to payload */
> +	memcpy(plogi, node->sport->service_params, sizeof(*plogi));
> +	plogi->fl_cmd = ELS_LS_ACC;
> +	memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd));
> +
> +	/* Set Application header support bit if requested */
> +	if (req->fl_csp.sp_features & cpu_to_be16(FC_SP_FT_BCAST))
> +		plogi->fl_csp.sp_features |= cpu_to_be16(FC_SP_FT_BCAST);
> +
> +	io->hio_type = EFCT_HW_ELS_RSP;
> +	rc = efct_els_send_rsp(io, sizeof(*plogi));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +	return io;
> +}
> +
> +void *
> +efct_send_flogi_p2p_acc(struct efc *efc, struct efc_node *node,
> +			u32 ox_id, u32 s_id)
> +{
> +	struct efct_io *io = NULL;
> +	int rc;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_flogi  *flogi;
> +
> +	node_els_trace();
> +
> +	io = efct_els_io_alloc(node, sizeof(*flogi), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	io->els_callback = NULL;
> +	io->els_callback_arg = NULL;
> +	io->display_name = "flogi_p2p_acc";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +	io->iparam.els.s_id = s_id;
> +
> +	flogi = io->els_req.virt;
> +
> +	/* copy our port's service parameters to payload */
> +	memcpy(flogi, node->sport->service_params, sizeof(*flogi));
> +	flogi->fl_cmd = ELS_LS_ACC;
> +	memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
> +
> +	memset(flogi->fl_cssp, 0, sizeof(flogi->fl_cssp));
> +
> +	io->hio_type = EFCT_HW_ELS_RSP_SID;
> +	rc = efct_els_send_rsp(io, sizeof(*flogi));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
> +struct efct_io *
> +efct_send_flogi_acc(struct efc_node *node, u32 ox_id, u32 is_fport,
> +		    els_cb_t cb, void *cbarg)
> +{
> +	int rc;
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *io = NULL;
> +	struct fc_els_flogi  *flogi;
> +
> +	node_els_trace();
> +
> +	io = efct_els_io_alloc(node, sizeof(*flogi), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +	io->els_callback = cb;
> +	io->els_callback_arg = cbarg;
> +	io->display_name = "flogi_acc";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +	io->iparam.els.s_id = io->node->sport->fc_id;
> +
> +	flogi = io->els_req.virt;
> +
> +	/* copy our port's service parameters to payload */
> +	memcpy(flogi, node->sport->service_params, sizeof(*flogi));
> +
> +	/* Set F_port */
> +	if (is_fport) {
> +		/* Set F_PORT and Multiple N_PORT_ID Assignment */
> +		flogi->fl_csp.sp_r_a_tov |=  cpu_to_be32(3U << 28);
> +	}
> +
> +	flogi->fl_cmd = ELS_LS_ACC;
> +	memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
> +
> +	memset(flogi->fl_cssp, 0, sizeof(flogi->fl_cssp));
> +
> +	io->hio_type = EFCT_HW_ELS_RSP_SID;
> +	rc = efct_els_send_rsp(io, sizeof(*flogi));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
> +struct efct_io *efct_send_prli_acc(struct efc_node *node,
> +				     u32 ox_id, els_cb_t cb, void *cbarg)
> +{
> +	int rc;
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *io = NULL;
> +	struct {
> +		struct fc_els_prli prli;
> +		struct fc_els_spp spp;
> +	} *pp;
> +
> +	node_els_trace();
> +
> +	io = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	io->els_callback = cb;
> +	io->els_callback_arg = cbarg;
> +	io->display_name = "prli_acc";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +
> +	pp = io->els_req.virt;
> +	memset(pp, 0, sizeof(*pp));
> +
> +	pp->prli.prli_cmd = ELS_LS_ACC;
> +	pp->prli.prli_spp_len = 0x10;
> +	pp->prli.prli_len = cpu_to_be16(sizeof(*pp));
> +	pp->spp.spp_type = FC_TYPE_FCP;
> +	pp->spp.spp_type_ext = 0;
> +	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR | FC_SPP_RESP_ACK;
> +
> +	pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS |
> +					(node->sport->enable_ini ?
> +					 FCP_SPPF_INIT_FCN : 0) |
> +					(node->sport->enable_tgt ?
> +					 FCP_SPPF_TARG_FCN : 0));
> +
> +	io->hio_type = EFCT_HW_ELS_RSP;
> +	rc = efct_els_send_rsp(io, sizeof(*pp));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
> +struct efct_io *
> +efct_send_prlo_acc(struct efc_node *node, u32 ox_id,
> +		   els_cb_t cb, void *cbarg)
> +{
> +	int rc;
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *io = NULL;
> +	struct {
> +		struct fc_els_prlo prlo;
> +		struct fc_els_spp spp;
> +	} *pp;
> +
> +	node_els_trace();
> +
> +	io = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	io->els_callback = cb;
> +	io->els_callback_arg = cbarg;
> +	io->display_name = "prlo_acc";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +
> +	pp = io->els_req.virt;
> +	memset(pp, 0, sizeof(*pp));
> +	pp->prlo.prlo_cmd = ELS_LS_ACC;
> +	pp->prlo.prlo_obs = 0x10;
> +	pp->prlo.prlo_len = cpu_to_be16(sizeof(*pp));
> +
> +	pp->spp.spp_type = FC_TYPE_FCP;
> +	pp->spp.spp_type_ext = 0;
> +	pp->spp.spp_flags = FC_SPP_RESP_ACK;
> +
> +	io->hio_type = EFCT_HW_ELS_RSP;
> +	rc = efct_els_send_rsp(io, sizeof(*pp));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
> +struct efct_io *
> +efct_send_ls_acc(struct efc_node *node, u32 ox_id, els_cb_t cb,
> +		 void *cbarg)
> +{
> +	int rc;
> +	struct efct *efct = node->efc->base;
> +	struct efct_io *io = NULL;
> +	struct fc_els_ls_acc *acc;
> +
> +	node_els_trace();
> +
> +	io = efct_els_io_alloc(node, sizeof(*acc), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	io->els_callback = cb;
> +	io->els_callback_arg = cbarg;
> +	io->display_name = "ls_acc";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +
> +	acc = io->els_req.virt;
> +	memset(acc, 0, sizeof(*acc));
> +
> +	acc->la_cmd = ELS_LS_ACC;
> +
> +	io->hio_type = EFCT_HW_ELS_RSP;
> +	rc = efct_els_send_rsp(io, sizeof(*acc));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
> +struct efct_io *
> +efct_send_logo_acc(struct efc_node *node, u32 ox_id,
> +		   els_cb_t cb, void *cbarg)
> +{
> +	int rc;
> +	struct efct_io *io = NULL;
> +	struct efct *efct = node->efc->base;
> +	struct fc_els_ls_acc *logo;
> +
> +	node_els_trace();
> +
> +	io = efct_els_io_alloc(node, sizeof(*logo), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	io->els_callback = cb;
> +	io->els_callback_arg = cbarg;
> +	io->display_name = "logo_acc";
> +	io->init_task_tag = ox_id;
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +
> +	logo = io->els_req.virt;
> +	memset(logo, 0, sizeof(*logo));
> +
> +	logo->la_cmd = ELS_LS_ACC;
> +
> +	io->hio_type = EFCT_HW_ELS_RSP;
> +	rc = efct_els_send_rsp(io, sizeof(*logo));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
> +struct efct_io *
> +efct_send_adisc_acc(struct efc_node *node, u32 ox_id,
> +		    els_cb_t cb, void *cbarg)
> +{
> +	int rc;
> +	struct efct_io *io = NULL;
> +	struct fc_els_adisc *adisc;
> +	struct fc_els_flogi  *sparams;
> +	struct efct *efct;
> +
> +	efct = node->efc->base;
> +
> +	node_els_trace();
> +
> +	io = efct_els_io_alloc(node, sizeof(*adisc), EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efct, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	io->els_callback = cb;
> +	io->els_callback_arg = cbarg;
> +	io->display_name = "adisc_acc";
> +	io->init_task_tag = ox_id;
> +
> +	/* Go ahead and send the ELS_ACC */
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.els.ox_id = ox_id;
> +
> +	sparams = (struct fc_els_flogi  *)node->sport->service_params;
> +	adisc = io->els_req.virt;
> +	memset(adisc, 0, sizeof(*adisc));
> +	adisc->adisc_cmd = ELS_LS_ACC;
> +	adisc->adisc_wwpn = sparams->fl_wwpn;
> +	adisc->adisc_wwnn = sparams->fl_wwnn;
> +	hton24(adisc->adisc_port_id, node->rnode.sport->fc_id);
> +
> +	io->hio_type = EFCT_HW_ELS_RSP;
> +	rc = efct_els_send_rsp(io, sizeof(*adisc));
> +	if (rc) {
> +		efct_els_io_free(io);
> +		io = NULL;
> +	}
> +
> +	return io;
> +}
> +
> +void *
> +efct_els_send_ct(struct efc *efc, struct efc_node *node, u32 cmd,
> +		 u32 timeout_sec, u32 retries)
> +{
> +	struct efct *efct = efc->base;
> +
> +	switch (cmd) {
> +	case FC_RCTL_ELS_REQ:
> +		efct_ns_send_rftid(node, timeout_sec, retries, NULL, NULL);
> +		break;
> +	case FC_NS_RFF_ID:
> +		efct_ns_send_rffid(node, timeout_sec, retries, NULL, NULL);
> +		break;
> +	case FC_NS_GID_PT:
> +		efct_ns_send_gidpt(node, timeout_sec, retries, NULL, NULL);
> +		break;
> +	default:
> +		efc_log_err(efct, "Unhandled command cmd: %x\n", cmd);
> +	}
> +
> +	return NULL;
> +}
> +
> +static inline void fcct_build_req_header(struct fc_ct_hdr  *hdr,
> +					 u16 cmd, u16 max_size)
> +{
> +	hdr->ct_rev = FC_CT_REV;
> +	hdr->ct_fs_type = FC_FST_DIR;
> +	hdr->ct_fs_subtype = FC_NS_SUBTYPE;
> +	hdr->ct_options = 0;
> +	hdr->ct_cmd = cpu_to_be16(cmd);
> +	/* words */
> +	hdr->ct_mr_size = cpu_to_be16(max_size / (sizeof(u32)));
> +	hdr->ct_reason = 0;
> +	hdr->ct_explan = 0;
> +	hdr->ct_vendor = 0;
> +}
> +
> +struct efct_io *
> +efct_ns_send_rftid(struct efc_node *node, u32 timeout_sec,
> +		   u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_ct_hdr *ct;
> +	struct fc_ns_rft_id *rftid;
> +
> +	node_els_trace();
> +
> +	els = efct_els_io_alloc(node, sizeof(*ct) + sizeof(*rftid),
> +				EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +
> +	els->iparam.fc_ct.r_ctl = FC_RCTL_ELS_REQ;
> +	els->iparam.fc_ct.type = FC_TYPE_CT;
> +	els->iparam.fc_ct.df_ctl = 0;
> +	els->iparam.fc_ct.timeout = timeout_sec;
> +
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "rftid";
> +
> +	ct = els->els_req.virt;
> +	memset(ct, 0, sizeof(*ct));
> +	fcct_build_req_header(ct, FC_NS_RFT_ID, sizeof(*rftid));
> +
> +	rftid = els->els_req.virt + sizeof(*ct);
> +	memset(rftid, 0, sizeof(*rftid));
> +	hton24(rftid->fr_fid.fp_fid, node->rnode.sport->fc_id);
> +	rftid->fr_fts.ff_type_map[FC_TYPE_FCP / FC_NS_BPW] =
> +		cpu_to_be32(1 << (FC_TYPE_FCP % FC_NS_BPW));
> +
> +	els->hio_type = EFCT_HW_FC_CT;
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_ns_send_rffid(struct efc_node *node, u32 timeout_sec,
> +		   u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els;
> +	struct efct *efct = node->efc->base;
> +	struct fc_ct_hdr *ct;
> +	struct fc_ns_rff_id *rffid;
> +	u32 size = 0;
> +
> +	node_els_trace();
> +
> +	size = sizeof(*ct) + sizeof(*rffid);
> +
> +	els = efct_els_io_alloc(node, size, EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +	els->iparam.fc_ct.r_ctl = FC_RCTL_ELS_REQ;
> +	els->iparam.fc_ct.type = FC_TYPE_CT;
> +	els->iparam.fc_ct.df_ctl = 0;
> +	els->iparam.fc_ct.timeout = timeout_sec;
> +
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "rffid";
> +	ct = els->els_req.virt;
> +
> +	memset(ct, 0, sizeof(*ct));
> +	fcct_build_req_header(ct, FC_NS_RFF_ID, sizeof(*rffid));
> +
> +	rffid = els->els_req.virt + sizeof(*ct);
> +	memset(rffid, 0, sizeof(*rffid));
> +
> +	hton24(rffid->fr_fid.fp_fid, node->rnode.sport->fc_id);
> +	if (node->sport->enable_ini)
> +		rffid->fr_feat |= FCP_FEAT_INIT;
> +	if (node->sport->enable_tgt)
> +		rffid->fr_feat |= FCP_FEAT_TARG;
> +	rffid->fr_type = FC_TYPE_FCP;
> +
> +	els->hio_type = EFCT_HW_FC_CT;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +struct efct_io *
> +efct_ns_send_gidpt(struct efc_node *node, u32 timeout_sec,
> +		   u32 retries, els_cb_t cb, void *cbarg)
> +{
> +	struct efct_io *els = NULL;
> +	struct efct *efct = node->efc->base;
> +	struct fc_ct_hdr *ct;
> +	struct fc_ns_gid_pt *gidpt;
> +	u32 size = 0;
> +
> +	node_els_trace();
> +
> +	size = sizeof(*ct) + sizeof(*gidpt);
> +	els = efct_els_io_alloc_size(node, size,
> +				     EFCT_ELS_GID_PT_RSP_LEN,
> +				   EFCT_ELS_ROLE_ORIGINATOR);
> +	if (!els) {
> +		efc_log_err(efct, "IO alloc failed\n");
> +		return els;
> +	}
> +
> +	els->iparam.fc_ct.r_ctl = FC_RCTL_ELS_REQ;
> +	els->iparam.fc_ct.type = FC_TYPE_CT;
> +	els->iparam.fc_ct.df_ctl = 0;
> +	els->iparam.fc_ct.timeout = timeout_sec;
> +
> +	els->els_callback = cb;
> +	els->els_callback_arg = cbarg;
> +	els->display_name = "gidpt";
> +
> +	ct = els->els_req.virt;
> +
> +	memset(ct, 0, sizeof(*ct));
> +	fcct_build_req_header(ct, FC_NS_GID_PT, sizeof(*gidpt));
> +
> +	gidpt = els->els_req.virt + sizeof(*ct);
> +	memset(gidpt, 0, sizeof(*gidpt));
> +	gidpt->fn_pt_type = FC_TYPE_FCP;
> +
> +	els->hio_type = EFCT_HW_FC_CT;
> +
> +	efct_els_send_req(node, els);
> +
> +	return els;
> +}
> +
> +static int efct_bls_send_rjt_cb(struct efct_hw_io *hio,
> +				struct efc_remote_node *rnode, u32 length,
> +		int status, u32 ext_status, void *app)
> +{
> +	struct efct_io *io = app;
> +
> +	efct_scsi_io_free(io);
> +	return EFC_SUCCESS;
> +}
> +
> +static struct efct_io *
> +efct_bls_send_rjt(struct efct_io *io, u32 s_id,
> +		  u16 ox_id, u16 rx_id)
> +{
> +	struct efc_node *node = io->node;
> +	int rc;
> +	struct fc_ba_rjt *acc;
> +	struct efct *efct;
> +
> +	efct = node->efc->base;
> +
> +	if (node->rnode.sport->fc_id == s_id)
> +		s_id = U32_MAX;
> +
> +	/* fill out generic fields */
> +	io->efct = efct;
> +	io->node = node;
> +	io->cmd_tgt = true;
> +
> +	/* fill out BLS Response-specific fields */
> +	io->io_type = EFCT_IO_TYPE_BLS_RESP;
> +	io->display_name = "ba_rjt";
> +	io->hio_type = EFCT_HW_BLS_RJT;
> +	io->init_task_tag = ox_id;
> +
> +	/* fill out iparam fields */
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.bls.ox_id = ox_id;
> +	io->iparam.bls.rx_id = rx_id;
> +
> +	acc = (void *)io->iparam.bls.payload;
> +
> +	memset(io->iparam.bls.payload, 0,
> +	       sizeof(io->iparam.bls.payload));
> +	acc->br_reason = ELS_RJT_UNAB;
> +	acc->br_explan = ELS_EXPL_NONE;
> +
> +	rc = efct_scsi_io_dispatch(io, efct_bls_send_rjt_cb);
> +	if (rc) {
> +		efc_log_err(efct, "efct_scsi_io_dispatch() failed: %d\n", rc);
> +		efct_scsi_io_free(io);
> +		io = NULL;
> +	}
> +	return io;
> +}
> +
> +struct efct_io *
> +efct_bls_send_rjt_hdr(struct efct_io *io, struct fc_frame_header *hdr)
> +{
> +	u16 ox_id = be16_to_cpu(hdr->fh_ox_id);
> +	u16 rx_id = be16_to_cpu(hdr->fh_rx_id);
> +	u32 d_id = ntoh24(hdr->fh_d_id);
> +
> +	return efct_bls_send_rjt(io, d_id, ox_id, rx_id);
> +}
> +
> +static int efct_bls_send_acc_cb(struct efct_hw_io *hio,
> +				struct efc_remote_node *rnode, u32 length,
> +		int status, u32 ext_status, void *app)
> +{
> +	struct efct_io *io = app;
> +
> +	efct_scsi_io_free(io);
> +	return EFC_SUCCESS;
> +}
> +
> +static struct efct_io *
> +efct_bls_send_acc(struct efct_io *io, u32 s_id,
> +		  u16 ox_id, u16 rx_id)
> +{
> +	struct efc_node *node = io->node;
> +	int rc;
> +	struct fc_ba_acc *acc;
> +	struct efct *efct;
> +
> +	efct = node->efc->base;
> +
> +	if (node->rnode.sport->fc_id == s_id)
> +		s_id = U32_MAX;
> +
> +	/* fill out generic fields */
> +	io->efct = efct;
> +	io->node = node;
> +	io->cmd_tgt = true;
> +
> +	/* fill out BLS Response-specific fields */
> +	io->io_type = EFCT_IO_TYPE_BLS_RESP;
> +	io->display_name = "ba_acc";
> +	io->hio_type = EFCT_HW_BLS_ACC_SID;
> +	io->init_task_tag = ox_id;
> +
> +	/* fill out iparam fields */
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +	io->iparam.bls.s_id = s_id;
> +	io->iparam.bls.ox_id = ox_id;
> +	io->iparam.bls.rx_id = rx_id;
> +
> +	acc = (void *)io->iparam.bls.payload;
> +
> +	memset(io->iparam.bls.payload, 0,
> +	       sizeof(io->iparam.bls.payload));
> +	acc->ba_ox_id = cpu_to_be16(io->iparam.bls.ox_id);
> +	acc->ba_rx_id = cpu_to_be16(io->iparam.bls.rx_id);
> +	acc->ba_high_seq_cnt = cpu_to_be16(U16_MAX);
> +
> +	rc = efct_scsi_io_dispatch(io, efct_bls_send_acc_cb);
> +	if (rc) {
> +		efc_log_err(efct, "efct_scsi_io_dispatch() failed: %d\n", rc);
> +		efct_scsi_io_free(io);
> +		io = NULL;
> +	}
> +	return io;
> +}
> +
> +void *
> +efct_bls_send_acc_hdr(struct efc *efc, struct efc_node *node,
> +		      struct fc_frame_header *hdr)
> +{
> +	struct efct_io *io = NULL;
> +	u16 ox_id = be16_to_cpu(hdr->fh_ox_id);
> +	u16 rx_id = be16_to_cpu(hdr->fh_rx_id);
> +	u32 d_id = ntoh24(hdr->fh_d_id);
> +
> +	io = efct_scsi_io_alloc(node, EFCT_SCSI_IO_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efc, "els IO alloc failed\n");
> +		return io;
> +	}
> +
> +	return efct_bls_send_acc(io, d_id, ox_id, rx_id);
> +}
> +
> +static int
> +efct_els_abort_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
> +		  u32 length, int status, u32 ext_status,
> +		 void *app)
> +{
> +	struct efct_io *els;
> +	struct efct_io *abort_io = NULL; /* IO structure used to abort ELS */
> +	struct efct *efct;
> +
> +	abort_io = app;
> +	els = abort_io->io_to_abort;
> +
> +	if (!els || !els->node || !els->node->efc)
> +		return EFC_FAIL;
> +
> +	efct = els->node->efc->base;
> +
> +	if (status != 0)
> +		efc_log_warn(efct, "status x%x ext x%x\n", status, ext_status);
> +
> +	/* now free the abort IO */
> +	efct_io_pool_io_free(efct->xport->io_pool, abort_io);
> +
> +	/* send completion event to indicate abort process is complete
> +	 * Note: The ELS SM will already be receiving
> +	 * ELS_REQ_OK/FAIL/RJT/ABORTED
> +	 */
> +	if (els->state == EFCT_ELS_REQ_ABORTED) {
> +		/* completion for ELS that was aborted */
> +		efct_els_abort_cleanup(els);
> +	} else {
> +		/* completion for abort was received first,
> +		 * transition to wait for req cmpl
> +		 */
> +		els->state = EFCT_ELS_ABORT_IO_COMPL;
> +	}
> +
> +	/* done with ELS IO to abort */
> +	kref_put(&els->ref, els->release);
> +	return EFC_SUCCESS;
> +}
> +
> +static struct efct_io *
> +efct_els_abort_io(struct efct_io *els, bool send_abts)
> +{
> +	struct efct *efct;
> +	struct efct_xport *xport;
> +	int rc;
> +	struct efct_io *abort_io = NULL;
> +
> +	efct = els->node->efc->base;
> +	xport = efct->xport;
> +
> +	/* take a reference on IO being aborted */
> +	if ((kref_get_unless_zero(&els->ref) == 0)) {
> +		/* command no longer active */
> +		efc_log_debug(efct, "els no longer active\n");
> +		return NULL;
> +	}
> +
> +	/* allocate IO structure to send abort */
> +	abort_io = efct_io_pool_io_alloc(efct->xport->io_pool);
> +	if (!abort_io) {
> +		atomic_add_return(1, &xport->io_alloc_failed_count);
> +	} else {
> +		/* set generic fields */
> +		abort_io->efct = efct;
> +		abort_io->node = els->node;
> +		abort_io->cmd_ini = true;
> +
> +		/* set type and ABORT-specific fields */
> +		abort_io->io_type = EFCT_IO_TYPE_ABORT;
> +		abort_io->display_name = "abort_els";
> +		abort_io->io_to_abort = els;
> +		abort_io->send_abts = send_abts;
> +
> +		/* now dispatch IO */
> +		rc = efct_scsi_io_dispatch_abort(abort_io, efct_els_abort_cb);
> +		if (rc) {
> +			efc_log_err(efct,
> +				     "efct_scsi_io_dispatch failed: %d\n", rc);
> +			efct_io_pool_io_free(efct->xport->io_pool, abort_io);
> +			abort_io = NULL;
> +		}
> +	}
> +
> +	/* if something failed, put reference on ELS to abort */
> +	if (!abort_io)
> +		kref_put(&els->ref, els->release);
> +	return abort_io;
> +}
> +
> +void
> +efct_els_abort(struct efct_io *els, struct efc_node_cb *arg)
> +{
> +	struct efct_io *io = NULL;
> +	struct efc_node *node;
> +	struct efct *efct;
> +
> +	node = els->node;
> +	efct = node->efc->base;
> +
> +	/* request to abort this ELS without an ABTS */
> +	els_io_printf(els, "ELS abort requested\n");
> +	/* Set retries to zero,we are done */
> +	els->els_retries_remaining = 0;
> +	if (els->state == EFCT_ELS_REQUEST) {
> +		els->state = EFCT_ELS_REQ_ABORT;
> +		io = efct_els_abort_io(els, false);
> +		if (!io) {
> +			efc_log_err(efct, "efct_els_abort_io failed\n");
> +			efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
> +					    arg);
> +		}
> +
> +	} else if (els->state == EFCT_ELS_REQUEST_DELAYED) {
> +		/* mod/resched the timer for a short duration */
> +		mod_timer(&els->delay_timer,
> +			  jiffies + msecs_to_jiffies(1));
> +
> +		els->state = EFCT_ELS_REQUEST_DELAY_ABORT;
> +	}
> +}
> +
> +void
> +efct_els_io_cleanup(struct efct_io *els,
> +		    enum efc_hw_node_els_event node_evt, void *arg)
> +{
> +	/* don't want further events that could come; e.g. abort requests
> +	 * from the node state machine; thus, disable state machine
> +	 */
> +	els->els_req_free = true;
> +	efc_node_post_els_resp(els->node, node_evt, arg);
> +
> +	/* If this IO has a callback, invoke it */
> +	if (els->els_callback) {
> +		(*els->els_callback)(els->node, arg,
> +				    els->els_callback_arg);
> +	}
> +	efct_els_io_free(els);
> +}
> +
> +int
> +efct_els_io_list_empty(struct efc_node *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;
> +}
> +
> +static int
> +efct_ct_acc_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
> +	       u32 length, int status, u32 ext_status,
> +	      void *arg)
> +{
> +	struct efct_io *io = arg;
> +
> +	efct_els_io_free(io);
> +
> +	return EFC_SUCCESS;
> +}
> +
> +int
> +efct_send_ct_rsp(struct efc *efc, struct efc_node *node, u16 ox_id,
> +		 struct fc_ct_hdr  *ct_hdr, u32 cmd_rsp_code,
> +		u32 reason_code, u32 reason_code_explanation)
> +{
> +	struct efct_io *io = NULL;
> +	struct fc_ct_hdr  *rsp = NULL;
> +
> +	io = efct_els_io_alloc(node, 256, EFCT_ELS_ROLE_RESPONDER);
> +	if (!io) {
> +		efc_log_err(efc, "IO alloc failed\n");
> +		return EFC_FAIL;
> +	}
> +
> +	rsp = io->els_rsp.virt;
> +	io->io_type = EFCT_IO_TYPE_CT_RESP;
> +
> +	*rsp = *ct_hdr;
> +
> +	fcct_build_req_header(rsp, cmd_rsp_code, 0);
> +	rsp->ct_reason = reason_code;
> +	rsp->ct_explan = reason_code_explanation;
> +
> +	io->display_name = "ct_rsp";
> +	io->init_task_tag = ox_id;
> +	io->wire_len += sizeof(*rsp);
> +
> +	memset(&io->iparam, 0, sizeof(io->iparam));
> +
> +	io->io_type = EFCT_IO_TYPE_CT_RESP;
> +	io->hio_type = EFCT_HW_FC_CT_RSP;
> +	io->iparam.fc_ct.ox_id = ox_id;
> +	io->iparam.fc_ct.r_ctl = 3;
> +	io->iparam.fc_ct.type = FC_TYPE_CT;
> +	io->iparam.fc_ct.df_ctl = 0;
> +	io->iparam.fc_ct.timeout = 5;
> +
> +	if (efct_scsi_io_dispatch(io, efct_ct_acc_cb) < 0) {
> +		efct_els_io_free(io);
> +		return EFC_FAIL;
> +	}
> +	return EFC_SUCCESS;
> +}
> diff --git a/drivers/scsi/elx/efct/efct_els.h b/drivers/scsi/elx/efct/efct_els.h
> new file mode 100644
> index 000000000000..9b79783a39a3
> --- /dev/null
> +++ b/drivers/scsi/elx/efct/efct_els.h
> @@ -0,0 +1,133 @@
> +/* 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(__EFCT_ELS_H__)
> +#define __EFCT_ELS_H__
> +
> +enum efct_els_role {
> +	EFCT_ELS_ROLE_ORIGINATOR,
> +	EFCT_ELS_ROLE_RESPONDER,
> +};
> +
> +void _efct_els_io_free(struct kref *arg);
> +extern struct efct_io *
> +efct_els_io_alloc(struct efc_node *node, u32 reqlen,
> +		  enum efct_els_role role);
> +extern struct efct_io *
> +efct_els_io_alloc_size(struct efc_node *node, u32 reqlen,
> +		       u32 rsplen,
> +				       enum efct_els_role role);
> +void efct_els_io_free(struct efct_io *els);
> +
> +extern void *
> +efct_els_req_send(struct efc *efc, struct efc_node *node,
> +		  u32 cmd, u32 timeout_sec, u32 retries);
> +extern void *
> +efct_els_send_ct(struct efc *efc, struct efc_node *node,
> +		 u32 cmd, u32 timeout_sec, u32 retries);
> +extern void *
> +efct_els_resp_send(struct efc *efc, struct efc_node *node,
> +		   u32 cmd, u16 ox_id);
> +void
> +efct_els_abort(struct efct_io *els, struct efc_node_cb *arg);
> +/* ELS command send */
> +typedef void (*els_cb_t)(struct efc_node *node,
> +			 struct efc_node_cb *cbdata, void *arg);
> +extern struct efct_io *
> +efct_send_plogi(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_flogi(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_fdisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_prli(struct efc_node *node, u32 timeout_sec,
> +	       u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_prlo(struct efc_node *node, u32 timeout_sec,
> +	       u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_logo(struct efc_node *node, u32 timeout_sec,
> +	       u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_adisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_pdisc(struct efc_node *node, u32 timeout_sec,
> +		u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_scr(struct efc_node *node, u32 timeout_sec,
> +	      u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_ns_send_rftid(struct efc_node *node,
> +		   u32 timeout_sec,
> +		  u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_ns_send_rffid(struct efc_node *node,
> +		   u32 timeout_sec,
> +		  u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_ns_send_gidpt(struct efc_node *node, u32 timeout_sec,
> +		   u32 retries, els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_rscn(struct efc_node *node, u32 timeout_sec,
> +	       u32 retries, void *port_ids,
> +	      u32 port_ids_count, els_cb_t cb, void *cbarg);
> +extern void
> +efct_els_io_cleanup(struct efct_io *els, enum efc_hw_node_els_event,
> +		    void *arg);
> +
> +/* ELS acc send */
> +extern struct efct_io *
> +efct_send_ls_acc(struct efc_node *node, u32 ox_id,
> +		 els_cb_t cb, void *cbarg);
> +
> +extern void *
> +efct_send_ls_rjt(struct efc *efc, struct efc_node *node, u32 ox_id,
> +		 u32 reason_cod, u32 reason_code_expl,
> +		u32 vendor_unique);
> +extern void *
> +efct_send_flogi_p2p_acc(struct efc *efc, struct efc_node *node,
> +			u32 ox_id, u32 s_id);
> +extern struct efct_io *
> +efct_send_flogi_acc(struct efc_node *node, u32 ox_id,
> +		    u32 is_fport, els_cb_t cb,
> +		   void *cbarg);
> +extern struct efct_io *
> +efct_send_plogi_acc(struct efc_node *node, u32 ox_id,
> +		    els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_prli_acc(struct efc_node *node, u32 ox_id,
> +		   els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_logo_acc(struct efc_node *node, u32 ox_id,
> +		   els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_prlo_acc(struct efc_node *node, u32 ox_id,
> +		   els_cb_t cb, void *cbarg);
> +extern struct efct_io *
> +efct_send_adisc_acc(struct efc_node *node, u32 ox_id,
> +		    els_cb_t cb, void *cbarg);
> +
> +extern void *
> +efct_bls_send_acc_hdr(struct efc *efc, struct efc_node *node,
> +		      struct fc_frame_header *hdr);
> +extern struct efct_io *
> +efct_bls_send_rjt_hdr(struct efct_io *io, struct fc_frame_header *hdr);
> +
> +extern int
> +efct_els_io_list_empty(struct efc_node *node, struct list_head *list);
> +
> +/* CT */
> +extern int
> +efct_send_ct_rsp(struct efc *efc, struct efc_node *node, u16 ox_id,
> +		 struct fc_ct_hdr *ct_hdr,
> +		 u32 cmd_rsp_code, u32 reason_code,
> +		 u32 reason_code_explanation);
> +
> +#endif /* __EFCT_ELS_H__ */
> -- 
> 2.16.4
> 

Thanks,
Daniel
James Smart April 23, 2020, 3:30 a.m. UTC | #3
On 4/16/2020 12:58 AM, Hannes Reinecke wrote:
...
> That is a bit strange.
> Sending a response can fail, but (apparently) sending a request cannot; 
> at the very least you don't have (or check) the return value from the 
> send request.
> 
> Some intricate scheme I've missed?
> 

The send_request fully handles the failure case, which ends up calling 
the request completion callback with an error status, before 
send_request returns to the callee.   Not handled that way with the 
response. The response routine handles the error.

Agree with your other comments and will address them.
Same thing for comments on patches 19 and 20.

-- james
diff mbox series

Patch

diff --git a/drivers/scsi/elx/efct/efct_els.c b/drivers/scsi/elx/efct/efct_els.c
new file mode 100644
index 000000000000..8a2598a83445
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_els.c
@@ -0,0 +1,1928 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * Functions to build and send ELS/CT/BLS commands and responses.
+ */
+
+#include "efct_driver.h"
+#include "efct_els.h"
+
+#define ELS_IOFMT "[i:%04x t:%04x h:%04x]"
+
+#define EFCT_LOG_ENABLE_ELS_TRACE(efct)		\
+		(((efct) != NULL) ? (((efct)->logmask & (1U << 1)) != 0) : 0)
+
+#define node_els_trace()  \
+	do { \
+		if (EFCT_LOG_ENABLE_ELS_TRACE(efct)) \
+			efc_log_info(efct, "[%s] %-20s\n", \
+				node->display_name, __func__); \
+	} while (0)
+
+#define els_io_printf(els, fmt, ...) \
+	efc_log_debug((struct efct *)els->node->efc->base,\
+		      "[%s]" ELS_IOFMT " %-8s " fmt, \
+		      els->node->display_name,\
+		      els->init_task_tag, els->tgt_task_tag, els->hw_tag,\
+		      els->display_name, ##__VA_ARGS__)
+
+#define EFCT_ELS_RSP_LEN		1024
+#define EFCT_ELS_GID_PT_RSP_LEN		8096
+
+static char *cmd_name[] = FC_ELS_CMDS_INIT;
+
+void *
+efct_els_req_send(struct efc *efc, struct efc_node *node, u32 cmd,
+		  u32 timeout_sec, u32 retries)
+{
+	struct efct *efct = efc->base;
+
+	efc_log_debug(efct, "send %s\n", cmd_name[cmd]);
+
+	switch (cmd) {
+	case ELS_PLOGI:
+		return efct_send_plogi(node, timeout_sec, retries, NULL, NULL);
+	case ELS_FLOGI:
+		return efct_send_flogi(node, timeout_sec, retries, NULL, NULL);
+	case ELS_FDISC:
+		return efct_send_fdisc(node, timeout_sec, retries, NULL, NULL);
+	case ELS_LOGO:
+		return efct_send_logo(node, timeout_sec, retries, NULL, NULL);
+	case ELS_PRLI:
+		return efct_send_prli(node, timeout_sec, retries, NULL, NULL);
+	case ELS_ADISC:
+		return efct_send_adisc(node, timeout_sec, retries, NULL, NULL);
+	case ELS_SCR:
+		return efct_send_scr(node, timeout_sec, retries, NULL, NULL);
+	default:
+		efc_log_err(efct, "Unhandled command cmd: %x\n", cmd);
+	}
+
+	return NULL;
+}
+
+void *
+efct_els_resp_send(struct efc *efc, struct efc_node *node,
+		   u32 cmd, u16 ox_id)
+{
+	struct efct *efct = efc->base;
+
+	switch (cmd) {
+	case ELS_PLOGI:
+		efct_send_plogi_acc(node, ox_id, NULL, NULL);
+		break;
+	case ELS_FLOGI:
+		efct_send_flogi_acc(node, ox_id, 0, NULL, NULL);
+		break;
+	case ELS_LOGO:
+		efct_send_logo_acc(node, ox_id, NULL, NULL);
+		break;
+	case ELS_PRLI:
+		efct_send_prli_acc(node, ox_id, NULL, NULL);
+		break;
+	case ELS_PRLO:
+		efct_send_prlo_acc(node, ox_id, NULL, NULL);
+		break;
+	case ELS_ADISC:
+		efct_send_adisc_acc(node, ox_id, NULL, NULL);
+		break;
+	case ELS_LS_ACC:
+		efct_send_ls_acc(node, ox_id, NULL, NULL);
+		break;
+	case ELS_PDISC:
+	case ELS_FDISC:
+	case ELS_RSCN:
+	case ELS_SCR:
+		efct_send_ls_rjt(efc, node, ox_id, ELS_RJT_UNAB,
+				 ELS_EXPL_NONE, 0);
+		break;
+	default:
+		efc_log_err(efct, "Unhandled command cmd: %x\n", cmd);
+	}
+
+	return NULL;
+}
+
+struct efct_io *
+efct_els_io_alloc(struct efc_node *node, u32 reqlen,
+		  enum efct_els_role role)
+{
+	return efct_els_io_alloc_size(node, reqlen, EFCT_ELS_RSP_LEN, role);
+}
+
+struct efct_io *
+efct_els_io_alloc_size(struct efc_node *node, u32 reqlen,
+		       u32 rsplen, enum efct_els_role role)
+{
+	struct efct *efct;
+	struct efct_xport *xport;
+	struct efct_io *els;
+	unsigned long flags = 0;
+
+	efct = node->efc->base;
+
+	xport = efct->xport;
+
+	spin_lock_irqsave(&node->active_ios_lock, flags);
+
+	if (!node->io_alloc_enabled) {
+		efc_log_debug(efct,
+			       "called with io_alloc_enabled = FALSE\n");
+		spin_unlock_irqrestore(&node->active_ios_lock, flags);
+		return NULL;
+	}
+
+	els = efct_io_pool_io_alloc(efct->xport->io_pool);
+	if (!els) {
+		atomic_add_return(1, &xport->io_alloc_failed_count);
+		spin_unlock_irqrestore(&node->active_ios_lock, flags);
+		return NULL;
+	}
+
+	/* initialize refcount */
+	kref_init(&els->ref);
+	els->release = _efct_els_io_free;
+
+	switch (role) {
+	case EFCT_ELS_ROLE_ORIGINATOR:
+		els->cmd_ini = true;
+		els->cmd_tgt = false;
+		break;
+	case EFCT_ELS_ROLE_RESPONDER:
+		els->cmd_ini = false;
+		els->cmd_tgt = true;
+		break;
+	}
+
+	/* IO should not have an associated HW IO yet.
+	 * Assigned below.
+	 */
+	if (els->hio) {
+		efc_log_err(efct, "Error: HW io not null hio:%p\n", els->hio);
+		efct_io_pool_io_free(efct->xport->io_pool, els);
+		spin_unlock_irqrestore(&node->active_ios_lock, flags);
+		return NULL;
+	}
+
+	/* populate generic io fields */
+	els->efct = efct;
+	els->node = node;
+
+	/* set type and ELS-specific fields */
+	els->io_type = EFCT_IO_TYPE_ELS;
+	els->display_name = "pending";
+
+	/* now allocate DMA for request and response */
+	els->els_req.size = reqlen;
+	els->els_req.virt = dma_alloc_coherent(&efct->pcidev->dev,
+					       els->els_req.size,
+					       &els->els_req.phys,
+					       GFP_DMA);
+	if (els->els_req.virt) {
+		els->els_rsp.size = rsplen;
+		els->els_rsp.virt = dma_alloc_coherent(&efct->pcidev->dev,
+						       els->els_rsp.size,
+						       &els->els_rsp.phys,
+						       GFP_DMA);
+		if (!els->els_rsp.virt) {
+			efc_log_err(efct, "dma_alloc rsp\n");
+			dma_free_coherent(&efct->pcidev->dev,
+					  els->els_req.size,
+				els->els_req.virt, els->els_req.phys);
+			memset(&els->els_req, 0, sizeof(struct efc_dma));
+			efct_io_pool_io_free(efct->xport->io_pool, els);
+			els = NULL;
+		}
+	} else {
+		efc_log_err(efct, "dma_alloc req\n");
+		efct_io_pool_io_free(efct->xport->io_pool, els);
+		els = NULL;
+	}
+
+	if (els) {
+		/* initialize fields */
+		els->els_retries_remaining =
+					EFCT_FC_ELS_DEFAULT_RETRIES;
+		els->els_pend = false;
+		els->els_active = false;
+
+		/* add els structure to ELS IO list */
+		INIT_LIST_HEAD(&els->list_entry);
+		list_add_tail(&els->list_entry,
+			      &node->els_io_pend_list);
+		els->els_pend = true;
+	}
+
+	spin_unlock_irqrestore(&node->active_ios_lock, flags);
+	return els;
+}
+
+void
+efct_els_io_free(struct efct_io *els)
+{
+	kref_put(&els->ref, els->release);
+}
+
+void
+_efct_els_io_free(struct kref *arg)
+{
+	struct efct_io *els = container_of(arg, struct efct_io, ref);
+	struct efct *efct;
+	struct efc_node *node;
+	int send_empty_event = false;
+	unsigned long flags = 0;
+
+	node = els->node;
+	efct = node->efc->base;
+
+	spin_lock_irqsave(&node->active_ios_lock, flags);
+		if (els->els_active) {
+			/* if active, remove from active list and check empty */
+			list_del(&els->list_entry);
+			/* Send list empty event if the IO allocator
+			 * is disabled, and the list is empty
+			 * If node->io_alloc_enabled was not checked,
+			 * the event would be posted continually
+			 */
+			send_empty_event = (!node->io_alloc_enabled) &&
+				list_empty(&node->els_io_active_list);
+			els->els_active = false;
+		} else if (els->els_pend) {
+			/* if pending, remove from pending list;
+			 * node shutdown isn't gated off the
+			 * pending list (only the active list),
+			 * so no need to check if pending list is empty
+			 */
+			list_del(&els->list_entry);
+			els->els_pend = 0;
+		} else {
+			efc_log_err(efct,
+				"Error: els not in pending or active set\n");
+			spin_unlock_irqrestore(&node->active_ios_lock, flags);
+			return;
+		}
+
+	spin_unlock_irqrestore(&node->active_ios_lock, flags);
+
+	/* free ELS request and response buffers */
+	dma_free_coherent(&efct->pcidev->dev, els->els_rsp.size,
+			  els->els_rsp.virt, els->els_rsp.phys);
+	dma_free_coherent(&efct->pcidev->dev, els->els_req.size,
+			  els->els_req.virt, els->els_req.phys);
+
+	memset(&els->els_rsp, 0, sizeof(struct efc_dma));
+	memset(&els->els_req, 0, sizeof(struct efc_dma));
+	efct_io_pool_io_free(efct->xport->io_pool, els);
+
+	if (send_empty_event)
+		efc_scsi_io_list_empty(node->efc, node);
+
+	efct_scsi_check_pending(efct);
+}
+
+static void
+efct_els_make_active(struct efct_io *els)
+{
+	struct efc_node *node = els->node;
+	unsigned long flags = 0;
+
+	/* move ELS from pending list to active list */
+	spin_lock_irqsave(&node->active_ios_lock, flags);
+		if (els->els_pend) {
+			if (els->els_active) {
+				efc_log_err(node->efc,
+					"Error: els in both pend and active\n");
+				spin_unlock_irqrestore(&node->active_ios_lock,
+						       flags);
+				return;
+			}
+			/* remove from pending list */
+			list_del(&els->list_entry);
+			els->els_pend = false;
+
+			/* add els structure to ELS IO list */
+			INIT_LIST_HEAD(&els->list_entry);
+			list_add_tail(&els->list_entry,
+				      &node->els_io_active_list);
+			els->els_active = true;
+		} else {
+			/* must be retrying; make sure it's already active */
+			if (!els->els_active) {
+				efc_log_err(node->efc,
+					"Error: els not in pending or active set\n");
+			}
+		}
+	spin_unlock_irqrestore(&node->active_ios_lock, flags);
+}
+
+static int efct_els_send(struct efct_io *els, u32 reqlen,
+			 u32 timeout_sec, efct_hw_srrs_cb_t cb)
+{
+	struct efc_node *node = els->node;
+
+	/* update ELS request counter */
+	node->els_req_cnt++;
+
+	/* move ELS from pending list to active list */
+	efct_els_make_active(els);
+
+	els->wire_len = reqlen;
+	return efct_scsi_io_dispatch(els, cb);
+}
+
+static void
+efct_els_retry(struct efct_io *els);
+
+static void
+efct_els_delay_timer_cb(struct timer_list *t)
+{
+	struct efct_io *els = from_timer(els, t, delay_timer);
+	struct efc_node *node = els->node;
+
+	/* Retry delay timer expired, retry the ELS request,
+	 * Free the HW IO so that a new oxid is used.
+	 */
+	if (els->state == EFCT_ELS_REQUEST_DELAY_ABORT) {
+		node->els_req_cnt++;
+		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
+					    NULL);
+	} else {
+		efct_els_retry(els);
+	}
+}
+
+static void
+efct_els_abort_cleanup(struct efct_io *els)
+{
+	/* handle event for ABORT_WQE
+	 * whatever state ELS happened to be in, propagate aborted even
+	 * up to node state machine in lieu of EFC_HW_SRRS_ELS_* event
+	 */
+	struct efc_node_cb cbdata;
+
+	cbdata.status = 0;
+	cbdata.ext_status = 0;
+	cbdata.els_rsp = els->els_rsp;
+	els_io_printf(els, "Request aborted\n");
+	efct_els_io_cleanup(els, EFC_HW_ELS_REQ_ABORTED, &cbdata);
+}
+
+static int
+efct_els_req_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
+		u32 length, int status, u32 ext_status, void *arg)
+{
+	struct efct_io *els;
+	struct efc_node *node;
+	struct efct *efct;
+	struct efc_node_cb cbdata;
+	u32 reason_code;
+
+	els = arg;
+	node = els->node;
+	efct = node->efc->base;
+
+	if (status != 0)
+		els_io_printf(els, "status x%x ext x%x\n", status, ext_status);
+
+	/* set the response len element of els->rsp */
+	els->els_rsp.len = length;
+
+	cbdata.status = status;
+	cbdata.ext_status = ext_status;
+	cbdata.header = NULL;
+	cbdata.els_rsp = els->els_rsp;
+
+	/* FW returns the number of bytes received on the link in
+	 * the WCQE, not the amount placed in the buffer; use this info to
+	 * check if there was an overrun.
+	 */
+	if (length > els->els_rsp.size) {
+		efc_log_warn(efct,
+			      "ELS response returned len=%d > buflen=%zu\n",
+			     length, els->els_rsp.size);
+		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL, &cbdata);
+		return EFC_SUCCESS;
+	}
+
+	/* Post event to ELS IO object */
+	switch (status) {
+	case SLI4_FC_WCQE_STATUS_SUCCESS:
+		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_OK, &cbdata);
+		break;
+
+	case SLI4_FC_WCQE_STATUS_LS_RJT:
+		reason_code = (ext_status >> 16) & 0xff;
+
+		/* delay and retry if reason code is Logical Busy */
+		switch (reason_code) {
+		case ELS_RJT_BUSY:
+			els->node->els_req_cnt--;
+			els_io_printf(els,
+				      "LS_RJT Logical Busy response,delay and retry\n");
+			timer_setup(&els->delay_timer,
+				    efct_els_delay_timer_cb, 0);
+			mod_timer(&els->delay_timer,
+				  jiffies + msecs_to_jiffies(5000));
+			els->state = EFCT_ELS_REQUEST_DELAYED;
+			break;
+		default:
+			efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_RJT,
+					    &cbdata);
+			break;
+		}
+		break;
+
+	case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
+		switch (ext_status) {
+		case SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT:
+			efct_els_retry(els);
+			break;
+
+		case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
+			if (els->state == EFCT_ELS_ABORT_IO_COMPL) {
+				/* completion for ELS that was aborted */
+				efct_els_abort_cleanup(els);
+			} else {
+				/* completion for ELS received first,
+				 * transition to wait for abort cmpl
+				 */
+				els->state = EFCT_ELS_REQ_ABORTED;
+			}
+
+			break;
+		default:
+			efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
+					    &cbdata);
+			break;
+		}
+		break;
+	default:	/* Other error */
+		efc_log_warn(efct,
+			      "els req failed status x%x, ext_status, x%x\n",
+					status, ext_status);
+		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL, &cbdata);
+		break;
+	}
+
+	return EFC_SUCCESS;
+}
+
+static void efct_els_send_req(struct efc_node *node, struct efct_io *els)
+{
+	int rc = 0;
+	struct efct *efct;
+
+	efct = node->efc->base;
+	rc = efct_els_send(els, els->els_req.size,
+			   els->els_timeout_sec, efct_els_req_cb);
+
+	if (rc) {
+		struct efc_node_cb cbdata;
+
+		cbdata.status = INT_MAX;
+		cbdata.ext_status = INT_MAX;
+		cbdata.els_rsp = els->els_rsp;
+		efc_log_err(efct, "efct_els_send failed: %d\n", rc);
+		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
+				    &cbdata);
+	}
+}
+
+static void
+efct_els_retry(struct efct_io *els)
+{
+	struct efct *efct;
+	struct efc_node_cb cbdata;
+
+	efct = els->node->efc->base;
+	cbdata.status = INT_MAX;
+	cbdata.ext_status = INT_MAX;
+	cbdata.els_rsp = els->els_rsp;
+
+	if (!els->els_retries_remaining) {
+		efc_log_err(efct, "ELS retries exhausted\n");
+		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
+				    &cbdata);
+		return;
+	}
+
+	els->els_retries_remaining--;
+	 /* Free the HW IO so that a new oxid is used.*/
+	if (els->hio) {
+		efct_hw_io_free(&efct->hw, els->hio);
+		els->hio = NULL;
+	}
+
+	efct_els_send_req(els->node, els);
+}
+
+static int
+efct_els_acc_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
+		u32 length, int status, u32 ext_status, void *arg)
+{
+	struct efct_io *els;
+	struct efc_node *node;
+	struct efct *efct;
+	struct efc_node_cb cbdata;
+
+	els = arg;
+	node = els->node;
+	efct = node->efc->base;
+
+	cbdata.status = status;
+	cbdata.ext_status = ext_status;
+	cbdata.header = NULL;
+	cbdata.els_rsp = els->els_rsp;
+
+	/* Post node event */
+	switch (status) {
+	case SLI4_FC_WCQE_STATUS_SUCCESS:
+		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_CMPL_OK, &cbdata);
+		break;
+
+	default:	/* Other error */
+		efc_log_warn(efct,
+			      "[%s] %-8s failed status x%x, ext_status x%x\n",
+			    node->display_name, els->display_name,
+			    status, ext_status);
+		efc_log_warn(efct,
+			      "els acc complete: failed status x%x, ext_status, x%x\n",
+		     status, ext_status);
+		efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_CMPL_FAIL, &cbdata);
+		break;
+	}
+
+	return EFC_SUCCESS;
+}
+
+static int
+efct_els_send_rsp(struct efct_io *els, u32 rsplen)
+{
+	struct efc_node *node = els->node;
+
+	/* increment ELS completion counter */
+	node->els_cmpl_cnt++;
+
+	/* move ELS from pending list to active list */
+	efct_els_make_active(els);
+
+	els->wire_len = rsplen;
+	return efct_scsi_io_dispatch(els, efct_els_acc_cb);
+}
+
+struct efct_io *
+efct_send_plogi(struct efc_node *node, u32 timeout_sec,
+		u32 retries,
+	      void (*cb)(struct efc_node *node,
+			 struct efc_node_cb *cbdata, void *arg), void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct = node->efc->base;
+	struct fc_els_flogi  *plogi;
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, sizeof(*plogi), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "plogi";
+
+	/* Build PLOGI request */
+	plogi = els->els_req.virt;
+
+	memcpy(plogi, node->sport->service_params, sizeof(*plogi));
+
+	plogi->fl_cmd = ELS_PLOGI;
+	memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd));
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_flogi(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct;
+	struct fc_els_flogi  *flogi;
+
+	efct = node->efc->base;
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, sizeof(*flogi), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "flogi";
+
+	/* Build FLOGI request */
+	flogi = els->els_req.virt;
+
+	memcpy(flogi, node->sport->service_params, sizeof(*flogi));
+	flogi->fl_cmd = ELS_FLOGI;
+	memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_fdisc(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct;
+	struct fc_els_flogi *fdisc;
+
+	efct = node->efc->base;
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, sizeof(*fdisc), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "fdisc";
+
+	/* Build FDISC request */
+	fdisc = els->els_req.virt;
+
+	memcpy(fdisc, node->sport->service_params, sizeof(*fdisc));
+	fdisc->fl_cmd = ELS_FDISC;
+	memset(fdisc->_fl_resvd, 0, sizeof(fdisc->_fl_resvd));
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_prli(struct efc_node *node, u32 timeout_sec, u32 retries,
+	       els_cb_t cb, void *cbarg)
+{
+	struct efct *efct = node->efc->base;
+	struct efct_io *els;
+	struct {
+		struct fc_els_prli prli;
+		struct fc_els_spp spp;
+	} *pp;
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "prli";
+
+	/* Build PRLI request */
+	pp = els->els_req.virt;
+
+	memset(pp, 0, sizeof(*pp));
+
+	pp->prli.prli_cmd = ELS_PRLI;
+	pp->prli.prli_spp_len = 16;
+	pp->prli.prli_len = cpu_to_be16(sizeof(*pp));
+	pp->spp.spp_type = FC_TYPE_FCP;
+	pp->spp.spp_type_ext = 0;
+	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
+	pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS |
+			       (node->sport->enable_ini ?
+			       FCP_SPPF_INIT_FCN : 0) |
+			       (node->sport->enable_tgt ?
+			       FCP_SPPF_TARG_FCN : 0));
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_prlo(struct efc_node *node, u32 timeout_sec, u32 retries,
+	       els_cb_t cb, void *cbarg)
+{
+	struct efct *efct = node->efc->base;
+	struct efct_io *els;
+	struct {
+		struct fc_els_prlo prlo;
+		struct fc_els_spp spp;
+	} *pp;
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "prlo";
+
+	/* Build PRLO request */
+	pp = els->els_req.virt;
+
+	memset(pp, 0, sizeof(*pp));
+	pp->prlo.prlo_cmd = ELS_PRLO;
+	pp->prlo.prlo_obs = 0x10;
+	pp->prlo.prlo_len = cpu_to_be16(sizeof(*pp));
+
+	pp->spp.spp_type = FC_TYPE_FCP;
+	pp->spp.spp_type_ext = 0;
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_logo(struct efc_node *node, u32 timeout_sec, u32 retries,
+	       els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct;
+	struct fc_els_logo *logo;
+	struct fc_els_flogi  *sparams;
+
+	efct = node->efc->base;
+
+	node_els_trace();
+
+	sparams = (struct fc_els_flogi *)node->sport->service_params;
+
+	els = efct_els_io_alloc(node, sizeof(*logo), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "logo";
+
+	/* Build LOGO request */
+
+	logo = els->els_req.virt;
+
+	memset(logo, 0, sizeof(*logo));
+	logo->fl_cmd = ELS_LOGO;
+	hton24(logo->fl_n_port_id, node->rnode.sport->fc_id);
+	logo->fl_n_port_wwn = sparams->fl_wwpn;
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_adisc(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct;
+	struct fc_els_adisc *adisc;
+	struct fc_els_flogi  *sparams;
+	struct efc_sli_port *sport = node->sport;
+
+	efct = node->efc->base;
+
+	node_els_trace();
+
+	sparams = (struct fc_els_flogi *)node->sport->service_params;
+
+	els = efct_els_io_alloc(node, sizeof(*adisc), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "adisc";
+
+	/* Build ADISC request */
+
+	adisc = els->els_req.virt;
+
+	memset(adisc, 0, sizeof(*adisc));
+	adisc->adisc_cmd = ELS_ADISC;
+	hton24(adisc->adisc_hard_addr, sport->fc_id);
+	adisc->adisc_wwpn = sparams->fl_wwpn;
+	adisc->adisc_wwnn = sparams->fl_wwnn;
+	hton24(adisc->adisc_port_id, node->rnode.sport->fc_id);
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_pdisc(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct = node->efc->base;
+	struct fc_els_flogi  *pdisc;
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, sizeof(*pdisc), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "pdisc";
+
+	pdisc = els->els_req.virt;
+
+	memcpy(pdisc, node->sport->service_params, sizeof(*pdisc));
+
+	pdisc->fl_cmd = ELS_PDISC;
+	memset(pdisc->_fl_resvd, 0, sizeof(pdisc->_fl_resvd));
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_scr(struct efc_node *node, u32 timeout_sec, u32 retries,
+	      els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct = node->efc->base;
+	struct fc_els_scr *req;
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, sizeof(*req), EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "scr";
+
+	req = els->els_req.virt;
+
+	memset(req, 0, sizeof(*req));
+	req->scr_cmd = ELS_SCR;
+	req->scr_reg_func = ELS_SCRF_FULL;
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_send_rscn(struct efc_node *node, u32 timeout_sec, u32 retries,
+	       void *port_ids, u32 port_ids_count, els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct = node->efc->base;
+	struct fc_els_rscn *req;
+	struct fc_els_rscn_page *rscn_page;
+	u32 length = sizeof(*rscn_page) * port_ids_count;
+
+	length += sizeof(*req);
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, length, EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->els_timeout_sec = timeout_sec;
+	els->els_retries_remaining = retries;
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "rscn";
+
+	req = els->els_req.virt;
+
+	req->rscn_cmd = ELS_RSCN;
+	req->rscn_page_len = sizeof(struct fc_els_rscn_page);
+	req->rscn_plen = cpu_to_be16(length);
+
+	els->hio_type = EFCT_HW_ELS_REQ;
+	els->iparam.els.timeout = timeout_sec;
+
+	/* copy in the payload */
+	rscn_page = els->els_req.virt + sizeof(*req);
+	memcpy(rscn_page, port_ids,
+	       port_ids_count * sizeof(*rscn_page));
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+void *
+efct_send_ls_rjt(struct efc *efc, struct efc_node *node,
+		 u32 ox_id, u32 reason_code,
+		u32 reason_code_expl, u32 vendor_unique)
+{
+	struct efct_io *io = NULL;
+	int rc;
+	struct efct *efct = node->efc->base;
+	struct fc_els_ls_rjt *rjt;
+
+	io = efct_els_io_alloc(node, sizeof(*rjt), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+
+	node_els_trace();
+
+	io->els_callback = NULL;
+	io->els_callback_arg = NULL;
+	io->display_name = "ls_rjt";
+	io->init_task_tag = ox_id;
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+
+	rjt = io->els_req.virt;
+	memset(rjt, 0, sizeof(*rjt));
+
+	rjt->er_cmd = ELS_LS_RJT;
+	rjt->er_reason = reason_code;
+	rjt->er_explan = reason_code_expl;
+
+	io->hio_type = EFCT_HW_ELS_RSP;
+	rc = efct_els_send_rsp(io, sizeof(*rjt));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+
+	return io;
+}
+
+struct efct_io *
+efct_send_plogi_acc(struct efc_node *node, u32 ox_id,
+		    els_cb_t cb, void *cbarg)
+{
+	int rc;
+	struct efct *efct = node->efc->base;
+	struct efct_io *io = NULL;
+	struct fc_els_flogi  *plogi;
+	struct fc_els_flogi  *req = (struct fc_els_flogi *)node->service_params;
+
+	node_els_trace();
+
+	io = efct_els_io_alloc(node, sizeof(*plogi), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+
+	io->els_callback = cb;
+	io->els_callback_arg = cbarg;
+	io->display_name = "plogi_acc";
+	io->init_task_tag = ox_id;
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+
+	plogi = io->els_req.virt;
+
+	/* copy our port's service parameters to payload */
+	memcpy(plogi, node->sport->service_params, sizeof(*plogi));
+	plogi->fl_cmd = ELS_LS_ACC;
+	memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd));
+
+	/* Set Application header support bit if requested */
+	if (req->fl_csp.sp_features & cpu_to_be16(FC_SP_FT_BCAST))
+		plogi->fl_csp.sp_features |= cpu_to_be16(FC_SP_FT_BCAST);
+
+	io->hio_type = EFCT_HW_ELS_RSP;
+	rc = efct_els_send_rsp(io, sizeof(*plogi));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+	return io;
+}
+
+void *
+efct_send_flogi_p2p_acc(struct efc *efc, struct efc_node *node,
+			u32 ox_id, u32 s_id)
+{
+	struct efct_io *io = NULL;
+	int rc;
+	struct efct *efct = node->efc->base;
+	struct fc_els_flogi  *flogi;
+
+	node_els_trace();
+
+	io = efct_els_io_alloc(node, sizeof(*flogi), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+
+	io->els_callback = NULL;
+	io->els_callback_arg = NULL;
+	io->display_name = "flogi_p2p_acc";
+	io->init_task_tag = ox_id;
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+	io->iparam.els.s_id = s_id;
+
+	flogi = io->els_req.virt;
+
+	/* copy our port's service parameters to payload */
+	memcpy(flogi, node->sport->service_params, sizeof(*flogi));
+	flogi->fl_cmd = ELS_LS_ACC;
+	memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
+
+	memset(flogi->fl_cssp, 0, sizeof(flogi->fl_cssp));
+
+	io->hio_type = EFCT_HW_ELS_RSP_SID;
+	rc = efct_els_send_rsp(io, sizeof(*flogi));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+
+	return io;
+}
+
+struct efct_io *
+efct_send_flogi_acc(struct efc_node *node, u32 ox_id, u32 is_fport,
+		    els_cb_t cb, void *cbarg)
+{
+	int rc;
+	struct efct *efct = node->efc->base;
+	struct efct_io *io = NULL;
+	struct fc_els_flogi  *flogi;
+
+	node_els_trace();
+
+	io = efct_els_io_alloc(node, sizeof(*flogi), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+	io->els_callback = cb;
+	io->els_callback_arg = cbarg;
+	io->display_name = "flogi_acc";
+	io->init_task_tag = ox_id;
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+	io->iparam.els.s_id = io->node->sport->fc_id;
+
+	flogi = io->els_req.virt;
+
+	/* copy our port's service parameters to payload */
+	memcpy(flogi, node->sport->service_params, sizeof(*flogi));
+
+	/* Set F_port */
+	if (is_fport) {
+		/* Set F_PORT and Multiple N_PORT_ID Assignment */
+		flogi->fl_csp.sp_r_a_tov |=  cpu_to_be32(3U << 28);
+	}
+
+	flogi->fl_cmd = ELS_LS_ACC;
+	memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
+
+	memset(flogi->fl_cssp, 0, sizeof(flogi->fl_cssp));
+
+	io->hio_type = EFCT_HW_ELS_RSP_SID;
+	rc = efct_els_send_rsp(io, sizeof(*flogi));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+
+	return io;
+}
+
+struct efct_io *efct_send_prli_acc(struct efc_node *node,
+				     u32 ox_id, els_cb_t cb, void *cbarg)
+{
+	int rc;
+	struct efct *efct = node->efc->base;
+	struct efct_io *io = NULL;
+	struct {
+		struct fc_els_prli prli;
+		struct fc_els_spp spp;
+	} *pp;
+
+	node_els_trace();
+
+	io = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+
+	io->els_callback = cb;
+	io->els_callback_arg = cbarg;
+	io->display_name = "prli_acc";
+	io->init_task_tag = ox_id;
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+
+	pp = io->els_req.virt;
+	memset(pp, 0, sizeof(*pp));
+
+	pp->prli.prli_cmd = ELS_LS_ACC;
+	pp->prli.prli_spp_len = 0x10;
+	pp->prli.prli_len = cpu_to_be16(sizeof(*pp));
+	pp->spp.spp_type = FC_TYPE_FCP;
+	pp->spp.spp_type_ext = 0;
+	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR | FC_SPP_RESP_ACK;
+
+	pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS |
+					(node->sport->enable_ini ?
+					 FCP_SPPF_INIT_FCN : 0) |
+					(node->sport->enable_tgt ?
+					 FCP_SPPF_TARG_FCN : 0));
+
+	io->hio_type = EFCT_HW_ELS_RSP;
+	rc = efct_els_send_rsp(io, sizeof(*pp));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+
+	return io;
+}
+
+struct efct_io *
+efct_send_prlo_acc(struct efc_node *node, u32 ox_id,
+		   els_cb_t cb, void *cbarg)
+{
+	int rc;
+	struct efct *efct = node->efc->base;
+	struct efct_io *io = NULL;
+	struct {
+		struct fc_els_prlo prlo;
+		struct fc_els_spp spp;
+	} *pp;
+
+	node_els_trace();
+
+	io = efct_els_io_alloc(node, sizeof(*pp), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+
+	io->els_callback = cb;
+	io->els_callback_arg = cbarg;
+	io->display_name = "prlo_acc";
+	io->init_task_tag = ox_id;
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+
+	pp = io->els_req.virt;
+	memset(pp, 0, sizeof(*pp));
+	pp->prlo.prlo_cmd = ELS_LS_ACC;
+	pp->prlo.prlo_obs = 0x10;
+	pp->prlo.prlo_len = cpu_to_be16(sizeof(*pp));
+
+	pp->spp.spp_type = FC_TYPE_FCP;
+	pp->spp.spp_type_ext = 0;
+	pp->spp.spp_flags = FC_SPP_RESP_ACK;
+
+	io->hio_type = EFCT_HW_ELS_RSP;
+	rc = efct_els_send_rsp(io, sizeof(*pp));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+
+	return io;
+}
+
+struct efct_io *
+efct_send_ls_acc(struct efc_node *node, u32 ox_id, els_cb_t cb,
+		 void *cbarg)
+{
+	int rc;
+	struct efct *efct = node->efc->base;
+	struct efct_io *io = NULL;
+	struct fc_els_ls_acc *acc;
+
+	node_els_trace();
+
+	io = efct_els_io_alloc(node, sizeof(*acc), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+
+	io->els_callback = cb;
+	io->els_callback_arg = cbarg;
+	io->display_name = "ls_acc";
+	io->init_task_tag = ox_id;
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+
+	acc = io->els_req.virt;
+	memset(acc, 0, sizeof(*acc));
+
+	acc->la_cmd = ELS_LS_ACC;
+
+	io->hio_type = EFCT_HW_ELS_RSP;
+	rc = efct_els_send_rsp(io, sizeof(*acc));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+
+	return io;
+}
+
+struct efct_io *
+efct_send_logo_acc(struct efc_node *node, u32 ox_id,
+		   els_cb_t cb, void *cbarg)
+{
+	int rc;
+	struct efct_io *io = NULL;
+	struct efct *efct = node->efc->base;
+	struct fc_els_ls_acc *logo;
+
+	node_els_trace();
+
+	io = efct_els_io_alloc(node, sizeof(*logo), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+
+	io->els_callback = cb;
+	io->els_callback_arg = cbarg;
+	io->display_name = "logo_acc";
+	io->init_task_tag = ox_id;
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+
+	logo = io->els_req.virt;
+	memset(logo, 0, sizeof(*logo));
+
+	logo->la_cmd = ELS_LS_ACC;
+
+	io->hio_type = EFCT_HW_ELS_RSP;
+	rc = efct_els_send_rsp(io, sizeof(*logo));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+
+	return io;
+}
+
+struct efct_io *
+efct_send_adisc_acc(struct efc_node *node, u32 ox_id,
+		    els_cb_t cb, void *cbarg)
+{
+	int rc;
+	struct efct_io *io = NULL;
+	struct fc_els_adisc *adisc;
+	struct fc_els_flogi  *sparams;
+	struct efct *efct;
+
+	efct = node->efc->base;
+
+	node_els_trace();
+
+	io = efct_els_io_alloc(node, sizeof(*adisc), EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efct, "els IO alloc failed\n");
+		return io;
+	}
+
+	io->els_callback = cb;
+	io->els_callback_arg = cbarg;
+	io->display_name = "adisc_acc";
+	io->init_task_tag = ox_id;
+
+	/* Go ahead and send the ELS_ACC */
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.els.ox_id = ox_id;
+
+	sparams = (struct fc_els_flogi  *)node->sport->service_params;
+	adisc = io->els_req.virt;
+	memset(adisc, 0, sizeof(*adisc));
+	adisc->adisc_cmd = ELS_LS_ACC;
+	adisc->adisc_wwpn = sparams->fl_wwpn;
+	adisc->adisc_wwnn = sparams->fl_wwnn;
+	hton24(adisc->adisc_port_id, node->rnode.sport->fc_id);
+
+	io->hio_type = EFCT_HW_ELS_RSP;
+	rc = efct_els_send_rsp(io, sizeof(*adisc));
+	if (rc) {
+		efct_els_io_free(io);
+		io = NULL;
+	}
+
+	return io;
+}
+
+void *
+efct_els_send_ct(struct efc *efc, struct efc_node *node, u32 cmd,
+		 u32 timeout_sec, u32 retries)
+{
+	struct efct *efct = efc->base;
+
+	switch (cmd) {
+	case FC_RCTL_ELS_REQ:
+		efct_ns_send_rftid(node, timeout_sec, retries, NULL, NULL);
+		break;
+	case FC_NS_RFF_ID:
+		efct_ns_send_rffid(node, timeout_sec, retries, NULL, NULL);
+		break;
+	case FC_NS_GID_PT:
+		efct_ns_send_gidpt(node, timeout_sec, retries, NULL, NULL);
+		break;
+	default:
+		efc_log_err(efct, "Unhandled command cmd: %x\n", cmd);
+	}
+
+	return NULL;
+}
+
+static inline void fcct_build_req_header(struct fc_ct_hdr  *hdr,
+					 u16 cmd, u16 max_size)
+{
+	hdr->ct_rev = FC_CT_REV;
+	hdr->ct_fs_type = FC_FST_DIR;
+	hdr->ct_fs_subtype = FC_NS_SUBTYPE;
+	hdr->ct_options = 0;
+	hdr->ct_cmd = cpu_to_be16(cmd);
+	/* words */
+	hdr->ct_mr_size = cpu_to_be16(max_size / (sizeof(u32)));
+	hdr->ct_reason = 0;
+	hdr->ct_explan = 0;
+	hdr->ct_vendor = 0;
+}
+
+struct efct_io *
+efct_ns_send_rftid(struct efc_node *node, u32 timeout_sec,
+		   u32 retries, els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct = node->efc->base;
+	struct fc_ct_hdr *ct;
+	struct fc_ns_rft_id *rftid;
+
+	node_els_trace();
+
+	els = efct_els_io_alloc(node, sizeof(*ct) + sizeof(*rftid),
+				EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+
+	els->iparam.fc_ct.r_ctl = FC_RCTL_ELS_REQ;
+	els->iparam.fc_ct.type = FC_TYPE_CT;
+	els->iparam.fc_ct.df_ctl = 0;
+	els->iparam.fc_ct.timeout = timeout_sec;
+
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "rftid";
+
+	ct = els->els_req.virt;
+	memset(ct, 0, sizeof(*ct));
+	fcct_build_req_header(ct, FC_NS_RFT_ID, sizeof(*rftid));
+
+	rftid = els->els_req.virt + sizeof(*ct);
+	memset(rftid, 0, sizeof(*rftid));
+	hton24(rftid->fr_fid.fp_fid, node->rnode.sport->fc_id);
+	rftid->fr_fts.ff_type_map[FC_TYPE_FCP / FC_NS_BPW] =
+		cpu_to_be32(1 << (FC_TYPE_FCP % FC_NS_BPW));
+
+	els->hio_type = EFCT_HW_FC_CT;
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_ns_send_rffid(struct efc_node *node, u32 timeout_sec,
+		   u32 retries, els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els;
+	struct efct *efct = node->efc->base;
+	struct fc_ct_hdr *ct;
+	struct fc_ns_rff_id *rffid;
+	u32 size = 0;
+
+	node_els_trace();
+
+	size = sizeof(*ct) + sizeof(*rffid);
+
+	els = efct_els_io_alloc(node, size, EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+	els->iparam.fc_ct.r_ctl = FC_RCTL_ELS_REQ;
+	els->iparam.fc_ct.type = FC_TYPE_CT;
+	els->iparam.fc_ct.df_ctl = 0;
+	els->iparam.fc_ct.timeout = timeout_sec;
+
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "rffid";
+	ct = els->els_req.virt;
+
+	memset(ct, 0, sizeof(*ct));
+	fcct_build_req_header(ct, FC_NS_RFF_ID, sizeof(*rffid));
+
+	rffid = els->els_req.virt + sizeof(*ct);
+	memset(rffid, 0, sizeof(*rffid));
+
+	hton24(rffid->fr_fid.fp_fid, node->rnode.sport->fc_id);
+	if (node->sport->enable_ini)
+		rffid->fr_feat |= FCP_FEAT_INIT;
+	if (node->sport->enable_tgt)
+		rffid->fr_feat |= FCP_FEAT_TARG;
+	rffid->fr_type = FC_TYPE_FCP;
+
+	els->hio_type = EFCT_HW_FC_CT;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+struct efct_io *
+efct_ns_send_gidpt(struct efc_node *node, u32 timeout_sec,
+		   u32 retries, els_cb_t cb, void *cbarg)
+{
+	struct efct_io *els = NULL;
+	struct efct *efct = node->efc->base;
+	struct fc_ct_hdr *ct;
+	struct fc_ns_gid_pt *gidpt;
+	u32 size = 0;
+
+	node_els_trace();
+
+	size = sizeof(*ct) + sizeof(*gidpt);
+	els = efct_els_io_alloc_size(node, size,
+				     EFCT_ELS_GID_PT_RSP_LEN,
+				   EFCT_ELS_ROLE_ORIGINATOR);
+	if (!els) {
+		efc_log_err(efct, "IO alloc failed\n");
+		return els;
+	}
+
+	els->iparam.fc_ct.r_ctl = FC_RCTL_ELS_REQ;
+	els->iparam.fc_ct.type = FC_TYPE_CT;
+	els->iparam.fc_ct.df_ctl = 0;
+	els->iparam.fc_ct.timeout = timeout_sec;
+
+	els->els_callback = cb;
+	els->els_callback_arg = cbarg;
+	els->display_name = "gidpt";
+
+	ct = els->els_req.virt;
+
+	memset(ct, 0, sizeof(*ct));
+	fcct_build_req_header(ct, FC_NS_GID_PT, sizeof(*gidpt));
+
+	gidpt = els->els_req.virt + sizeof(*ct);
+	memset(gidpt, 0, sizeof(*gidpt));
+	gidpt->fn_pt_type = FC_TYPE_FCP;
+
+	els->hio_type = EFCT_HW_FC_CT;
+
+	efct_els_send_req(node, els);
+
+	return els;
+}
+
+static int efct_bls_send_rjt_cb(struct efct_hw_io *hio,
+				struct efc_remote_node *rnode, u32 length,
+		int status, u32 ext_status, void *app)
+{
+	struct efct_io *io = app;
+
+	efct_scsi_io_free(io);
+	return EFC_SUCCESS;
+}
+
+static struct efct_io *
+efct_bls_send_rjt(struct efct_io *io, u32 s_id,
+		  u16 ox_id, u16 rx_id)
+{
+	struct efc_node *node = io->node;
+	int rc;
+	struct fc_ba_rjt *acc;
+	struct efct *efct;
+
+	efct = node->efc->base;
+
+	if (node->rnode.sport->fc_id == s_id)
+		s_id = U32_MAX;
+
+	/* fill out generic fields */
+	io->efct = efct;
+	io->node = node;
+	io->cmd_tgt = true;
+
+	/* fill out BLS Response-specific fields */
+	io->io_type = EFCT_IO_TYPE_BLS_RESP;
+	io->display_name = "ba_rjt";
+	io->hio_type = EFCT_HW_BLS_RJT;
+	io->init_task_tag = ox_id;
+
+	/* fill out iparam fields */
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.bls.ox_id = ox_id;
+	io->iparam.bls.rx_id = rx_id;
+
+	acc = (void *)io->iparam.bls.payload;
+
+	memset(io->iparam.bls.payload, 0,
+	       sizeof(io->iparam.bls.payload));
+	acc->br_reason = ELS_RJT_UNAB;
+	acc->br_explan = ELS_EXPL_NONE;
+
+	rc = efct_scsi_io_dispatch(io, efct_bls_send_rjt_cb);
+	if (rc) {
+		efc_log_err(efct, "efct_scsi_io_dispatch() failed: %d\n", rc);
+		efct_scsi_io_free(io);
+		io = NULL;
+	}
+	return io;
+}
+
+struct efct_io *
+efct_bls_send_rjt_hdr(struct efct_io *io, struct fc_frame_header *hdr)
+{
+	u16 ox_id = be16_to_cpu(hdr->fh_ox_id);
+	u16 rx_id = be16_to_cpu(hdr->fh_rx_id);
+	u32 d_id = ntoh24(hdr->fh_d_id);
+
+	return efct_bls_send_rjt(io, d_id, ox_id, rx_id);
+}
+
+static int efct_bls_send_acc_cb(struct efct_hw_io *hio,
+				struct efc_remote_node *rnode, u32 length,
+		int status, u32 ext_status, void *app)
+{
+	struct efct_io *io = app;
+
+	efct_scsi_io_free(io);
+	return EFC_SUCCESS;
+}
+
+static struct efct_io *
+efct_bls_send_acc(struct efct_io *io, u32 s_id,
+		  u16 ox_id, u16 rx_id)
+{
+	struct efc_node *node = io->node;
+	int rc;
+	struct fc_ba_acc *acc;
+	struct efct *efct;
+
+	efct = node->efc->base;
+
+	if (node->rnode.sport->fc_id == s_id)
+		s_id = U32_MAX;
+
+	/* fill out generic fields */
+	io->efct = efct;
+	io->node = node;
+	io->cmd_tgt = true;
+
+	/* fill out BLS Response-specific fields */
+	io->io_type = EFCT_IO_TYPE_BLS_RESP;
+	io->display_name = "ba_acc";
+	io->hio_type = EFCT_HW_BLS_ACC_SID;
+	io->init_task_tag = ox_id;
+
+	/* fill out iparam fields */
+	memset(&io->iparam, 0, sizeof(io->iparam));
+	io->iparam.bls.s_id = s_id;
+	io->iparam.bls.ox_id = ox_id;
+	io->iparam.bls.rx_id = rx_id;
+
+	acc = (void *)io->iparam.bls.payload;
+
+	memset(io->iparam.bls.payload, 0,
+	       sizeof(io->iparam.bls.payload));
+	acc->ba_ox_id = cpu_to_be16(io->iparam.bls.ox_id);
+	acc->ba_rx_id = cpu_to_be16(io->iparam.bls.rx_id);
+	acc->ba_high_seq_cnt = cpu_to_be16(U16_MAX);
+
+	rc = efct_scsi_io_dispatch(io, efct_bls_send_acc_cb);
+	if (rc) {
+		efc_log_err(efct, "efct_scsi_io_dispatch() failed: %d\n", rc);
+		efct_scsi_io_free(io);
+		io = NULL;
+	}
+	return io;
+}
+
+void *
+efct_bls_send_acc_hdr(struct efc *efc, struct efc_node *node,
+		      struct fc_frame_header *hdr)
+{
+	struct efct_io *io = NULL;
+	u16 ox_id = be16_to_cpu(hdr->fh_ox_id);
+	u16 rx_id = be16_to_cpu(hdr->fh_rx_id);
+	u32 d_id = ntoh24(hdr->fh_d_id);
+
+	io = efct_scsi_io_alloc(node, EFCT_SCSI_IO_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efc, "els IO alloc failed\n");
+		return io;
+	}
+
+	return efct_bls_send_acc(io, d_id, ox_id, rx_id);
+}
+
+static int
+efct_els_abort_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
+		  u32 length, int status, u32 ext_status,
+		 void *app)
+{
+	struct efct_io *els;
+	struct efct_io *abort_io = NULL; /* IO structure used to abort ELS */
+	struct efct *efct;
+
+	abort_io = app;
+	els = abort_io->io_to_abort;
+
+	if (!els || !els->node || !els->node->efc)
+		return EFC_FAIL;
+
+	efct = els->node->efc->base;
+
+	if (status != 0)
+		efc_log_warn(efct, "status x%x ext x%x\n", status, ext_status);
+
+	/* now free the abort IO */
+	efct_io_pool_io_free(efct->xport->io_pool, abort_io);
+
+	/* send completion event to indicate abort process is complete
+	 * Note: The ELS SM will already be receiving
+	 * ELS_REQ_OK/FAIL/RJT/ABORTED
+	 */
+	if (els->state == EFCT_ELS_REQ_ABORTED) {
+		/* completion for ELS that was aborted */
+		efct_els_abort_cleanup(els);
+	} else {
+		/* completion for abort was received first,
+		 * transition to wait for req cmpl
+		 */
+		els->state = EFCT_ELS_ABORT_IO_COMPL;
+	}
+
+	/* done with ELS IO to abort */
+	kref_put(&els->ref, els->release);
+	return EFC_SUCCESS;
+}
+
+static struct efct_io *
+efct_els_abort_io(struct efct_io *els, bool send_abts)
+{
+	struct efct *efct;
+	struct efct_xport *xport;
+	int rc;
+	struct efct_io *abort_io = NULL;
+
+	efct = els->node->efc->base;
+	xport = efct->xport;
+
+	/* take a reference on IO being aborted */
+	if ((kref_get_unless_zero(&els->ref) == 0)) {
+		/* command no longer active */
+		efc_log_debug(efct, "els no longer active\n");
+		return NULL;
+	}
+
+	/* allocate IO structure to send abort */
+	abort_io = efct_io_pool_io_alloc(efct->xport->io_pool);
+	if (!abort_io) {
+		atomic_add_return(1, &xport->io_alloc_failed_count);
+	} else {
+		/* set generic fields */
+		abort_io->efct = efct;
+		abort_io->node = els->node;
+		abort_io->cmd_ini = true;
+
+		/* set type and ABORT-specific fields */
+		abort_io->io_type = EFCT_IO_TYPE_ABORT;
+		abort_io->display_name = "abort_els";
+		abort_io->io_to_abort = els;
+		abort_io->send_abts = send_abts;
+
+		/* now dispatch IO */
+		rc = efct_scsi_io_dispatch_abort(abort_io, efct_els_abort_cb);
+		if (rc) {
+			efc_log_err(efct,
+				     "efct_scsi_io_dispatch failed: %d\n", rc);
+			efct_io_pool_io_free(efct->xport->io_pool, abort_io);
+			abort_io = NULL;
+		}
+	}
+
+	/* if something failed, put reference on ELS to abort */
+	if (!abort_io)
+		kref_put(&els->ref, els->release);
+	return abort_io;
+}
+
+void
+efct_els_abort(struct efct_io *els, struct efc_node_cb *arg)
+{
+	struct efct_io *io = NULL;
+	struct efc_node *node;
+	struct efct *efct;
+
+	node = els->node;
+	efct = node->efc->base;
+
+	/* request to abort this ELS without an ABTS */
+	els_io_printf(els, "ELS abort requested\n");
+	/* Set retries to zero,we are done */
+	els->els_retries_remaining = 0;
+	if (els->state == EFCT_ELS_REQUEST) {
+		els->state = EFCT_ELS_REQ_ABORT;
+		io = efct_els_abort_io(els, false);
+		if (!io) {
+			efc_log_err(efct, "efct_els_abort_io failed\n");
+			efct_els_io_cleanup(els, EFC_HW_SRRS_ELS_REQ_FAIL,
+					    arg);
+		}
+
+	} else if (els->state == EFCT_ELS_REQUEST_DELAYED) {
+		/* mod/resched the timer for a short duration */
+		mod_timer(&els->delay_timer,
+			  jiffies + msecs_to_jiffies(1));
+
+		els->state = EFCT_ELS_REQUEST_DELAY_ABORT;
+	}
+}
+
+void
+efct_els_io_cleanup(struct efct_io *els,
+		    enum efc_hw_node_els_event node_evt, void *arg)
+{
+	/* don't want further events that could come; e.g. abort requests
+	 * from the node state machine; thus, disable state machine
+	 */
+	els->els_req_free = true;
+	efc_node_post_els_resp(els->node, node_evt, arg);
+
+	/* If this IO has a callback, invoke it */
+	if (els->els_callback) {
+		(*els->els_callback)(els->node, arg,
+				    els->els_callback_arg);
+	}
+	efct_els_io_free(els);
+}
+
+int
+efct_els_io_list_empty(struct efc_node *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;
+}
+
+static int
+efct_ct_acc_cb(struct efct_hw_io *hio, struct efc_remote_node *rnode,
+	       u32 length, int status, u32 ext_status,
+	      void *arg)
+{
+	struct efct_io *io = arg;
+
+	efct_els_io_free(io);
+
+	return EFC_SUCCESS;
+}
+
+int
+efct_send_ct_rsp(struct efc *efc, struct efc_node *node, u16 ox_id,
+		 struct fc_ct_hdr  *ct_hdr, u32 cmd_rsp_code,
+		u32 reason_code, u32 reason_code_explanation)
+{
+	struct efct_io *io = NULL;
+	struct fc_ct_hdr  *rsp = NULL;
+
+	io = efct_els_io_alloc(node, 256, EFCT_ELS_ROLE_RESPONDER);
+	if (!io) {
+		efc_log_err(efc, "IO alloc failed\n");
+		return EFC_FAIL;
+	}
+
+	rsp = io->els_rsp.virt;
+	io->io_type = EFCT_IO_TYPE_CT_RESP;
+
+	*rsp = *ct_hdr;
+
+	fcct_build_req_header(rsp, cmd_rsp_code, 0);
+	rsp->ct_reason = reason_code;
+	rsp->ct_explan = reason_code_explanation;
+
+	io->display_name = "ct_rsp";
+	io->init_task_tag = ox_id;
+	io->wire_len += sizeof(*rsp);
+
+	memset(&io->iparam, 0, sizeof(io->iparam));
+
+	io->io_type = EFCT_IO_TYPE_CT_RESP;
+	io->hio_type = EFCT_HW_FC_CT_RSP;
+	io->iparam.fc_ct.ox_id = ox_id;
+	io->iparam.fc_ct.r_ctl = 3;
+	io->iparam.fc_ct.type = FC_TYPE_CT;
+	io->iparam.fc_ct.df_ctl = 0;
+	io->iparam.fc_ct.timeout = 5;
+
+	if (efct_scsi_io_dispatch(io, efct_ct_acc_cb) < 0) {
+		efct_els_io_free(io);
+		return EFC_FAIL;
+	}
+	return EFC_SUCCESS;
+}
diff --git a/drivers/scsi/elx/efct/efct_els.h b/drivers/scsi/elx/efct/efct_els.h
new file mode 100644
index 000000000000..9b79783a39a3
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_els.h
@@ -0,0 +1,133 @@ 
+/* 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(__EFCT_ELS_H__)
+#define __EFCT_ELS_H__
+
+enum efct_els_role {
+	EFCT_ELS_ROLE_ORIGINATOR,
+	EFCT_ELS_ROLE_RESPONDER,
+};
+
+void _efct_els_io_free(struct kref *arg);
+extern struct efct_io *
+efct_els_io_alloc(struct efc_node *node, u32 reqlen,
+		  enum efct_els_role role);
+extern struct efct_io *
+efct_els_io_alloc_size(struct efc_node *node, u32 reqlen,
+		       u32 rsplen,
+				       enum efct_els_role role);
+void efct_els_io_free(struct efct_io *els);
+
+extern void *
+efct_els_req_send(struct efc *efc, struct efc_node *node,
+		  u32 cmd, u32 timeout_sec, u32 retries);
+extern void *
+efct_els_send_ct(struct efc *efc, struct efc_node *node,
+		 u32 cmd, u32 timeout_sec, u32 retries);
+extern void *
+efct_els_resp_send(struct efc *efc, struct efc_node *node,
+		   u32 cmd, u16 ox_id);
+void
+efct_els_abort(struct efct_io *els, struct efc_node_cb *arg);
+/* ELS command send */
+typedef void (*els_cb_t)(struct efc_node *node,
+			 struct efc_node_cb *cbdata, void *arg);
+extern struct efct_io *
+efct_send_plogi(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_flogi(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_fdisc(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_prli(struct efc_node *node, u32 timeout_sec,
+	       u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_prlo(struct efc_node *node, u32 timeout_sec,
+	       u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_logo(struct efc_node *node, u32 timeout_sec,
+	       u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_adisc(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_pdisc(struct efc_node *node, u32 timeout_sec,
+		u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_scr(struct efc_node *node, u32 timeout_sec,
+	      u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_ns_send_rftid(struct efc_node *node,
+		   u32 timeout_sec,
+		  u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_ns_send_rffid(struct efc_node *node,
+		   u32 timeout_sec,
+		  u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_ns_send_gidpt(struct efc_node *node, u32 timeout_sec,
+		   u32 retries, els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_rscn(struct efc_node *node, u32 timeout_sec,
+	       u32 retries, void *port_ids,
+	      u32 port_ids_count, els_cb_t cb, void *cbarg);
+extern void
+efct_els_io_cleanup(struct efct_io *els, enum efc_hw_node_els_event,
+		    void *arg);
+
+/* ELS acc send */
+extern struct efct_io *
+efct_send_ls_acc(struct efc_node *node, u32 ox_id,
+		 els_cb_t cb, void *cbarg);
+
+extern void *
+efct_send_ls_rjt(struct efc *efc, struct efc_node *node, u32 ox_id,
+		 u32 reason_cod, u32 reason_code_expl,
+		u32 vendor_unique);
+extern void *
+efct_send_flogi_p2p_acc(struct efc *efc, struct efc_node *node,
+			u32 ox_id, u32 s_id);
+extern struct efct_io *
+efct_send_flogi_acc(struct efc_node *node, u32 ox_id,
+		    u32 is_fport, els_cb_t cb,
+		   void *cbarg);
+extern struct efct_io *
+efct_send_plogi_acc(struct efc_node *node, u32 ox_id,
+		    els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_prli_acc(struct efc_node *node, u32 ox_id,
+		   els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_logo_acc(struct efc_node *node, u32 ox_id,
+		   els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_prlo_acc(struct efc_node *node, u32 ox_id,
+		   els_cb_t cb, void *cbarg);
+extern struct efct_io *
+efct_send_adisc_acc(struct efc_node *node, u32 ox_id,
+		    els_cb_t cb, void *cbarg);
+
+extern void *
+efct_bls_send_acc_hdr(struct efc *efc, struct efc_node *node,
+		      struct fc_frame_header *hdr);
+extern struct efct_io *
+efct_bls_send_rjt_hdr(struct efct_io *io, struct fc_frame_header *hdr);
+
+extern int
+efct_els_io_list_empty(struct efc_node *node, struct list_head *list);
+
+/* CT */
+extern int
+efct_send_ct_rsp(struct efc *efc, struct efc_node *node, u16 ox_id,
+		 struct fc_ct_hdr *ct_hdr,
+		 u32 cmd_rsp_code, u32 reason_code,
+		 u32 reason_code_explanation);
+
+#endif /* __EFCT_ELS_H__ */