Re: [PATCH v3 23/31] elx: efct: SCSI IO handling routines

Daniel Wagner
2020-04-16T11:41:49Z

On Sat, Apr 11, 2020 at 08:32:55PM -0700, James Smart wrote:
> This patch continues the efct driver population.
>
> This patch adds driver definitions for:
> Routines for SCSI transport IO alloc, build and send IO.
>
> Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
> Signed-off-by: James Smart <jsmart2021@gmail.com>
> Reviewed-by: Hannes Reinecke <hare@suse.de>
>
> ---
> v3:
> Removed DIF related code which is not used.
> Removed SCSI get property.
> ---
> drivers/scsi/elx/efct/efct_scsi.c | 1192 +++++++++++++++++++++++++++++++++++++
> drivers/scsi/elx/efct/efct_scsi.h | 235 ++++++++
> 2 files changed, 1427 insertions(+)
> create mode 100644 drivers/scsi/elx/efct/efct_scsi.c
> create mode 100644 drivers/scsi/elx/efct/efct_scsi.h
>
> diff --git a/drivers/scsi/elx/efct/efct_scsi.c b/drivers/scsi/elx/efct/efct_scsi.c
> new file mode 100644
> index 000000000000..c299eadbc492
> --- /dev/null
> +++ b/drivers/scsi/elx/efct/efct_scsi.c
> @@ -0,0 +1,1192 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019 Broadcom. All Rights Reserved. The term
> + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
> + */
> +
> +#include "efct_driver.h"
> +#include "efct_els.h"
> +#include "efct_hw.h"
> +
> +#define enable_tsend_auto_resp(efct) 1
> +#define enable_treceive_auto_resp(efct) 0
> +
> +#define SCSI_IOFMT "[%04x][i:%04x t:%04x h:%04x]"
> +
> +#define scsi_io_printf(io, fmt, ...) \
> + efc_log_debug(io->efct, "[%s]" SCSI_IOFMT fmt, \
> + io->node->display_name, io->instance_index,\
> + io->init_task_tag, io->tgt_task_tag, io->hw_tag, ##__VA_ARGS__)
> +
> +#define EFCT_LOG_ENABLE_SCSI_TRACE(efct) \
> + (((efct) != NULL) ? (((efct)->logmask & (1U << 2)) != 0) : 0)
> +
> +#define scsi_io_trace(io, fmt, ...) \
> + do { \
> + if (EFCT_LOG_ENABLE_SCSI_TRACE(io->efct)) \
> + scsi_io_printf(io, fmt, ##__VA_ARGS__); \
> + } while (0)
> +
> +/* Enable the SCSI and Transport IO allocations */
> +void
> +efct_scsi_io_alloc_enable(struct efc *efc, struct efc_node *node)
> +{
> + unsigned long flags = 0;
> +
> + spin_lock_irqsave(&node->active_ios_lock, flags);
> + node->io_alloc_enabled = true;

no need to indent

> + spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +}
> +
> +/* Disable the SCSI and Transport IO allocations */
> +void
> +efct_scsi_io_alloc_disable(struct efc *efc, struct efc_node *node)
> +{
> + unsigned long flags = 0;
> +
> + spin_lock_irqsave(&node->active_ios_lock, flags);
> + node->io_alloc_enabled = false;

no need to indent

> + spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +}
> +
> +struct efct_io *
> +efct_scsi_io_alloc(struct efc_node *node, enum efct_scsi_io_role role)
> +{
> + struct efct *efct;
> + struct efc *efcp;
> + struct efct_xport *xport;
> + struct efct_io *io;
> + unsigned long flags = 0;
> +
> + efcp = node->efc;
> + efct = efcp->base;
> +
> + xport = efct->xport;
> +
> + spin_lock_irqsave(&node->active_ios_lock, flags);
> +
> + if (!node->io_alloc_enabled) {
> + spin_unlock_irqrestore(&node->active_ios_lock, flags);
> + return NULL;
> + }
> +
> + io = efct_io_pool_io_alloc(efct->xport->io_pool);
> + if (!io) {
> + atomic_add_return(1, &xport->io_alloc_failed_count);
> + spin_unlock_irqrestore(&node->active_ios_lock, flags);
> + return NULL;
> + }
> +
> + /* initialize refcount */
> + kref_init(&io->ref);
> + io->release = _efct_scsi_io_free;
> +
> + if (io->hio) {
> + efc_log_err(efct,
> + "assertion failed: io->hio is not NULL\n");
> + spin_unlock_irqrestore(&node->active_ios_lock, flags);
> + return NULL;
> + }
> +
> + /* set generic fields */
> + io->efct = efct;
> + io->node = node;
> +
> + /* set type and name */
> + io->io_type = EFCT_IO_TYPE_IO;
> + io->display_name = "scsi_io";
> +
> + switch (role) {
> + case EFCT_SCSI_IO_ROLE_ORIGINATOR:
> + io->cmd_ini = true;
> + io->cmd_tgt = false;
> + break;
> + case EFCT_SCSI_IO_ROLE_RESPONDER:
> + io->cmd_ini = false;
> + io->cmd_tgt = true;
> + break;
> + }
> +
> + /* Add to node's active_ios list */
> + INIT_LIST_HEAD(&io->list_entry);
> + list_add_tail(&io->list_entry, &node->active_ios);

no need to indent

> +
> + spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +
> + return io;
> +}
> +
> +void
> +_efct_scsi_io_free(struct kref *arg)
> +{
> + struct efct_io *io = container_of(arg, struct efct_io, ref);
> + struct efct *efct = io->efct;
> + struct efc_node *node = io->node;
> + int send_empty_event;
> + unsigned long flags = 0;
> +
> + scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
> +
> + if (io->io_free) {
> + efc_log_err(efct, "IO already freed.\n");
> + return;
> + }
> +
> + spin_lock_irqsave(&node->active_ios_lock, flags);
> + list_del(&io->list_entry);
> + send_empty_event = (!node->io_alloc_enabled) &&
> + list_empty(&node->active_ios);

no need to indent

> + spin_unlock_irqrestore(&node->active_ios_lock, flags);
> +
> + if (send_empty_event)
> + efc_scsi_io_list_empty(node->efc, node);
> +
> + io->node = NULL;
> + efct_io_pool_io_free(efct->xport->io_pool, io);
> +}
> +
> +void
> +efct_scsi_io_free(struct efct_io *io)
> +{
> + scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
> + WARN_ON(refcount_read(&io->ref.refcount) != 0);
> + kref_put(&io->ref, io->release);
> +}
> +
> +static void
> +efct_target_io_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;
> + struct efct *efct;
> + enum efct_scsi_io_status scsi_stat = EFCT_SCSI_STATUS_GOOD;
> +
> + if (!io || !io->efct) {
> + pr_err("%s: IO can not be NULL\n", __func__);
> + return;
> + }
> +
> + scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status);
> +
> + efct = io->efct;
> +
> + io->transferred += length;
> +
> + /* Call target server completion */
> + if (io->scsi_tgt_cb) {

move the following part into a function

> + efct_scsi_io_cb_t cb = io->scsi_tgt_cb;
> + u32 flags = 0;
> +
> + /* Clear the callback before invoking the callback */
> + io->scsi_tgt_cb = NULL;
> +
> + /* if status was good, and auto-good-response was set,
> + * then callback target-server with IO_CMPL_RSP_SENT,
> + * otherwise send IO_CMPL
> + */
> + if (status == 0 && io->auto_resp)
> + flags |= EFCT_SCSI_IO_CMPL_RSP_SENT;
> + else
> + flags |= EFCT_SCSI_IO_CMPL;
> +
> + switch (status) {
> + case SLI4_FC_WCQE_STATUS_SUCCESS:
> + scsi_stat = EFCT_SCSI_STATUS_GOOD;
> + break;
> + case SLI4_FC_WCQE_STATUS_DI_ERROR:
> + if (ext_status & SLI4_FC_DI_ERROR_GE)
> + scsi_stat = EFCT_SCSI_STATUS_DIF_GUARD_ERR;
> + else if (ext_status & SLI4_FC_DI_ERROR_AE)
> + scsi_stat = EFCT_SCSI_STATUS_DIF_APP_TAG_ERROR;
> + else if (ext_status & SLI4_FC_DI_ERROR_RE)
> + scsi_stat = EFCT_SCSI_STATUS_DIF_REF_TAG_ERROR;
> + else
> + scsi_stat = EFCT_SCSI_STATUS_DIF_UNKNOWN_ERROR;
> + break;
> + case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
> + switch (ext_status) {
> + case SLI4_FC_LOCAL_REJECT_INVALID_RELOFFSET:
> + case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
> + scsi_stat = EFCT_SCSI_STATUS_ABORTED;
> + break;
> + case SLI4_FC_LOCAL_REJECT_INVALID_RPI:
> + scsi_stat = EFCT_SCSI_STATUS_NEXUS_LOST;
> + break;
> + case SLI4_FC_LOCAL_REJECT_NO_XRI:
> + scsi_stat = EFCT_SCSI_STATUS_NO_IO;
> + break;
> + default:
> + /*we have seen 0x0d(TX_DMA_FAILED err)*/
> + scsi_stat = EFCT_SCSI_STATUS_ERROR;
> + break;
> + }
> + break;
> +
> + case SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT:
> + /* target IO timed out */
> + scsi_stat = EFCT_SCSI_STATUS_TIMEDOUT_AND_ABORTED;
> + break;
> +
> + case SLI4_FC_WCQE_STATUS_SHUTDOWN:
> + /* Target IO cancelled by HW */
> + scsi_stat = EFCT_SCSI_STATUS_SHUTDOWN;
> + break;
> +
> + default:
> + scsi_stat = EFCT_SCSI_STATUS_ERROR;
> + break;
> + }
> +
> + cb(io, scsi_stat, flags, io->scsi_tgt_cb_arg);
> + }
> + efct_scsi_check_pending(efct);
> +}
> +
> +static int
> +efct_scsi_build_sgls(struct efct_hw *hw, struct efct_hw_io *hio,
> + struct efct_scsi_sgl *sgl, u32 sgl_count,
> + enum efct_hw_io_type type)
> +{
> + int rc;
> + u32 i;
> + struct efct *efct = hw->os;
> +
> + /* Initialize HW SGL */
> + rc = efct_hw_io_init_sges(hw, hio, type);
> + if (rc) {
> + efc_log_err(efct, "efct_hw_io_init_sges failed: %d\n", rc);
> + return EFC_FAIL;
> + }
> +
> + for (i = 0; i < sgl_count; i++) {
> +
> + /* Add data SGE */
> + rc = efct_hw_io_add_sge(hw, hio,
> + sgl[i].addr, sgl[i].len);

fits on one line

> + if (rc) {
> + efc_log_err(efct,
> + "add sge failed cnt=%d rc=%d\n",
> + sgl_count, rc);
> + return rc;
> + }
> + }
> +
> + return EFC_SUCCESS;
> +}
> +
> +static void efc_log_sgl(struct efct_io *io)
> +{
> + struct efct_hw_io *hio = io->hio;
> + struct sli4_sge *data = NULL;
> + u32 *dword = NULL;
> + u32 i;
> + u32 n_sge;
> +
> + scsi_io_trace(io, "def_sgl at 0x%x 0x%08x\n",
> + upper_32_bits(hio->def_sgl.phys),
> + lower_32_bits(hio->def_sgl.phys));
> + n_sge = (hio->sgl == &hio->def_sgl ?
> + hio->n_sge : hio->def_sgl_count);

fits on one line

> + for (i = 0, data = hio->def_sgl.virt; i < n_sge; i++, data++) {
> + dword = (u32 *)data;
> +
> + scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n",
> + i, dword[0], dword[1], dword[2], dword[3]);
> +
> + if (dword[2] & (1U << 31))
> + break;
> + }
> +
> +}
> +
> +static int
> +efct_scsi_check_pending_async_cb(struct efct_hw *hw, int status,
> + u8 *mqe, void *arg)
> +{
> + struct efct_io *io = arg;
> +
> + if (io) {
> + if (io->hw_cb) {
> + efct_hw_done_t cb = io->hw_cb;
> +
> + io->hw_cb = NULL;
> + (cb)(io->hio, NULL, 0,
> + SLI4_FC_WCQE_STATUS_DISPATCH_ERROR, 0, io);
> + }
> + }
> + return EFC_SUCCESS;
> +}
> +
> +static int
> +efct_scsi_io_dispatch_hw_io(struct efct_io *io, struct efct_hw_io *hio)
> +{
> + int rc = 0;

EFC_SUCCESS ?

> + struct efct *efct = io->efct;
> +
> + /* Got a HW IO;
> + * update ini/tgt_task_tag with HW IO info and dispatch
> + */
> + io->hio = hio;
> + if (io->cmd_tgt)
> + io->tgt_task_tag = hio->indicator;
> + else if (io->cmd_ini)
> + io->init_task_tag = hio->indicator;
> + io->hw_tag = hio->reqtag;
> +
> + hio->eq = io->hw_priv;
> +
> + /* Copy WQ steering */
> + switch (io->wq_steering) {
> + case EFCT_SCSI_WQ_STEERING_CLASS >> EFCT_SCSI_WQ_STEERING_SHIFT:
> + hio->wq_steering = EFCT_HW_WQ_STEERING_CLASS;
> + break;
> + case EFCT_SCSI_WQ_STEERING_REQUEST >> EFCT_SCSI_WQ_STEERING_SHIFT:
> + hio->wq_steering = EFCT_HW_WQ_STEERING_REQUEST;
> + break;
> + case EFCT_SCSI_WQ_STEERING_CPU >> EFCT_SCSI_WQ_STEERING_SHIFT:
> + hio->wq_steering = EFCT_HW_WQ_STEERING_CPU;
> + break;
> + }
> +
> + switch (io->io_type) {
> + case EFCT_IO_TYPE_IO:
> + rc = efct_scsi_build_sgls(&efct->hw, io->hio,
> + io->sgl, io->sgl_count, io->hio_type);
> + if (rc)
> + break;
> +
> + if (EFCT_LOG_ENABLE_SCSI_TRACE(efct))
> + efc_log_sgl(io);
> +
> + if (io->app_id)
> + io->iparam.fcp_tgt.app_id = io->app_id;
> +
> + rc = efct_hw_io_send(&io->efct->hw, io->hio_type, io->hio,
> + io->wire_len, &io->iparam,
> + &io->node->rnode, io->hw_cb, io);
> + break;
> + case EFCT_IO_TYPE_ELS:
> + case EFCT_IO_TYPE_CT:
> + rc = efct_hw_srrs_send(&efct->hw, io->hio_type, io->hio,
> + &io->els_req, io->wire_len,
> + &io->els_rsp, &io->node->rnode, &io->iparam,
> + io->hw_cb, io);
> + break;
> + case EFCT_IO_TYPE_CT_RESP:
> + rc = efct_hw_srrs_send(&efct->hw, io->hio_type, io->hio,
> + &io->els_rsp, io->wire_len,
> + NULL, &io->node->rnode, &io->iparam,
> + io->hw_cb, io);
> + break;
> + case EFCT_IO_TYPE_BLS_RESP:
> + /* no need to update tgt_task_tag for BLS response since
> + * the RX_ID will be specified by the payload, not the XRI
> + */
> + rc = efct_hw_srrs_send(&efct->hw, io->hio_type, io->hio,
> + NULL, 0, NULL, &io->node->rnode,
> + &io->iparam, io->hw_cb, io);
> + break;
> + default:
> + scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
> + rc = -1;

error code

> + break;
> + }
> + return rc;
> +}
> +
> +static int
> +efct_scsi_io_dispatch_no_hw_io(struct efct_io *io)
> +{
> + int rc;
> +
> + switch (io->io_type) {
> + case EFCT_IO_TYPE_ABORT: {
> + struct efct_hw_io *hio_to_abort = NULL;
> +
> + hio_to_abort = io->io_to_abort->hio;
> +
> + if (!hio_to_abort) {
> + /*
> + * If "IO to abort" does not have an
> + * associated HW IO, immediately make callback with
> + * success. The command must have been sent to
> + * the backend, but the data phase has not yet
> + * started, so we don't have a HW IO.
> + *
> + * Note: since the backend shims should be
> + * taking a reference on io_to_abort, it should not
> + * be possible to have been completed and freed by
> + * the backend before the abort got here.
> + */
> + scsi_io_printf(io, "IO: not active\n");
> + ((efct_hw_done_t)io->hw_cb)(io->hio, NULL, 0,
> + SLI4_FC_WCQE_STATUS_SUCCESS, 0, io);
> + rc = 0;

same as in above function

> + } else {
> + /* HW IO is valid, abort it */
> + scsi_io_printf(io, "aborting\n");
> + rc = efct_hw_io_abort(&io->efct->hw, hio_to_abort,
> + io->send_abts, io->hw_cb, io);
> + if (rc) {
> + int status = SLI4_FC_WCQE_STATUS_SUCCESS;
> +
> + if (rc != EFCT_HW_RTN_IO_NOT_ACTIVE &&
> + rc != EFCT_HW_RTN_IO_ABORT_IN_PROGRESS) {
> + status = -1;
> + scsi_io_printf(io,
> + "Failed to abort IO: status=%d\n",
> + rc);
> + }
> + ((efct_hw_done_t)io->hw_cb)(io->hio,
> + NULL, 0, status, 0, io);
> + rc = 0;
> + }
> + }
> +
> + break;
> + }
> + default:
> + scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
> + rc = -1;
> + break;
> + }
> + return rc;
> +}
> +
> +/**
> + * Check for pending IOs to dispatch.
> + *
> + * If there are IOs on the pending list, and a HW IO is available, then
> + * dispatch the IOs.
> + */

proper kerneldoc style?

> +void
> +efct_scsi_check_pending(struct efct *efct)
> +{
> + struct efct_xport *xport = efct->xport;
> + struct efct_io *io = NULL;
> + struct efct_hw_io *hio;
> + int status;
> + int count = 0;
> + int dispatch;
> + unsigned long flags = 0;
> +
> + /* Guard against recursion */
> + if (atomic_add_return(1, &xport->io_pending_recursing)) {
> + /* This function is already running. Decrement and return. */
> + atomic_sub_return(1, &xport->io_pending_recursing);
> + return;
> + }
> +
> + do {
> + spin_lock_irqsave(&xport->io_pending_lock, flags);
> + status = 0;
> + hio = NULL;
> + if (!list_empty(&xport->io_pending_list)) {
> + io = list_first_entry(&xport->io_pending_list,
> + struct efct_io,
> + io_pending_link);
> + }
> + if (io) {

Couldn't this part of the above if body? Could be io == NULL when
popped from the list?

> + list_del(&io->io_pending_link);

This says no

> + if (io->io_type == EFCT_IO_TYPE_ABORT) {
> + hio = NULL;
> + } else {
> + hio = efct_hw_io_alloc(&efct->hw);
> + if (!hio) {
> + /*
> + * No HW IO available.Put IO back on
> + * the front of pending list
> + */
> + list_add(&xport->io_pending_list,
> + &io->io_pending_link);
> + io = NULL;
> + } else {
> + hio->eq = io->hw_priv;
> + }
> + }
> + }
> + /* Must drop the lock before dispatching the IO */
> + spin_unlock_irqrestore(&xport->io_pending_lock, flags);
> +
> + if (io) {
> + count++;
> +
> + /*
> + * We pulled an IO off the pending list,
> + * and either got an HW IO or don't need one
> + */
> + atomic_sub_return(1, &xport->io_pending_count);
> + if (!hio)
> + status = efct_scsi_io_dispatch_no_hw_io(io);
> + else
> + status = efct_scsi_io_dispatch_hw_io(io, hio);
> + if (status) {
> + /*
> + * Invoke the HW callback, but do so in the
> + * separate execution context,provided by the
> + * NOP mailbox completion processing context
> + * by using efct_hw_async_call()
> + */
> + if (efct_hw_async_call(&efct->hw,
> + efct_scsi_check_pending_async_cb,
> + io)) {
> + efc_log_test(efct,
> + "call hw async failed\n");
> + }
> + }
> + }
> + } while (io);

This function is a bit long, I think it would be good to split it up.

> +
> + /*
> + * If nothing was removed from the list,
> + * we might be in a case where we need to abort an
> + * active IO and the abort is on the pending list.
> + * Look for an abort we can dispatch.
> + */
> + if (count == 0) {
> + dispatch = 0;
> +
> + spin_lock_irqsave(&xport->io_pending_lock, flags);
> + list_for_each_entry(io, &xport->io_pending_list,
> + io_pending_link) {
> + if (io->io_type == EFCT_IO_TYPE_ABORT) {
> + if (io->io_to_abort->hio) {
> + /* This IO has a HW IO, so it is
> + * active. Dispatch the abort.
> + */
> + dispatch = 1;
> + } else {
> + /* Leave this abort on the pending
> + * list and keep looking
> + */
> + dispatch = 0;
> + }
> + }
> + if (dispatch) {
> + list_del(&io->io_pending_link);
> + atomic_sub_return(1, &xport->io_pending_count);
> + break;
> + }
> + }
> + spin_unlock_irqrestore(&xport->io_pending_lock, flags);
> +
> + if (dispatch) {
> + status = efct_scsi_io_dispatch_no_hw_io(io);
> + if (status) {
> + if (efct_hw_async_call(&efct->hw,
> + efct_scsi_check_pending_async_cb,
> + io)) {
> + efc_log_test(efct,
> + "call to hw async failed\n");
> + }
> + }
> + }
> + }
> +
> + atomic_sub_return(1, &xport->io_pending_recursing);
> +}
> +
> +/**
> + * An IO is dispatched:
> + * - if the pending list is not empty, add IO to pending list
> + * and call a function to process the pending list.
> + * - if pending list is empty, try to allocate a HW IO. If none
> + * is available, place this IO at the tail of the pending IO
> + * list.
> + * - if HW IO is available, attach this IO to the HW IO and
> + * submit it.
> + */

proper kerneldoc style?

> +int
> +efct_scsi_io_dispatch(struct efct_io *io, void *cb)
> +{
> + struct efct_hw_io *hio;
> + struct efct *efct = io->efct;
> + struct efct_xport *xport = efct->xport;
> + unsigned long flags = 0;
> +
> + io->hw_cb = cb;
> +
> + /*
> + * if this IO already has a HW IO, then this is either
> + * not the first phase of the IO. Send it to the HW.
> + */
> + if (io->hio)
> + return efct_scsi_io_dispatch_hw_io(io, io->hio);
> +
> + /*
> + * We don't already have a HW IO associated with the IO. First check
> + * the pending list. If not empty, add IO to the tail and process the
> + * pending list.
> + */
> + spin_lock_irqsave(&xport->io_pending_lock, flags);
> + if (!list_empty(&xport->io_pending_list)) {
> + /*
> + * If this is a low latency request,
> + * the put at the front of the IO pending
> + * queue, otherwise put it at the end of the queue.
> + */
> + if (io->low_latency) {
> + INIT_LIST_HEAD(&io->io_pending_link);
> + list_add(&xport->io_pending_list,
> + &io->io_pending_link);
> + } else {
> + INIT_LIST_HEAD(&io->io_pending_link);
> + list_add_tail(&io->io_pending_link,
> + &xport->io_pending_list);
> + }
> + spin_unlock_irqrestore(&xport->io_pending_lock, flags);
> + atomic_add_return(1, &xport->io_pending_count);
> + atomic_add_return(1, &xport->io_total_pending);
> +
> + /* process pending list */
> + efct_scsi_check_pending(efct);
> + return EFC_SUCCESS;
> + }

no need to indent

> + spin_unlock_irqrestore(&xport->io_pending_lock, flags);
> +
> + /*
> + * We don't have a HW IO associated with the IO and there's nothing
> + * on the pending list. Attempt to allocate a HW IO and dispatch it.
> + */
> + hio = efct_hw_io_alloc(&io->efct->hw);
> + if (!hio) {
> + /* Couldn't get a HW IO. Save this IO on the pending list */
> + spin_lock_irqsave(&xport->io_pending_lock, flags);
> + INIT_LIST_HEAD(&io->io_pending_link);
> + list_add_tail(&io->io_pending_link, &xport->io_pending_list);
> + spin_unlock_irqrestore(&xport->io_pending_lock, flags);
> +
> + atomic_add_return(1, &xport->io_total_pending);
> + atomic_add_return(1, &xport->io_pending_count);
> + return EFC_SUCCESS;
> + }
> +
> + /* We successfully allocated a HW IO; dispatch to HW */
> + return efct_scsi_io_dispatch_hw_io(io, hio);
> +}
> +
> +/**
> + * An Abort IO is dispatched:
> + * - if the pending list is not empty, add IO to pending list
> + * and call a function to process the pending list.
> + * - if pending list is empty, send abort to the HW.
> + */

not proper kerneldoc style

> +
> +int
> +efct_scsi_io_dispatch_abort(struct efct_io *io, void *cb)
> +{
> + struct efct *efct = io->efct;
> + struct efct_xport *xport = efct->xport;
> + unsigned long flags = 0;
> +
> + io->hw_cb = cb;
> +
> + /*
> + * For aborts, we don't need a HW IO, but we still want
> + * to pass through the pending list to preserve ordering.
> + * Thus, if the pending list is not empty, add this abort
> + * to the pending list and process the pending list.
> + */
> + spin_lock_irqsave(&xport->io_pending_lock, flags);
> + if (!list_empty(&xport->io_pending_list)) {
> + INIT_LIST_HEAD(&io->io_pending_link);
> + list_add_tail(&io->io_pending_link,
> + &xport->io_pending_list);
> + spin_unlock_irqrestore(&xport->io_pending_lock, flags);
> + atomic_add_return(1, &xport->io_pending_count);
> + atomic_add_return(1, &xport->io_total_pending);
> +
> + /* process pending list */
> + efct_scsi_check_pending(efct);
> + return EFC_SUCCESS;
> + }

no need to indent

> + spin_unlock_irqrestore(&xport->io_pending_lock, flags);
> +
> + /* nothing on pending list, dispatch abort */
> + return efct_scsi_io_dispatch_no_hw_io(io);
> +}
> +
> +static inline int
> +efct_scsi_xfer_data(struct efct_io *io, u32 flags,
> + struct efct_scsi_sgl *sgl, u32 sgl_count, u64 xwire_len,
> + enum efct_hw_io_type type, int enable_ar,
> + efct_scsi_io_cb_t cb, void *arg)
> +{
> + struct efct *efct;
> + size_t residual = 0;
> +
> + io->sgl_count = sgl_count;
> +
> + efct = io->efct;
> +
> + scsi_io_trace(io, "%s wire_len %llu\n",
> + (type == EFCT_HW_IO_TARGET_READ) ? "send" : "recv",
> + xwire_len);
> +
> + io->hio_type = type;
> +
> + io->scsi_tgt_cb = cb;
> + io->scsi_tgt_cb_arg = arg;
> +
> + residual = io->exp_xfer_len - io->transferred;
> + io->wire_len = (xwire_len < residual) ? xwire_len : residual;
> + residual = (xwire_len - io->wire_len);
> +
> + memset(&io->iparam, 0, sizeof(io->iparam));
> + io->iparam.fcp_tgt.ox_id = io->init_task_tag;
> + io->iparam.fcp_tgt.offset = io->transferred;
> + io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
> + io->iparam.fcp_tgt.timeout = io->timeout;
> +
> + /* if this is the last data phase and there is no residual, enable
> + * auto-good-response
> + */
> + if (enable_ar && (flags & EFCT_SCSI_LAST_DATAPHASE) &&
> + residual == 0 &&
> + ((io->transferred + io->wire_len) == io->exp_xfer_len) &&
> + (!(flags & EFCT_SCSI_NO_AUTO_RESPONSE))) {

This should be reformated in a proper way.

> + io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
> + io->auto_resp = true;
> + } else {
> + io->auto_resp = false;
> + }
> +
> + /* save this transfer length */
> + io->xfer_req = io->wire_len;
> +
> + /* Adjust the transferred count to account for overrun
> + * when the residual is calculated in efct_scsi_send_resp
> + */
> + io->transferred += residual;
> +
> + /* Adjust the SGL size if there is overrun */
> +
> + if (residual) {
> + struct efct_scsi_sgl *sgl_ptr = &io->sgl[sgl_count - 1];
> +
> + while (residual) {
> + size_t len = sgl_ptr->len;
> +
> + if (len > residual) {
> + sgl_ptr->len = len - residual;
> + residual = 0;
> + } else {
> + sgl_ptr->len = 0;
> + residual -= len;
> + io->sgl_count--;
> + }
> + sgl_ptr--;
> + }
> + }
> +
> + /* Set latency and WQ steering */
> + io->low_latency = (flags & EFCT_SCSI_LOW_LATENCY) != 0;
> + io->wq_steering = (flags & EFCT_SCSI_WQ_STEERING_MASK) >>
> + EFCT_SCSI_WQ_STEERING_SHIFT;
> + io->wq_class = (flags & EFCT_SCSI_WQ_CLASS_MASK) >>
> + EFCT_SCSI_WQ_CLASS_SHIFT;
> +
> + if (efct->xport) {
> + struct efct_xport *xport = efct->xport;
> +
> + if (type == EFCT_HW_IO_TARGET_READ) {
> + xport->fcp_stats.input_requests++;
> + xport->fcp_stats.input_bytes += xwire_len;
> + } else if (type == EFCT_HW_IO_TARGET_WRITE) {
> + xport->fcp_stats.output_requests++;
> + xport->fcp_stats.output_bytes += xwire_len;
> + }
> + }
> + return efct_scsi_io_dispatch(io, efct_target_io_cb);
> +}
> +
> +int
> +efct_scsi_send_rd_data(struct efct_io *io, u32 flags,
> + struct efct_scsi_sgl *sgl, u32 sgl_count, u64 len,
> + efct_scsi_io_cb_t cb, void *arg)
> +{
> + return efct_scsi_xfer_data(io, flags, sgl, sgl_count,
> + len, EFCT_HW_IO_TARGET_READ,
> + enable_tsend_auto_resp(io->efct), cb, arg);
> +}
> +
> +int
> +efct_scsi_recv_wr_data(struct efct_io *io, u32 flags,
> + struct efct_scsi_sgl *sgl, u32 sgl_count, u64 len,
> + efct_scsi_io_cb_t cb, void *arg)
> +{
> + return efct_scsi_xfer_data(io, flags, sgl, sgl_count, len,
> + EFCT_HW_IO_TARGET_WRITE,
> + enable_treceive_auto_resp(io->efct), cb, arg);
> +}
> +
> +int
> +efct_scsi_send_resp(struct efct_io *io, u32 flags,
> + struct efct_scsi_cmd_resp *rsp,
> + efct_scsi_io_cb_t cb, void *arg)
> +{
> + struct efct *efct;
> + int residual;
> + bool auto_resp = true; /* Always try auto resp */
> + u8 scsi_status = 0;
> + u16 scsi_status_qualifier = 0;
> + u8 *sense_data = NULL;
> + u32 sense_data_length = 0;
> +
> + efct = io->efct;
> +
> + if (rsp) {
> + scsi_status = rsp->scsi_status;
> + scsi_status_qualifier = rsp->scsi_status_qualifier;
> + sense_data = rsp->sense_data;
> + sense_data_length = rsp->sense_data_length;
> + residual = rsp->residual;
> + } else {
> + residual = io->exp_xfer_len - io->transferred;
> + }
> +
> + io->wire_len = 0;
> + io->hio_type = EFCT_HW_IO_TARGET_RSP;
> +
> + io->scsi_tgt_cb = cb;
> + io->scsi_tgt_cb_arg = arg;
> +
> + memset(&io->iparam, 0, sizeof(io->iparam));
> + io->iparam.fcp_tgt.ox_id = io->init_task_tag;
> + io->iparam.fcp_tgt.offset = 0;
> + io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
> + io->iparam.fcp_tgt.timeout = io->timeout;
> +
> + /* Set low latency queueing request */
> + io->low_latency = (flags & EFCT_SCSI_LOW_LATENCY) != 0;
> + io->wq_steering = (flags & EFCT_SCSI_WQ_STEERING_MASK) >>
> + EFCT_SCSI_WQ_STEERING_SHIFT;
> + io->wq_class = (flags & EFCT_SCSI_WQ_CLASS_MASK) >>
> + EFCT_SCSI_WQ_CLASS_SHIFT;
> +
> + if (scsi_status != 0 || residual || sense_data_length) {
> + struct fcp_resp_with_ext *fcprsp = io->rspbuf.virt;
> + u8 *sns_data = io->rspbuf.virt + sizeof(*fcprsp);
> +
> + if (!fcprsp) {
> + efc_log_err(efct, "NULL response buffer\n");
> + return EFC_FAIL;
> + }
> +
> + auto_resp = false;
> +
> + memset(fcprsp, 0, sizeof(*fcprsp));
> +
> + io->wire_len += sizeof(*fcprsp);
> +
> + fcprsp->resp.fr_status = scsi_status;
> + fcprsp->resp.fr_retry_delay =
> + cpu_to_be16(scsi_status_qualifier);
> +
> + /* set residual status if necessary */
> + if (residual != 0) {
> + /* FCP: if data transferred is less than the
> + * amount expected, then this is an underflow.
> + * If data transferred would have been greater
> + * than the amount expected this is an overflow
> + */
> + if (residual > 0) {
> + fcprsp->resp.fr_flags |= FCP_RESID_UNDER;
> + fcprsp->ext.fr_resid = cpu_to_be32(residual);
> + } else {
> + fcprsp->resp.fr_flags |= FCP_RESID_OVER;
> + fcprsp->ext.fr_resid = cpu_to_be32(-residual);
> + }
> + }
> +
> + if (EFCT_SCSI_SNS_BUF_VALID(sense_data) && sense_data_length) {
> + if (sense_data_length > SCSI_SENSE_BUFFERSIZE) {
> + efc_log_err(efct, "Sense exceeds max size.\n");
> + return EFC_FAIL;
> + }
> +
> + fcprsp->resp.fr_flags |= FCP_SNS_LEN_VAL;
> + memcpy(sns_data, sense_data, sense_data_length);
> + fcprsp->ext.fr_sns_len = cpu_to_be32(sense_data_length);
> + io->wire_len += sense_data_length;
> + }
> +
> + io->sgl[0].addr = io->rspbuf.phys;
> + //io->sgl[0].dif_addr = 0;

debug left over

> + io->sgl[0].len = io->wire_len;
> + io->sgl_count = 1;
> + }
> +
> + if (auto_resp)
> + io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
> +
> + return efct_scsi_io_dispatch(io, efct_target_io_cb);
> +}
> +
> +static int
> +efct_target_bls_resp_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;
> + struct efct *efct;
> + enum efct_scsi_io_status bls_status;
> +
> + efct = io->efct;
> +
> + /* BLS isn't really a "SCSI" concept, but use SCSI status */
> + if (status) {
> + io_error_log(io, "s=%#x x=%#x\n", status, ext_status);
> + bls_status = EFCT_SCSI_STATUS_ERROR;
> + } else {
> + bls_status = EFCT_SCSI_STATUS_GOOD;
> + }
> +
> + if (io->bls_cb) {
> + efct_scsi_io_cb_t bls_cb = io->bls_cb;
> + void *bls_cb_arg = io->bls_cb_arg;
> +
> + io->bls_cb = NULL;
> + io->bls_cb_arg = NULL;
> +
> + /* invoke callback */
> + bls_cb(io, bls_status, 0, bls_cb_arg);
> + }
> +
> + efct_scsi_check_pending(efct);
> + return EFC_SUCCESS;
> +}
> +
> +static int
> +efct_target_send_bls_resp(struct efct_io *io,
> + efct_scsi_io_cb_t cb, void *arg)
> +{
> + int rc;
> + struct fc_ba_acc *acc;
> +
> + /* fill out IO structure with everything needed to send BA_ACC */
> + memset(&io->iparam, 0, sizeof(io->iparam));
> + io->iparam.bls.ox_id = io->init_task_tag;
> + io->iparam.bls.rx_id = io->abort_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);
> +
> + /* generic io fields have already been populated */
> +
> + /* set type and BLS-specific fields */
> + io->io_type = EFCT_IO_TYPE_BLS_RESP;
> + io->display_name = "bls_rsp";
> + io->hio_type = EFCT_HW_BLS_ACC;
> + io->bls_cb = cb;
> + io->bls_cb_arg = arg;
> +
> + /* dispatch IO */
> + rc = efct_scsi_io_dispatch(io, efct_target_bls_resp_cb);
> + return rc;
> +}
> +
> +int
> +efct_scsi_send_tmf_resp(struct efct_io *io,
> + enum efct_scsi_tmf_resp rspcode,
> + u8 addl_rsp_info[3],
> + efct_scsi_io_cb_t cb, void *arg)
> +{
> + int rc = -1;

again the error code topic.

> + struct fcp_resp_with_ext *fcprsp = NULL;
> + struct fcp_resp_rsp_info *rspinfo = NULL;
> + u8 fcp_rspcode;
> +
> + io->wire_len = 0;
> +
> + switch (rspcode) {
> + case EFCT_SCSI_TMF_FUNCTION_COMPLETE:
> + fcp_rspcode = FCP_TMF_CMPL;
> + break;
> + case EFCT_SCSI_TMF_FUNCTION_SUCCEEDED:
> + case EFCT_SCSI_TMF_FUNCTION_IO_NOT_FOUND:
> + fcp_rspcode = FCP_TMF_CMPL;
> + break;
> + case EFCT_SCSI_TMF_FUNCTION_REJECTED:
> + fcp_rspcode = FCP_TMF_REJECTED;
> + break;
> + case EFCT_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER:
> + fcp_rspcode = FCP_TMF_INVALID_LUN;
> + break;
> + case EFCT_SCSI_TMF_SERVICE_DELIVERY:
> + fcp_rspcode = FCP_TMF_FAILED;
> + break;
> + default:
> + fcp_rspcode = FCP_TMF_REJECTED;
> + break;
> + }
> +
> + io->hio_type = EFCT_HW_IO_TARGET_RSP;
> +
> + io->scsi_tgt_cb = cb;
> + io->scsi_tgt_cb_arg = arg;
> +
> + if (io->tmf_cmd == EFCT_SCSI_TMF_ABORT_TASK) {
> + rc = efct_target_send_bls_resp(io, cb, arg);
> + return rc;
> + }
> +
> + /* populate the FCP TMF response */
> + fcprsp = io->rspbuf.virt;
> + memset(fcprsp, 0, sizeof(*fcprsp));
> +
> + fcprsp->resp.fr_flags |= FCP_SNS_LEN_VAL;
> +
> + rspinfo = io->rspbuf.virt + sizeof(*fcprsp);
> + if (addl_rsp_info) {
> + memcpy(rspinfo->_fr_resvd, addl_rsp_info,
> + sizeof(rspinfo->_fr_resvd));
> + }
> + rspinfo->rsp_code = fcp_rspcode;
> +
> + io->wire_len = sizeof(*fcprsp) + sizeof(*rspinfo);
> +
> + fcprsp->ext.fr_rsp_len = cpu_to_be32(sizeof(*rspinfo));
> +
> + io->sgl[0].addr = io->rspbuf.phys;
> + io->sgl[0].dif_addr = 0;
> + io->sgl[0].len = io->wire_len;
> + io->sgl_count = 1;
> +
> + memset(&io->iparam, 0, sizeof(io->iparam));
> + io->iparam.fcp_tgt.ox_id = io->init_task_tag;
> + io->iparam.fcp_tgt.offset = 0;
> + io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
> + io->iparam.fcp_tgt.timeout = io->timeout;
> +
> + rc = efct_scsi_io_dispatch(io, efct_target_io_cb);
> +
> + return rc;
> +}
> +
> +static int
> +efct_target_abort_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;
> + struct efct *efct;
> + enum efct_scsi_io_status scsi_status;
> +
> + efct = io->efct;
> +
> + if (io->abort_cb) {

I would move follwing part again into new function.

> + efct_scsi_io_cb_t abort_cb = io->abort_cb;
> + void *abort_cb_arg = io->abort_cb_arg;
> +
> + io->abort_cb = NULL;
> + io->abort_cb_arg = NULL;
> +
> + switch (status) {
> + case SLI4_FC_WCQE_STATUS_SUCCESS:
> + scsi_status = EFCT_SCSI_STATUS_GOOD;
> + break;
> + case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
> + switch (ext_status) {
> + case SLI4_FC_LOCAL_REJECT_NO_XRI:
> + scsi_status = EFCT_SCSI_STATUS_NO_IO;
> + break;
> + case SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS:
> + scsi_status =
> + EFCT_SCSI_STATUS_ABORT_IN_PROGRESS;
> + break;
> + default:
> + /*we have seen 0x15 (abort in progress)*/
> + scsi_status = EFCT_SCSI_STATUS_ERROR;
> + break;
> + }
> + break;
> + case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE:
> + scsi_status = EFCT_SCSI_STATUS_CHECK_RESPONSE;
> + break;
> + default:
> + scsi_status = EFCT_SCSI_STATUS_ERROR;
> + break;
> + }
> + /* invoke callback */
> + abort_cb(io->io_to_abort, scsi_status, 0, abort_cb_arg);
> + }
> +
> + /* done with IO to abort,efct_ref_get(): efct_scsi_tgt_abort_io() */
> + kref_put(&io->io_to_abort->ref, io->io_to_abort->release);
> +
> + efct_io_pool_io_free(efct->xport->io_pool, io);
> +
> + efct_scsi_check_pending(efct);
> + return EFC_SUCCESS;
> +}
> +
> +int
> +efct_scsi_tgt_abort_io(struct efct_io *io, efct_scsi_io_cb_t cb, void *arg)
> +{
> + struct efct *efct;
> + struct efct_xport *xport;
> + int rc;
> + struct efct_io *abort_io = NULL;
> +
> + efct = io->efct;
> + xport = efct->xport;
> +
> + /* take a reference on IO being aborted */
> + if ((kref_get_unless_zero(&io->ref) == 0)) {

the double brackets are not needed

> + /* command no longer active */
> + scsi_io_printf(io, "command no longer active\n");
> + return EFC_FAIL;
> + }
> +
> + /*
> + * allocate a new IO to send the abort request. Use efct_io_alloc()
> + * directly, as we need an IO object that will not fail allocation
> + * due to allocations being disabled (in efct_scsi_io_alloc())
> + */
> + abort_io = efct_io_pool_io_alloc(efct->xport->io_pool);
> + if (!abort_io) {
> + atomic_add_return(1, &xport->io_alloc_failed_count);
> + kref_put(&io->ref, io->release);
> + return EFC_FAIL;
> + }
> +
> + /* Save the target server callback and argument */
> + /* set generic fields */
> + abort_io->cmd_tgt = true;
> + abort_io->node = io->node;
> +
> + /* set type and abort-specific fields */
> + abort_io->io_type = EFCT_IO_TYPE_ABORT;
> + abort_io->display_name = "tgt_abort";
> + abort_io->io_to_abort = io;
> + abort_io->send_abts = false;
> + abort_io->abort_cb = cb;
> + abort_io->abort_cb_arg = arg;
> +
> + /* now dispatch IO */
> + rc = efct_scsi_io_dispatch_abort(abort_io, efct_target_abort_cb);
> + if (rc)
> + kref_put(&io->ref, io->release);
> + return rc;
> +}
> +
> +void
> +efct_scsi_io_complete(struct efct_io *io)
> +{
> + if (io->io_free) {
> + efc_log_test(io->efct,
> + "Got completion for non-busy io with tag 0x%x\n",
> + io->tag);

code aligment

> + return;
> + }
> +
> + scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
> + kref_put(&io->ref, io->release);
> +}
> diff --git a/drivers/scsi/elx/efct/efct_scsi.h b/drivers/scsi/elx/efct/efct_scsi.h
> new file mode 100644
> index 000000000000..28204c5fde69
> --- /dev/null
> +++ b/drivers/scsi/elx/efct/efct_scsi.h
> @@ -0,0 +1,235 @@
> +/* 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_SCSI_H__)
> +#define __EFCT_SCSI_H__
> +#include <scsi/scsi_host.h>
> +#include <scsi/scsi_transport_fc.h>
> +
> +/* efct_scsi_rcv_cmd() efct_scsi_rcv_tmf() flags */
> +#define EFCT_SCSI_CMD_DIR_IN (1 << 0)
> +#define EFCT_SCSI_CMD_DIR_OUT (1 << 1)
> +#define EFCT_SCSI_CMD_SIMPLE (1 << 2)
> +#define EFCT_SCSI_CMD_HEAD_OF_QUEUE (1 << 3)
> +#define EFCT_SCSI_CMD_ORDERED (1 << 4)
> +#define EFCT_SCSI_CMD_UNTAGGED (1 << 5)
> +#define EFCT_SCSI_CMD_ACA (1 << 6)
> +#define EFCT_SCSI_FIRST_BURST_ERR (1 << 7)
> +#define EFCT_SCSI_FIRST_BURST_ABORTED (1 << 8)
> +
> +/* efct_scsi_send_rd_data/recv_wr_data/send_resp flags */
> +#define EFCT_SCSI_LAST_DATAPHASE (1 << 0)
> +#define EFCT_SCSI_NO_AUTO_RESPONSE (1 << 1)
> +#define EFCT_SCSI_LOW_LATENCY (1 << 2)
> +
> +#define EFCT_SCSI_SNS_BUF_VALID(sense) ((sense) && \
> + (0x70 == (((const u8 *)(sense))[0] & 0x70)))
> +
> +#define EFCT_SCSI_WQ_STEERING_SHIFT 16
> +#define EFCT_SCSI_WQ_STEERING_MASK (0xf << EFCT_SCSI_WQ_STEERING_SHIFT)
> +#define EFCT_SCSI_WQ_STEERING_CLASS (0 << EFCT_SCSI_WQ_STEERING_SHIFT)
> +#define EFCT_SCSI_WQ_STEERING_REQUEST (1 << EFCT_SCSI_WQ_STEERING_SHIFT)
> +#define EFCT_SCSI_WQ_STEERING_CPU (2 << EFCT_SCSI_WQ_STEERING_SHIFT)
> +
> +#define EFCT_SCSI_WQ_CLASS_SHIFT (20)
> +#define EFCT_SCSI_WQ_CLASS_MASK (0xf << EFCT_SCSI_WQ_CLASS_SHIFT)
> +#define EFCT_SCSI_WQ_CLASS(x) ((x & EFCT_SCSI_WQ_CLASS_MASK) << \
> + EFCT_SCSI_WQ_CLASS_SHIFT)
> +
> +#define EFCT_SCSI_WQ_CLASS_LOW_LATENCY 1
> +
> +struct efct_scsi_cmd_resp {
> + u8 scsi_status; /* SCSI status */

kerneldoc

> + u16 scsi_status_qualifier; /* SCSI status qualifier */
> + /* pointer to response data buffer */
> + u8 *response_data;
> + /* length of response data buffer (bytes) */
> + u32 response_data_length;
> + u8 *sense_data; /* pointer to sense data buffer */
> + /* length of sense data buffer (bytes) */
> + u32 sense_data_length;
> + /* command residual (not used for target), positive value
> + * indicates an underflow, negative value indicates overflow
> + */
> + int residual;
> + /* Command response length received in wcqe */
> + u32 response_wire_length;
> +};
> +
> +struct efct_vport {
> + struct efct *efct;
> + bool is_vport;
> + struct fc_host_statistics fc_host_stats;
> + struct Scsi_Host *shost;
> + struct fc_vport *fc_vport;
> + u64 npiv_wwpn;
> + u64 npiv_wwnn;
> +};
> +
> +/* Status values returned by IO callbacks */
> +enum efct_scsi_io_status {
> + EFCT_SCSI_STATUS_GOOD = 0,
> + EFCT_SCSI_STATUS_ABORTED,
> + EFCT_SCSI_STATUS_ERROR,
> + EFCT_SCSI_STATUS_DIF_GUARD_ERR,
> + EFCT_SCSI_STATUS_DIF_REF_TAG_ERROR,
> + EFCT_SCSI_STATUS_DIF_APP_TAG_ERROR,
> + EFCT_SCSI_STATUS_DIF_UNKNOWN_ERROR,
> + EFCT_SCSI_STATUS_PROTOCOL_CRC_ERROR,
> + EFCT_SCSI_STATUS_NO_IO,
> + EFCT_SCSI_STATUS_ABORT_IN_PROGRESS,
> + EFCT_SCSI_STATUS_CHECK_RESPONSE,
> + EFCT_SCSI_STATUS_COMMAND_TIMEOUT,
> + EFCT_SCSI_STATUS_TIMEDOUT_AND_ABORTED,
> + EFCT_SCSI_STATUS_SHUTDOWN,
> + EFCT_SCSI_STATUS_NEXUS_LOST,
> +};
> +
> +struct efct_io;
> +struct efc_node;
> +struct efc_domain;
> +struct efc_sli_port;
> +
> +/* Callback used by send_rd_data(), recv_wr_data(), send_resp() */
> +typedef int (*efct_scsi_io_cb_t)(struct efct_io *io,
> + enum efct_scsi_io_status status,
> + u32 flags, void *arg);
> +
> +/* Callback used by send_rd_io(), send_wr_io() */
> +typedef int (*efct_scsi_rsp_io_cb_t)(struct efct_io *io,
> + enum efct_scsi_io_status status,
> + struct efct_scsi_cmd_resp *rsp,
> + u32 flags, void *arg);
> +
> +/* efct_scsi_cb_t flags */
> +#define EFCT_SCSI_IO_CMPL (1 << 0)
> +/* IO completed, response sent */
> +#define EFCT_SCSI_IO_CMPL_RSP_SENT (1 << 1)
> +#define EFCT_SCSI_IO_ABORTED (1 << 2)
> +
> +/* efct_scsi_recv_tmf() request values */
> +enum efct_scsi_tmf_cmd {
> + EFCT_SCSI_TMF_ABORT_TASK = 1,
> + EFCT_SCSI_TMF_QUERY_TASK_SET,
> + EFCT_SCSI_TMF_ABORT_TASK_SET,
> + EFCT_SCSI_TMF_CLEAR_TASK_SET,
> + EFCT_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT,
> + EFCT_SCSI_TMF_LOGICAL_UNIT_RESET,
> + EFCT_SCSI_TMF_CLEAR_ACA,
> + EFCT_SCSI_TMF_TARGET_RESET,
> +};
> +
> +/* efct_scsi_send_tmf_resp() response values */
> +enum efct_scsi_tmf_resp {
> + EFCT_SCSI_TMF_FUNCTION_COMPLETE = 1,
> + EFCT_SCSI_TMF_FUNCTION_SUCCEEDED,
> + EFCT_SCSI_TMF_FUNCTION_IO_NOT_FOUND,
> + EFCT_SCSI_TMF_FUNCTION_REJECTED,
> + EFCT_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER,
> + EFCT_SCSI_TMF_SERVICE_DELIVERY,
> +};
> +
> +struct efct_scsi_sgl {
> + uintptr_t addr;
> + uintptr_t dif_addr;
> + size_t len;
> +};
> +
> +/* Return values for calls from base driver to libefc */
> +#define EFCT_SCSI_CALL_COMPLETE 0 /* All work is done */
> +#define EFCT_SCSI_CALL_ASYNC 1 /* Work will be completed asynchronously */
> +
> +enum efct_scsi_io_role {
> + EFCT_SCSI_IO_ROLE_ORIGINATOR,
> + EFCT_SCSI_IO_ROLE_RESPONDER,
> +};
> +
> +void efct_scsi_io_alloc_enable(struct efc *efc, struct efc_node *node);
> +void efct_scsi_io_alloc_disable(struct efc *efc, struct efc_node *node);
> +extern struct efct_io *
> +efct_scsi_io_alloc(struct efc_node *node, enum efct_scsi_io_role);
> +void efct_scsi_io_free(struct efct_io *io);
> +struct efct_io *efct_io_get_instance(struct efct *efct, u32 index);
> +
> +int efct_scsi_tgt_driver_init(void);
> +int efct_scsi_tgt_driver_exit(void);
> +int efct_scsi_tgt_new_device(struct efct *efct);
> +int efct_scsi_tgt_del_device(struct efct *efct);
> +int
> +efct_scsi_tgt_new_domain(struct efc *efc, struct efc_domain *domain);
> +void
> +efct_scsi_tgt_del_domain(struct efc *efc, struct efc_domain *domain);
> +int
> +efct_scsi_tgt_new_sport(struct efc *efc, struct efc_sli_port *sport);
> +void
> +efct_scsi_tgt_del_sport(struct efc *efc, struct efc_sli_port *sport);
> +int
> +efct_scsi_validate_initiator(struct efc *efc, struct efc_node *node);
> +int
> +efct_scsi_new_initiator(struct efc *efc, struct efc_node *node);
> +
> +enum efct_scsi_del_initiator_reason {
> + EFCT_SCSI_INITIATOR_DELETED,
> + EFCT_SCSI_INITIATOR_MISSING,
> +};
> +
> +extern int
> +efct_scsi_del_initiator(struct efc *efc, struct efc_node *node,
> + int reason);

extern not needed

> +extern int
> +efct_scsi_recv_cmd(struct efct_io *io, uint64_t lun, u8 *cdb,
> + u32 cdb_len, u32 flags);
> +extern int
> +efct_scsi_recv_tmf(struct efct_io *tmfio, u32 lun,
> + enum efct_scsi_tmf_cmd cmd, struct efct_io *abortio,
> + u32 flags);
> +
> +extern int
> +efct_scsi_send_rd_data(struct efct_io *io, u32 flags,
> + struct efct_scsi_sgl *sgl, u32 sgl_count,
> + u64 wire_len, efct_scsi_io_cb_t cb, void *arg);
> +extern int
> +efct_scsi_recv_wr_data(struct efct_io *io, u32 flags,
> + struct efct_scsi_sgl *sgl, u32 sgl_count,
> + u64 wire_len, efct_scsi_io_cb_t cb, void *arg);
> +extern int
> +efct_scsi_send_resp(struct efct_io *io, u32 flags,
> + struct efct_scsi_cmd_resp *rsp, efct_scsi_io_cb_t cb,
> + void *arg);
> +extern int
> +efct_scsi_send_tmf_resp(struct efct_io *io,
> + enum efct_scsi_tmf_resp rspcode,
> + u8 addl_rsp_info[3],
> + efct_scsi_io_cb_t cb, void *arg);
> +extern int
> +efct_scsi_tgt_abort_io(struct efct_io *io, efct_scsi_io_cb_t cb, void *arg);
> +
> +void efct_scsi_io_complete(struct efct_io *io);
> +
> +int efct_scsi_reg_fc_transport(void);
> +int efct_scsi_release_fc_transport(void);
> +int efct_scsi_new_device(struct efct *efct);
> +int efct_scsi_del_device(struct efct *efct);
> +void _efct_scsi_io_free(struct kref *arg);
> +
> +int efct_scsi_send_tmf(struct efc_node *node,
> + struct efct_io *io,
> + struct efct_io *io_to_abort, u32 lun,
> + enum efct_scsi_tmf_cmd tmf,
> + struct efct_scsi_sgl *sgl,
> + u32 sgl_count, u32 len,
> + efct_scsi_rsp_io_cb_t cb, void *arg);
> +
> +extern int
> +efct_scsi_del_vport(struct efct *efct, struct Scsi_Host *shost);
> +extern struct efct_vport *
> +efct_scsi_new_vport(struct efct *efct, struct device *dev);
> +
> +int efct_scsi_io_dispatch(struct efct_io *io, void *cb);
> +int efct_scsi_io_dispatch_abort(struct efct_io *io, void *cb);
> +void efct_scsi_check_pending(struct efct *efct);
> +
> +#endif /* __EFCT_SCSI_H__ */
> --
> 2.16.4
>
>

Thanks,
Daniel