[PATCH 20/32] elx: efct: Hardware queues processing

James Smart
2019-10-23T21:56:46Z

This patch continues the efct driver population.

This patch adds driver definitions for:
Routines for EQ, CQ, WQ and RQ processing.
Routines for IO object pool allocation and deallocation.

Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
---
drivers/scsi/elx/efct/efct_hw.c | 628 +++++++++++++++++++++++++++++++++
drivers/scsi/elx/efct/efct_hw.h | 36 ++
drivers/scsi/elx/efct/efct_hw_queues.c | 247 +++++++++++++
drivers/scsi/elx/efct/efct_io.c | 288 +++++++++++++++
drivers/scsi/elx/efct/efct_io.h | 219 ++++++++++++
5 files changed, 1418 insertions(+)
create mode 100644 drivers/scsi/elx/efct/efct_io.c
create mode 100644 drivers/scsi/elx/efct/efct_io.h

diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c
index ae0f49e5d751..aab66f5d7908 100644
--- a/drivers/scsi/elx/efct/efct_hw.c
+++ b/drivers/scsi/elx/efct/efct_hw.c
@@ -49,6 +49,14 @@ efct_hw_config_sli_port_health_check(struct efct_hw_s *hw, u8 query,
u8 enable);
static enum efct_hw_rtn_e
efct_hw_set_dif_seed(struct efct_hw_s *hw);
+static void
+efct_hw_queue_hash_add(struct efct_queue_hash_s *, u16, u16);
+static int
+efct_hw_flush(struct efct_hw_s *);
+static void
+efct_hw_wq_process_io(void *arg, u8 *cqe, int status);
+static void
+efct_hw_wq_process_abort(void *arg, u8 *cqe, int status);

static enum efct_hw_rtn_e
efct_hw_link_event_init(struct efct_hw_s *hw)
@@ -3273,3 +3281,623 @@ efct_hw_config_set_fdt_xfer_hint(struct efct_hw_s *hw, u32 fdt_xfer_hint)

return rc;
}
+
+static u8 efct_hw_iotype_is_originator(u16 io_type)
+{
+ switch (io_type) {
+ case EFCT_HW_FC_CT:
+ case EFCT_HW_ELS_REQ:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * @brief Update the queue hash with the ID and index.
+ *
+ * @param hash Pointer to hash table.
+ * @param id ID that was created.
+ * @param index The index into the hash object.
+ */
+static void
+efct_hw_queue_hash_add(struct efct_queue_hash_s *hash,
+ u16 id, u16 index)
+{
+ u32 hash_index = id & (EFCT_HW_Q_HASH_SIZE - 1);
+
+ /*
+ * Since the hash is always bigger than the number of queues, then we
+ * never have to worry about an infinite loop.
+ */
+ while (hash[hash_index].in_use)
+ hash_index = (hash_index + 1) & (EFCT_HW_Q_HASH_SIZE - 1);
+
+ /* not used, claim the entry */
+ hash[hash_index].id = id;
+ hash[hash_index].in_use = true;
+ hash[hash_index].index = index;
+}
+
+/**
+ * @brief Find index given queue ID.
+ *
+ * @param hash Pointer to hash table.
+ * @param id ID to find.
+ *
+ * @return Returns the index into the HW cq array or -1 if not found.
+ */
+int
+efct_hw_queue_hash_find(struct efct_queue_hash_s *hash, u16 id)
+{
+ int rc = -1;
+ int index = id & (EFCT_HW_Q_HASH_SIZE - 1);
+
+ /*
+ * Since the hash is always bigger than the maximum number of Qs, then
+ * we never have to worry about an infinite loop. We will always find
+ * an unused entry.
+ */
+ do {
+ if (hash[index].in_use &&
+ hash[index].id == id)
+ rc = hash[index].index;
+ else
+ index = (index + 1) & (EFCT_HW_Q_HASH_SIZE - 1);
+ } while (rc == -1 && hash[index].in_use);
+
+ return rc;
+}
+
+int
+efct_hw_process(struct efct_hw_s *hw, u32 vector,
+ u32 max_isr_time_msec)
+{
+ struct hw_eq_s *eq;
+ int rc = 0;
+
+ /*
+ * The caller should disable interrupts if they wish to prevent us
+ * from processing during a shutdown. The following states are defined:
+ * EFCT_HW_STATE_UNINITIALIZED - No queues allocated
+ * EFCT_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
+ * queues are cleared.
+ * EFCT_HW_STATE_ACTIVE - Chip and queues are operational
+ * EFCT_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
+ * EFCT_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
+ * completions.
+ */
+ if (hw->state == EFCT_HW_STATE_UNINITIALIZED)
+ return 0;
+
+ /* Get pointer to struct hw_eq_s */
+ eq = hw->hw_eq[vector];
+ if (!eq)
+ return 0;
+
+ eq->use_count++;
+
+ rc = efct_hw_eq_process(hw, eq, max_isr_time_msec);
+
+ return rc;
+}
+
+/**
+ * @ingroup interrupt
+ * @brief Process events associated with an EQ.
+ *
+ * @par Description
+ * Loop termination:
+ * @n @n Without a mechanism to terminate the completion processing loop, it
+ * is possible under some workload conditions for the loop to never terminate
+ * (or at least take longer than the OS is happy to have an interrupt handler
+ * or kernel thread context hold a CPU without yielding).
+ * @n @n The approach taken here is to periodically check how much time
+ * we have been in this
+ * processing loop, and if we exceed a predetermined time (multiple seconds),
+ * the loop is terminated, and efct_hw_process() returns.
+ *
+ * @param hw Hardware context.
+ * @param eq Pointer to HW EQ object.
+ * @param max_isr_time_msec Maximum time in msec to stay in this function.
+ *
+ * @return Returns 0 on success, or a non-zero value on failure.
+ */
+int
+efct_hw_eq_process(struct efct_hw_s *hw, struct hw_eq_s *eq,
+ u32 max_isr_time_msec)
+{
+ u8 eqe[sizeof(struct sli4_eqe_s)] = { 0 };
+ u32 tcheck_count;
+ time_t tstart;
+ time_t telapsed;
+ bool done = false;
+
+ tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS;
+ tstart = jiffies_to_msecs(jiffies);
+
+ while (!done && !sli_eq_read(&hw->sli, eq->queue, eqe)) {
+ u16 cq_id = 0;
+ int rc;
+
+ rc = sli_eq_parse(&hw->sli, eqe, &cq_id);
+ if (unlikely(rc)) {
+ if (rc > 0) {
+ u32 i;
+
+ /*
+ * Received a sentinel EQE indicating the
+ * EQ is full. Process all CQs
+ */
+ for (i = 0; i < hw->cq_count; i++)
+ efct_hw_cq_process(hw, hw->hw_cq[i]);
+ continue;
+ } else {
+ return rc;
+ }
+ } else {
+ int index;
+
+ index = efct_hw_queue_hash_find(hw->cq_hash, cq_id);
+
+ if (likely(index >= 0))
+ efct_hw_cq_process(hw, hw->hw_cq[index]);
+ else
+ efc_log_err(hw->os, "bad CQ_ID %#06x\n",
+ cq_id);
+ }
+
+ if (eq->queue->n_posted > eq->queue->posted_limit)
+ sli_queue_arm(&hw->sli, eq->queue, false);
+
+ if (tcheck_count && (--tcheck_count == 0)) {
+ tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS;
+ telapsed = jiffies_to_msecs(jiffies) - tstart;
+ if (telapsed >= max_isr_time_msec)
+ done = true;
+ }
+ }
+ sli_queue_eq_arm(&hw->sli, eq->queue, true);
+
+ return 0;
+}
+
+/**
+ * @brief Process entries on the given completion queue.
+ *
+ * @param hw Hardware context.
+ * @param cq Pointer to the HW completion queue object.
+ *
+ * @return None.
+ */
+void
+efct_hw_cq_process(struct efct_hw_s *hw, struct hw_cq_s *cq)
+{
+ u8 cqe[sizeof(struct sli4_mcqe_s)];
+ u16 rid = U16_MAX;
+ enum sli4_qentry_e ctype; /* completion type */
+ int status;
+ u32 n_processed = 0;
+ u32 tstart, telapsed;
+
+ tstart = jiffies_to_msecs(jiffies);
+
+ while (!sli_cq_read(&hw->sli, cq->queue, cqe)) {
+ status = sli_cq_parse(&hw->sli, cq->queue,
+ cqe, &ctype, &rid);
+ /*
+ * The sign of status is significant. If status is:
+ * == 0 : call completed correctly and
+ * the CQE indicated success
+ * > 0 : call completed correctly and
+ * the CQE indicated an error
+ * < 0 : call failed and no information is available about the
+ * CQE
+ */
+ if (status < 0) {
+ if (status == -2)
+ /*
+ * Notification that an entry was consumed,
+ * but not completed
+ */
+ continue;
+
+ break;
+ }
+
+ switch (ctype) {
+ case SLI_QENTRY_ASYNC:
+ sli_cqe_async(&hw->sli, cqe);
+ break;
+ case SLI_QENTRY_MQ:
+ /*
+ * Process MQ entry. Note there is no way to determine
+ * the MQ_ID from the completion entry.
+ */
+ efct_hw_mq_process(hw, status, hw->mq);
+ break;
+ case SLI_QENTRY_WQ:
+ efct_hw_wq_process(hw, cq, cqe, status, rid);
+ break;
+ case SLI_QENTRY_WQ_RELEASE: {
+ u32 wq_id = rid;
+ int index;
+ struct hw_wq_s *wq = NULL;
+
+ index = efct_hw_queue_hash_find(hw->wq_hash, wq_id);
+
+ if (likely(index >= 0)) {
+ wq = hw->hw_wq[index];
+ } else {
+ efc_log_err(hw->os, "bad WQ_ID %#06x\n", wq_id);
+ break;
+ }
+ /* Submit any HW IOs that are on the WQ pending list */
+ hw_wq_submit_pending(wq, wq->wqec_set_count);
+
+ break;
+ }
+
+ case SLI_QENTRY_RQ:
+ efct_hw_rqpair_process_rq(hw, cq, cqe);
+ break;
+ case SLI_QENTRY_XABT: {
+ efct_hw_xabt_process(hw, cq, cqe, rid);
+ break;
+ }
+ default:
+ efc_log_test(hw->os,
+ "unhandled ctype=%#x rid=%#x\n",
+ ctype, rid);
+ break;
+ }
+
+ n_processed++;
+ if (n_processed == cq->queue->proc_limit)
+ break;
+
+ if (cq->queue->n_posted >= cq->queue->posted_limit)
+ sli_queue_arm(&hw->sli, cq->queue, false);
+ }
+
+ sli_queue_arm(&hw->sli, cq->queue, true);
+
+ if (n_processed > cq->queue->max_num_processed)
+ cq->queue->max_num_processed = n_processed;
+ telapsed = jiffies_to_msecs(jiffies) - tstart;
+ if (telapsed > cq->queue->max_process_time)
+ cq->queue->max_process_time = telapsed;
+}
+
+/**
+ * @brief Process WQ completion queue entries.
+ *
+ * @param hw Hardware context.
+ * @param cq Pointer to the HW completion queue object.
+ * @param cqe Pointer to WQ completion queue.
+ * @param status Completion status.
+ * @param rid Resource ID (IO tag).
+ *
+ * @return none
+ */
+void
+efct_hw_wq_process(struct efct_hw_s *hw, struct hw_cq_s *cq,
+ u8 *cqe, int status, u16 rid)
+{
+ struct hw_wq_callback_s *wqcb;
+
+ if (rid == EFCT_HW_REQUE_XRI_REGTAG) {
+ if (status)
+ efc_log_err(hw->os, "reque xri failed, status = %d\n",
+ status);
+ return;
+ }
+
+ wqcb = efct_hw_reqtag_get_instance(hw, rid);
+ if (!wqcb) {
+ efc_log_err(hw->os, "invalid request tag: x%x\n", rid);
+ return;
+ }
+
+ if (!wqcb->callback) {
+ efc_log_err(hw->os, "wqcb callback is NULL\n");
+ return;
+ }
+
+ (*wqcb->callback)(wqcb->arg, cqe, status);
+}
+
+/**
+ * @brief Process WQ completions for IO requests
+ *
+ * @param arg Generic callback argument
+ * @param cqe Pointer to completion queue entry
+ * @param status Completion status
+ *
+ * @par Description
+ * Note: Regarding io->reqtag, the reqtag is assigned once when HW IOs are
+ * initialized in efct_hw_setup_io(), and don't need to be returned to the
+ * hw->wq_reqtag_pool.
+ *
+ * @return None.
+ */
+static void
+efct_hw_wq_process_io(void *arg, u8 *cqe, int status)
+{
+ struct efct_hw_io_s *io = arg;
+ struct efct_hw_s *hw = io->hw;
+ struct sli4_fc_wcqe_s *wcqe = (void *)cqe;
+ u32 len = 0;
+ u32 ext = 0;
+
+ efct_hw_remove_io_timed_wqe(hw, io);
+
+ /* clear xbusy flag if WCQE[XB] is clear */
+ if (io->xbusy && (wcqe->flags & SLI4_WCQE_XB) == 0)
+ io->xbusy = false;
+
+ /* get extended CQE status */
+ switch (io->type) {
+ case EFCT_HW_BLS_ACC:
+ case EFCT_HW_BLS_ACC_SID:
+ break;
+ case EFCT_HW_ELS_REQ:
+ sli_fc_els_did(&hw->sli, cqe, &ext);
+ len = sli_fc_response_length(&hw->sli, cqe);
+ break;
+ case EFCT_HW_ELS_RSP:
+ case EFCT_HW_ELS_RSP_SID:
+ case EFCT_HW_FC_CT_RSP:
+ break;
+ case EFCT_HW_FC_CT:
+ len = sli_fc_response_length(&hw->sli, cqe);
+ break;
+ case EFCT_HW_IO_TARGET_WRITE:
+ len = sli_fc_io_length(&hw->sli, cqe);
+ break;
+ case EFCT_HW_IO_TARGET_READ:
+ len = sli_fc_io_length(&hw->sli, cqe);
+ break;
+ case EFCT_HW_IO_TARGET_RSP:
+ break;
+ case EFCT_HW_IO_DNRX_REQUEUE:
+ /* release the count for re-posting the buffer */
+ /* efct_hw_io_free(hw, io); */
+ break;
+ default:
+ efc_log_test(hw->os, "unhandled io type %#x for XRI 0x%x\n",
+ io->type, io->indicator);
+ break;
+ }
+ if (status) {
+ ext = sli_fc_ext_status(&hw->sli, cqe);
+ /*
+ * If we're not an originator IO, and XB is set, then issue
+ * abort for the IO from within the HW
+ */
+ if ((!efct_hw_iotype_is_originator(io->type)) &&
+ wcqe->flags & SLI4_WCQE_XB) {
+ enum efct_hw_rtn_e rc;
+
+ efc_log_debug(hw->os, "aborting xri=%#x tag=%#x\n",
+ io->indicator, io->reqtag);
+
+ /*
+ * Because targets may send a response when the IO
+ * completes using the same XRI, we must wait for the
+ * XRI_ABORTED CQE to issue the IO callback
+ */
+ rc = efct_hw_io_abort(hw, io, false, NULL, NULL);
+ if (rc == EFCT_HW_RTN_SUCCESS) {
+ /*
+ * latch status to return after abort is
+ * complete
+ */
+ io->status_saved = true;
+ io->saved_status = status;
+ io->saved_ext = ext;
+ io->saved_len = len;
+ goto exit_efct_hw_wq_process_io;
+ } else if (rc == EFCT_HW_RTN_IO_ABORT_IN_PROGRESS) {
+ /*
+ * Already being aborted by someone else (ABTS
+ * perhaps). Just fall thru and return original
+ * error.
+ */
+ efc_log_debug(hw->os, "%s%#x tag=%#x\n",
+ "abort in progress xri=",
+ io->indicator, io->reqtag);
+
+ } else {
+ /* Failed to abort for some other reason, log
+ * error
+ */
+ efc_log_test(hw->os, "%s%#x tag=%#x rc=%d\n",
+ "Failed to abort xri=",
+ io->indicator, io->reqtag, rc);
+ }
+ }
+ }
+
+ if (io->done) {
+ efct_hw_done_t done = io->done;
+ void *arg = io->arg;
+
+ io->done = NULL;
+
+ if (io->status_saved) {
+ /* use latched status if exists */
+ status = io->saved_status;
+ len = io->saved_len;
+ ext = io->saved_ext;
+ io->status_saved = false;
+ }
+
+ /* Restore default SGL */
+ efct_hw_io_restore_sgl(hw, io);
+ done(io, io->rnode, len, status, ext, arg);
+ }
+
+exit_efct_hw_wq_process_io:
+ return;
+}
+
+/**
+ * @brief Process WQ completions for abort requests.
+ *
+ * @param arg Generic callback argument.
+ * @param cqe Pointer to completion queue entry.
+ * @param status Completion status.
+ *
+ * @return None.
+ */
+static void
+efct_hw_wq_process_abort(void *arg, u8 *cqe, int status)
+{
+ struct efct_hw_io_s *io = arg;
+ struct efct_hw_s *hw = io->hw;
+ u32 ext = 0;
+ u32 len = 0;
+ struct hw_wq_callback_s *wqcb;
+ unsigned long flags = 0;
+
+ /*
+ * For IOs that were aborted internally, we may need to issue the
+ * callback here depending on whether a XRI_ABORTED CQE is expected ot
+ * not. If the status is Local Reject/No XRI, then
+ * issue the callback now.
+ */
+ ext = sli_fc_ext_status(&hw->sli, cqe);
+ if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT &&
+ ext == SLI4_FC_LOCAL_REJECT_NO_XRI &&
+ io->done) {
+ efct_hw_done_t done = io->done;
+ void *arg = io->arg;
+
+ io->done = NULL;
+
+ /*
+ * Use latched status as this is always saved for an internal
+ * abort Note: We wont have both a done and abort_done
+ * function, so don't worry about
+ * clobbering the len, status and ext fields.
+ */
+ status = io->saved_status;
+ len = io->saved_len;
+ ext = io->saved_ext;
+ io->status_saved = false;
+ done(io, io->rnode, len, status, ext, arg);
+ }
+
+ if (io->abort_done) {
+ efct_hw_done_t done = io->abort_done;
+ void *arg = io->abort_arg;
+
+ io->abort_done = NULL;
+
+ done(io, io->rnode, len, status, ext, arg);
+ }
+ spin_lock_irqsave(&hw->io_abort_lock, flags);
+ /* clear abort bit to indicate abort is complete */
+ io->abort_in_progress = false;
+ spin_unlock_irqrestore(&hw->io_abort_lock, flags);
+
+ /* Free the WQ callback */
+ if (io->abort_reqtag == U32_MAX) {
+ efc_log_err(hw->os, "HW IO already freed\n");
+ return;
+ }
+
+ wqcb = efct_hw_reqtag_get_instance(hw, io->abort_reqtag);
+ efct_hw_reqtag_free(hw, wqcb);
+
+ /*
+ * Call efct_hw_io_free() because this releases the WQ reservation as
+ * well as doing the refcount put. Don't duplicate the code here.
+ */
+ (void)efct_hw_io_free(hw, io);
+}
+
+/**
+ * @brief Process XABT completions
+ *
+ * @param hw Hardware context.
+ * @param cq Pointer to the HW completion queue object.
+ * @param cqe Pointer to WQ completion queue.
+ * @param rid Resource ID (IO tag).
+ *
+ *
+ * @return None.
+ */
+void
+efct_hw_xabt_process(struct efct_hw_s *hw, struct hw_cq_s *cq,
+ u8 *cqe, u16 rid)
+{
+ /* search IOs wait free list */
+ struct efct_hw_io_s *io = NULL;
+ unsigned long flags = 0;
+
+ io = efct_hw_io_lookup(hw, rid);
+ if (!io) {
+ /* IO lookup failure should never happen */
+ efc_log_err(hw->os,
+ "Error: xabt io lookup failed rid=%#x\n", rid);
+ return;
+ }
+
+ if (!io->xbusy)
+ efc_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid);
+ else
+ /* mark IO as no longer busy */
+ io->xbusy = false;
+
+ /*
+ * For IOs that were aborted internally, we need to issue any pending
+ * callback here.
+ */
+ if (io->done) {
+ efct_hw_done_t done = io->done;
+ void *arg = io->arg;
+
+ /*
+ * Use latched status as this is always saved for an internal
+ * abort
+ */
+ int status = io->saved_status;
+ u32 len = io->saved_len;
+ u32 ext = io->saved_ext;
+
+ io->done = NULL;
+ io->status_saved = false;
+
+ done(io, io->rnode, len, status, ext, arg);
+ }
+
+ spin_lock_irqsave(&hw->io_lock, flags);
+ if (io->state == EFCT_HW_IO_STATE_INUSE ||
+ io->state == EFCT_HW_IO_STATE_WAIT_FREE) {
+ /* if on wait_free list, caller has already freed IO;
+ * remove from wait_free list and add to free list.
+ * if on in-use list, already marked as no longer busy;
+ * just leave there and wait for caller to free.
+ */
+ if (io->state == EFCT_HW_IO_STATE_WAIT_FREE) {
+ io->state = EFCT_HW_IO_STATE_FREE;
+ list_del(&io->list_entry);
+ efct_hw_io_free_move_correct_list(hw, io);
+ }
+ }
+ spin_unlock_irqrestore(&hw->io_lock, flags);
+}
+
+static int
+efct_hw_flush(struct efct_hw_s *hw)
+{
+ u32 i = 0;
+
+ /* Process any remaining completions */
+ for (i = 0; i < hw->eq_count; i++)
+ efct_hw_process(hw, i, ~0);
+
+ return 0;
+}
diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h
index d913c0169c44..8a487df2338d 100644
--- a/drivers/scsi/elx/efct/efct_hw.h
+++ b/drivers/scsi/elx/efct/efct_hw.h
@@ -1076,4 +1076,40 @@ extern struct hw_wq_callback_s
*efct_hw_reqtag_get_instance(struct efct_hw_s *hw, u32 instance_index);
void efct_hw_reqtag_reset(struct efct_hw_s *hw);

+/* RQ completion handlers for RQ pair mode */
+extern int
+efct_hw_rqpair_process_rq(struct efct_hw_s *hw,
+ struct hw_cq_s *cq, u8 *cqe);
+extern
+enum efct_hw_rtn_e efct_hw_rqpair_sequence_free(struct efct_hw_s *hw,
+ struct efc_hw_sequence_s *seq);
+static inline void
+efct_hw_sequence_copy(struct efc_hw_sequence_s *dst,
+ struct efc_hw_sequence_s *src)
+{
+ /* Copy src to dst, then zero out the linked list link */
+ *dst = *src;
+}
+
+static inline enum efct_hw_rtn_e
+efct_hw_sequence_free(struct efct_hw_s *hw, struct efc_hw_sequence_s *seq)
+{
+ /* Only RQ pair mode is supported */
+ return efct_hw_rqpair_sequence_free(hw, seq);
+}
+extern int
+efct_hw_eq_process(struct efct_hw_s *hw, struct hw_eq_s *eq,
+ u32 max_isr_time_msec);
+void efct_hw_cq_process(struct efct_hw_s *hw, struct hw_cq_s *cq);
+extern void
+efct_hw_wq_process(struct efct_hw_s *hw, struct hw_cq_s *cq,
+ u8 *cqe, int status, u16 rid);
+extern void
+efct_hw_xabt_process(struct efct_hw_s *hw, struct hw_cq_s *cq,
+ u8 *cqe, u16 rid);
+extern int
+efct_hw_process(struct efct_hw_s *hw, u32 vector, u32 max_isr_time_msec);
+extern int
+efct_hw_queue_hash_find(struct efct_queue_hash_s *hash, u16 id);
+
#endif /* __EFCT_H__ */
diff --git a/drivers/scsi/elx/efct/efct_hw_queues.c b/drivers/scsi/elx/efct/efct_hw_queues.c
index 5196aa75553c..97d64e225f43 100644
--- a/drivers/scsi/elx/efct/efct_hw_queues.c
+++ b/drivers/scsi/elx/efct/efct_hw_queues.c
@@ -1715,3 +1715,250 @@ efct_hw_qtop_free(struct efct_hw_qtop_s *qtop)
kfree(qtop);
}
}
+
+/**
+ * @brief Process receive queue completions for RQ Pair mode.
+ *
+ * @par Description
+ * RQ completions are processed. In RQ pair mode, a single header and single
+ * payload buffer are received, and passed to the function that has registered
+ * for unsolicited callbacks.
+ *
+ * @param hw Hardware context.
+ * @param cq Pointer to HW completion queue.
+ * @param cqe Completion queue entry.
+ *
+ * @return Returns 0 for success, or a negative error code value for failure.
+ **/
+
+int
+efct_hw_rqpair_process_rq(struct efct_hw_s *hw, struct hw_cq_s *cq,
+ u8 *cqe)
+{
+ u16 rq_id;
+ u32 index;
+ int rqindex;
+ int rq_status;
+ u32 h_len;
+ u32 p_len;
+ struct efc_hw_sequence_s *seq;
+ struct hw_rq_s *rq;
+
+ rq_status = sli_fc_rqe_rqid_and_index(&hw->sli, cqe,
+ &rq_id, &index);
+ if (rq_status != 0) {
+ switch (rq_status) {
+ case SLI4_FC_ASYNC_RQ_BUF_LEN_EXCEEDED:
+ case SLI4_FC_ASYNC_RQ_DMA_FAILURE:
+ /* just get RQ buffer then return to chip */
+ rqindex = efct_hw_rqpair_find(hw, rq_id);
+ if (rqindex < 0) {
+ efc_log_test(hw->os,
+ "status=%#x: lookup fail id=%#x\n",
+ rq_status, rq_id);
+ break;
+ }
+
+ /* get RQ buffer */
+ seq = efct_hw_rqpair_get(hw, rqindex, index);
+
+ /* return to chip */
+ if (efct_hw_rqpair_sequence_free(hw, seq)) {
+ efc_log_test(hw->os,
+ "status=%#x,fail rtrn buf to RQ\n",
+ rq_status);
+ break;
+ }
+ break;
+ case SLI4_FC_ASYNC_RQ_INSUFF_BUF_NEEDED:
+ case SLI4_FC_ASYNC_RQ_INSUFF_BUF_FRM_DISC:
+ /*
+ * since RQ buffers were not consumed, cannot return
+ * them to chip
+ * fall through
+ */
+ efc_log_debug(hw->os, "Warning: RCQE status=%#x,\n",
+ rq_status);
+ default:
+ break;
+ }
+ return -1;
+ }
+
+ rqindex = efct_hw_rqpair_find(hw, rq_id);
+ if (rqindex < 0) {
+ efc_log_test(hw->os, "Error: rq_id lookup failed for id=%#x\n",
+ rq_id);
+ return -1;
+ }
+
+ rq = hw->hw_rq[hw->hw_rq_lookup[rqindex]];
+ rq->use_count++;
+
+ seq = efct_hw_rqpair_get(hw, rqindex, index);
+ if (WARN_ON(!seq))
+ return -1;
+
+ seq->hw = hw;
+ seq->auto_xrdy = 0;
+ seq->out_of_xris = 0;
+ seq->xri = 0;
+ seq->hio = NULL;
+
+ sli_fc_rqe_length(&hw->sli, cqe, &h_len, &p_len);
+ seq->header->dma.len = h_len;
+ seq->payload->dma.len = p_len;
+ seq->fcfi = sli_fc_rqe_fcfi(&hw->sli, cqe);
+ seq->hw_priv = cq->eq;
+
+ efct_unsolicited_cb(hw->os, seq);
+
+ return 0;
+}
+/**
+ * @brief Return pointer to RQ buffer entry.
+ *
+ * @par Description
+ * Returns a pointer to the RQ buffer entry given by @c rqindex and @c bufindex.
+ *
+ * @param hw Hardware context.
+ * @param rqindex Index of the RQ that is being processed.
+ * @param bufindex Index into the RQ that is being processed.
+ *
+ * @return Pointer to the sequence structure, or NULL otherwise.
+ */
+static struct efc_hw_sequence_s *
+efct_hw_rqpair_get(struct efct_hw_s *hw, u16 rqindex, u16 bufindex)
+{
+ struct sli4_queue_s *rq_hdr = &hw->rq[rqindex];
+ struct efc_hw_sequence_s *seq = NULL;
+ struct hw_rq_s *rq = hw->hw_rq[hw->hw_rq_lookup[rqindex]];
+ unsigned long flags = 0;
+
+ if (bufindex >= rq_hdr->length) {
+ efc_log_err(hw->os,
+ "RQidx %d bufidx %d exceed ring len %d for id %d\n",
+ rqindex, bufindex, rq_hdr->length, rq_hdr->id);
+ return NULL;
+ }
+
+ /* rq_hdr lock also covers rqindex+1 queue */
+ spin_lock_irqsave(&rq_hdr->lock, flags);
+
+ seq = rq->rq_tracker[bufindex];
+ rq->rq_tracker[bufindex] = NULL;
+
+ if (!seq) {
+ efc_log_err(hw->os,
+ "RQbuf NULL, rqidx %d, bufidx %d, cur q idx = %d\n",
+ rqindex, bufindex, rq_hdr->index);
+ }
+
+ spin_unlock_irqrestore(&rq_hdr->lock, flags);
+ return seq;
+}
+/**
+ * @brief Posts an RQ buffer to a queue and update the verification structures
+ *
+ * @param hw hardware context
+ * @param seq Pointer to sequence object.
+ *
+ * @return Returns 0 on success, or a non-zero value otherwise.
+ */
+static int
+efct_hw_rqpair_put(struct efct_hw_s *hw, struct efc_hw_sequence_s *seq)
+{
+ struct sli4_queue_s *rq_hdr = &hw->rq[seq->header->rqindex];
+ struct sli4_queue_s *rq_payload = &hw->rq[seq->payload->rqindex];
+ u32 hw_rq_index = hw->hw_rq_lookup[seq->header->rqindex];
+ struct hw_rq_s *rq = hw->hw_rq[hw_rq_index];
+ u32 phys_hdr[2];
+ u32 phys_payload[2];
+ int qindex_hdr;
+ int qindex_payload;
+ unsigned long flags = 0;
+
+ /* Update the RQ verification lookup tables */
+ phys_hdr[0] = upper_32_bits(seq->header->dma.phys);
+ phys_hdr[1] = lower_32_bits(seq->header->dma.phys);
+ phys_payload[0] = upper_32_bits(seq->payload->dma.phys);
+ phys_payload[1] = lower_32_bits(seq->payload->dma.phys);
+
+ /* rq_hdr lock also covers payload / header->rqindex+1 queue */
+ spin_lock_irqsave(&rq_hdr->lock, flags);
+
+ /*
+ * Note: The header must be posted last for buffer pair mode because
+ * posting on the header queue posts the payload queue as well.
+ * We do not ring the payload queue independently in RQ pair mode.
+ */
+ qindex_payload = sli_rq_write(&hw->sli, rq_payload,
+ (void *)phys_payload);
+ qindex_hdr = sli_rq_write(&hw->sli, rq_hdr, (void *)phys_hdr);
+ if (qindex_hdr < 0 ||
+ qindex_payload < 0) {
+ efc_log_err(hw->os, "RQ_ID=%#x write failed\n", rq_hdr->id);
+ spin_unlock_irqrestore(&rq_hdr->lock, flags);
+ return EFCT_HW_RTN_ERROR;
+ }
+
+ /* ensure the indexes are the same */
+ WARN_ON(qindex_hdr != qindex_payload);
+
+ /* Update the lookup table */
+ if (!rq->rq_tracker[qindex_hdr]) {
+ rq->rq_tracker[qindex_hdr] = seq;
+ } else {
+ efc_log_test(hw->os,
+ "expected rq_tracker[%d][%d] buffer to be NULL\n",
+ hw_rq_index, qindex_hdr);
+ }
+
+ spin_unlock_irqrestore(&rq_hdr->lock, flags);
+ return EFCT_HW_RTN_SUCCESS;
+}
+
+/**
+ * @brief Return RQ buffers (while in RQ pair mode).
+ *
+ * @par Description
+ * The header and payload buffers are returned to the Receive Queue.
+ *
+ * @param hw Hardware context.
+ * @param seq Header/payload sequence buffers.
+ *
+ * @return Returns EFCT_HW_RTN_SUCCESS on success, or an error code value on
+ * failure.
+ */
+
+enum efct_hw_rtn_e
+efct_hw_rqpair_sequence_free(struct efct_hw_s *hw,
+ struct efc_hw_sequence_s *seq)
+{
+ enum efct_hw_rtn_e rc = EFCT_HW_RTN_SUCCESS;
+
+ /*
+ * Post the data buffer first. Because in RQ pair mode, ringing the
+ * doorbell of the header ring will post the data buffer as well.
+ */
+ if (efct_hw_rqpair_put(hw, seq)) {
+ efc_log_err(hw->os, "error writing buffers\n");
+ return EFCT_HW_RTN_ERROR;
+ }
+
+ return rc;
+}
+
+/**
+ * @brief Find the RQ index of RQ_ID.
+ *
+ * @param hw Hardware context.
+ * @param rq_id RQ ID to find.
+ *
+ * @return Returns the RQ index, or -1 if not found
+ */
+static inline int
+efct_hw_rqpair_find(struct efct_hw_s *hw, u16 rq_id)
+{
+ return efct_hw_queue_hash_find(hw->rq_hash, rq_id);
+}
diff --git a/drivers/scsi/elx/efct/efct_io.c b/drivers/scsi/elx/efct/efct_io.c
new file mode 100644
index 000000000000..f61ee0fdd616
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_io.c
@@ -0,0 +1,288 @@
+// 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_utils.h"
+#include "efct_hw.h"
+#include "efct_io.h"
+
+/**
+ * @brief IO pool.
+ *
+ * Structure encapsulating a pool of IO objects.
+ *
+ */
+
+struct efct_io_pool_s {
+ struct efct_s *efct; /* Pointer to device object */
+ spinlock_t lock; /* IO pool lock */
+ u32 io_num_ios; /* Total IOs allocated */
+ struct efct_pool_s *pool;
+};
+
+/**
+ * @brief Create a pool of IO objects.
+ *
+ * @par Description
+ * This function allocates memory in larger chucks called
+ * "slabs" which are a fixed size. It calculates the number of IO objects that
+ * fit within each "slab" and determines the number of "slabs" required to
+ * allocate the number of IOs requested. Each of the slabs is allocated and
+ * then it grabs each IO object within the slab and adds it to the free list.
+ * Individual command, response and SGL DMA buffers are allocated for each IO.
+ *
+ * "Slabs"
+ * +----------------+
+ * | |
+ * +----------------+ |
+ * | IO | |
+ * +----------------+ |
+ * | ... | |
+ * +----------------+__+
+ * | IO |
+ * +----------------+
+ *
+ * @param efct Driver instance's software context.
+ * @param num_io Number of IO contexts to allocate.
+ * @param num_sgl Number of SGL entries to allocate for each IO.
+ *
+ * @return Returns a pointer to a new efct_io_pool_s on success,
+ * or NULL on failure.
+ */
+
+struct efct_io_pool_s *
+efct_io_pool_create(struct efct_s *efct, u32 num_io, u32 num_sgl)
+{
+ u32 i = 0;
+ struct efct_io_pool_s *io_pool;
+
+ /* Allocate the IO pool */
+ io_pool = kmalloc(sizeof(*io_pool), GFP_KERNEL);
+ if (!io_pool)
+ return NULL;
+
+ memset(io_pool, 0, sizeof(*io_pool));
+ io_pool->efct = efct;
+ io_pool->io_num_ios = num_io;
+
+ /* initialize IO pool lock */
+ spin_lock_init(&io_pool->lock);
+
+ io_pool->pool = efct_pool_alloc(efct, sizeof(struct efct_io_s),
+ io_pool->io_num_ios);
+
+ for (i = 0; i < io_pool->io_num_ios; i++) {
+ struct efct_io_s *io = efct_pool_get_instance(io_pool->pool, i);
+
+ io->tag = i;
+ io->instance_index = i;
+ io->efct = efct;
+
+ /* Allocate a response buffer */
+ io->rspbuf.size = SCSI_RSP_BUF_LENGTH;
+ io->rspbuf.virt = dma_alloc_coherent(&efct->pcidev->dev,
+ io->rspbuf.size,
+ &io->rspbuf.phys, GFP_DMA);
+ if (!io->rspbuf.virt) {
+ efc_log_err(efct, "dma_alloc cmdbuf failed\n");
+ efct_io_pool_free(io_pool);
+ return NULL;
+ }
+
+ /* Allocate SGL */
+ io->sgl = kzalloc(sizeof(*io->sgl) * num_sgl, GFP_ATOMIC);
+ if (!io->sgl) {
+ efct_io_pool_free(io_pool);
+ return NULL;
+ }
+
+ memset(io->sgl, 0, sizeof(*io->sgl) * num_sgl);
+ io->sgl_allocated = num_sgl;
+ io->sgl_count = 0;
+
+ /* Make IO backend call to initialize IO */
+ efct_scsi_tgt_io_init(io);
+ }
+
+ return io_pool;
+}
+
+/**
+ * @brief Free IO objects pool
+ *
+ * @par Description
+ * The pool of IO objects are freed.
+ *
+ * @param io_pool Pointer to IO pool object.
+ *
+ * @return Returns 0 on success, or a negative error code value on failure.
+ */
+int
+efct_io_pool_free(struct efct_io_pool_s *io_pool)
+{
+ struct efct_s *efct;
+ u32 i;
+ struct efct_io_s *io;
+
+ if (io_pool) {
+ efct = io_pool->efct;
+
+ for (i = 0; i < io_pool->io_num_ios; i++) {
+ io = efct_pool_get_instance(io_pool->pool, i);
+ if (!io)
+ continue;
+
+ efct_scsi_tgt_io_exit(io);
+ kfree(io->sgl);
+ dma_free_coherent(&efct->pcidev->dev,
+ io->cmdbuf.size, io->cmdbuf.virt,
+ io->cmdbuf.phys);
+ memset(&io->cmdbuf, 0, sizeof(struct efc_dma_s));
+ dma_free_coherent(&efct->pcidev->dev,
+ io->rspbuf.size, io->rspbuf.virt,
+ io->rspbuf.phys);
+ memset(&io->rspbuf, 0, sizeof(struct efc_dma_s));
+ }
+
+ if (io_pool->pool)
+ efct_pool_free(io_pool->pool);
+
+ kfree(io_pool);
+ efct->xport->io_pool = NULL;
+ }
+
+ return 0;
+}
+
+u32 efct_io_pool_allocated(struct efct_io_pool_s *io_pool)
+{
+ return io_pool->io_num_ios;
+}
+
+/**
+ * @ingroup io_alloc
+ * @brief Allocate an object used to track an IO.
+ *
+ * @param io_pool Pointer to the IO pool.
+ *
+ * @return Returns the pointer to a new object, or NULL if none available.
+ */
+struct efct_io_s *
+efct_io_pool_io_alloc(struct efct_io_pool_s *io_pool)
+{
+ struct efct_io_s *io = NULL;
+ struct efct_s *efct;
+ unsigned long flags = 0;
+
+ efct = io_pool->efct;
+
+ spin_lock_irqsave(&io_pool->lock, flags);
+ io = efct_pool_get(io_pool->pool);
+ if (io) {
+ spin_unlock_irqrestore(&io_pool->lock, flags);
+
+ io->io_type = EFCT_IO_TYPE_MAX;
+ io->hio_type = EFCT_HW_IO_MAX;
+ io->hio = NULL;
+ io->transferred = 0;
+ io->efct = efct;
+ io->timeout = 0;
+ io->sgl_count = 0;
+ io->tgt_task_tag = 0;
+ io->init_task_tag = 0;
+ io->hw_tag = 0;
+ io->display_name = "pending";
+ io->seq_init = 0;
+ io->els_req_free = false;
+ io->io_free = 0;
+ io->release = NULL;
+ atomic_add_return(1, &efct->xport->io_active_count);
+ atomic_add_return(1, &efct->xport->io_total_alloc);
+ } else {
+ spin_unlock_irqrestore(&io_pool->lock, flags);
+ }
+ return io;
+}
+
+/**
+ * @ingroup io_alloc
+ * @brief Free an object used to track an IO.
+ *
+ * @param io_pool Pointer to IO pool object.
+ * @param io Pointer to the IO object.
+ */
+void
+efct_io_pool_io_free(struct efct_io_pool_s *io_pool, struct efct_io_s *io)
+{
+ struct efct_s *efct;
+ struct efct_hw_io_s *hio = NULL;
+ unsigned long flags = 0;
+
+ efct = io_pool->efct;
+
+ spin_lock_irqsave(&io_pool->lock, flags);
+ hio = io->hio;
+ io->hio = NULL;
+ io->io_free = 1;
+ efct_pool_put_head(io_pool->pool, io);
+ spin_unlock_irqrestore(&io_pool->lock, flags);
+
+ if (hio)
+ efct_hw_io_free(&efct->hw, hio);
+
+ atomic_sub_return(1, &efct->xport->io_active_count);
+ atomic_add_return(1, &efct->xport->io_total_free);
+}
+
+/**
+ * @ingroup io_alloc
+ * @brief Find an I/O given it's node and ox_id.
+ *
+ * @param efct Driver instance's software context.
+ * @param node Pointer to node.
+ * @param ox_id OX_ID to find.
+ * @param rx_id RX_ID to find (0xffff for unassigned).
+ */
+struct efct_io_s *
+efct_io_find_tgt_io(struct efct_s *efct, struct efc_node_s *node,
+ u16 ox_id, u16 rx_id)
+{
+ struct efct_io_s *io = NULL;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&node->active_ios_lock, flags);
+ list_for_each_entry(io, &node->active_ios, list_entry) {
+ if ((io->cmd_tgt && io->init_task_tag == ox_id) &&
+ (rx_id == 0xffff || io->tgt_task_tag == rx_id)) {
+ if (!kref_get_unless_zero(&io->ref))
+ io = NULL;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&node->active_ios_lock, flags);
+ return io;
+}
+
+/**
+ * @ingroup io_alloc
+ * @brief Return IO context given the instance index.
+ *
+ * @par Description
+ * Returns a pointer to the IO context given by the instance index.
+ *
+ * @param efct Pointer to driver structure.
+ * @param index IO instance index to return.
+ *
+ * @return Returns a pointer to the IO context, or NULL if not found.
+ */
+struct efct_io_s *
+efct_io_get_instance(struct efct_s *efct, u32 index)
+{
+ struct efct_xport_s *xport = efct->xport;
+ struct efct_io_pool_s *io_pool = xport->io_pool;
+
+ return efct_pool_get_instance(io_pool->pool, index);
+}
diff --git a/drivers/scsi/elx/efct/efct_io.h b/drivers/scsi/elx/efct/efct_io.h
new file mode 100644
index 000000000000..4a4278433e25
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_io.h
@@ -0,0 +1,219 @@
+/* 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_IO_H__)
+#define __EFCT_IO_H__
+
+#define io_error_log(io, fmt, ...) \
+ do { \
+ if (EFCT_LOG_ENABLE_IO_ERRORS(io->efct)) \
+ efc_log_warn(io->efct, fmt, ##__VA_ARGS__); \
+ } while (0)
+
+/**
+ * @brief FCP IO context
+ *
+ * This structure is used for transport and backend IO requests and responses.
+ */
+
+#define SCSI_CMD_BUF_LENGTH 48
+#define SCSI_RSP_BUF_LENGTH (FCP_RESP_WITH_EXT + SCSI_SENSE_BUFFERSIZE)
+#define EFCT_NUM_SCSI_IOS 8192
+
+#include "efct_lio.h"
+/**
+ * @brief EFCT IO types
+ */
+enum efct_io_type_e {
+ EFCT_IO_TYPE_IO = 0,
+ EFCT_IO_TYPE_ELS,
+ EFCT_IO_TYPE_CT,
+ EFCT_IO_TYPE_CT_RESP,
+ EFCT_IO_TYPE_BLS_RESP,
+ EFCT_IO_TYPE_ABORT,
+
+ EFCT_IO_TYPE_MAX, /* must be last */
+};
+
+enum efct_els_state_e {
+ EFCT_ELS_REQUEST = 0,
+ EFCT_ELS_REQUEST_DELAYED,
+ EFCT_ELS_REQUEST_DELAY_ABORT,
+ EFCT_ELS_REQ_ABORT,
+ EFCT_ELS_REQ_ABORTED,
+ EFCT_ELS_ABORT_IO_COMPL,
+};
+
+struct efct_io_s {
+ struct list_head list_entry;
+ struct list_head io_pending_link;
+ /* reference counter and callback function */
+ struct kref ref;
+ void (*release)(struct kref *arg);
+ /* pointer back to efct */
+ struct efct_s *efct;
+ /* unique instance index value */
+ u32 instance_index;
+ /* display name */
+ const char *display_name;
+ /* pointer to node */
+ struct efc_node_s *node;
+ /* (io_pool->io_free_list) free list link */
+ /* initiator task tag (OX_ID) for back-end and SCSI logging */
+ u32 init_task_tag;
+ /* target task tag (RX_ID) - for back-end and SCSI logging */
+ u32 tgt_task_tag;
+ /* HW layer unique IO id - for back-end and SCSI logging */
+ u32 hw_tag;
+ /* unique IO identifier */
+ u32 tag;
+ /* SGL */
+ struct efct_scsi_sgl_s *sgl;
+ /* Number of allocated SGEs */
+ u32 sgl_allocated;
+ /* Number of SGEs in this SGL */
+ u32 sgl_count;
+ /* backend target private IO data */
+ struct efct_scsi_tgt_io_s tgt_io;
+ /* expected data transfer length, based on FC header */
+ u32 exp_xfer_len;
+
+ /* Declarations private to HW/SLI */
+ void *hw_priv; /* HW private context */
+
+ /* Declarations private to FC Transport */
+
+ /* indicates what this struct efct_io_s structure is used for */
+ enum efct_io_type_e io_type;
+ /* pointer back to dslab allocation object */
+ void *dslab_item;
+ struct efct_hw_io_s *hio; /* HW IO context */
+ size_t transferred; /* Number of bytes transferred so far */
+
+ /* set if auto_trsp was set */
+ bool auto_resp;
+ /* set if low latency request */
+ bool low_latency;
+ /* selected WQ steering request */
+ u8 wq_steering;
+ /* selected WQ class if steering is class */
+ u8 wq_class;
+ /* transfer size for current request */
+ u64 xfer_req;
+ /* target callback function */
+ efct_scsi_io_cb_t scsi_tgt_cb;
+ /* target callback function argument */
+ void *scsi_tgt_cb_arg;
+ /* abort callback function */
+ efct_scsi_io_cb_t abort_cb;
+ /* abort callback function argument */
+ void *abort_cb_arg;
+ /* BLS callback function */
+ efct_scsi_io_cb_t bls_cb;
+ /* BLS callback function argument */
+ void *bls_cb_arg;
+ /* TMF command being processed */
+ enum efct_scsi_tmf_cmd_e tmf_cmd;
+ /* rx_id from the ABTS that initiated the command abort */
+ u16 abort_rx_id;
+
+ /* True if this is a Target command */
+ bool cmd_tgt;
+ /* when aborting, indicates ABTS is to be sent */
+ bool send_abts;
+ /* True if this is an Initiator command */
+ bool cmd_ini;
+ /* True if local node has sequence initiative */
+ bool seq_init;
+ /* iparams for hw io send call */
+ union efct_hw_io_param_u iparam;
+ /* HW formatted DIF parameters */
+ struct efct_hw_dif_info_s hw_dif;
+ /* DIF info saved for DIF error recovery */
+ struct efct_scsi_dif_info_s scsi_dif_info;
+ /* HW IO type */
+ enum efct_hw_io_type_e hio_type;
+ /* wire length */
+ u64 wire_len;
+ /* saved HW callback */
+ void *hw_cb;
+ /* Overflow SGL */
+ struct efc_dma_s ovfl_sgl;
+
+ /* for ELS requests/responses */
+ /* True if ELS is pending */
+ bool els_pend;
+ /* True if ELS is active */
+ bool els_active;
+ /* ELS request payload buffer */
+ struct efc_dma_s els_req;
+ /* ELS response payload buffer */
+ struct efc_dma_s els_rsp;
+ /* EIO IO state machine context */
+ //struct efc_sm_ctx_s els_sm;
+ /* current event posting nesting depth */
+ //uint els_evtdepth;
+ /* this els is to be free'd */
+ bool els_req_free;
+ /* Retries remaining */
+ u32 els_retries_remaining;
+ void (*els_callback)(struct efc_node_s *node,
+ struct efc_node_cb_s *cbdata, void *cbarg);
+ void *els_callback_arg;
+ /* timeout */
+ u32 els_timeout_sec;
+
+ /* delay timer */
+ struct timer_list delay_timer;
+
+ /* for abort handling */
+ /* pointer to IO to abort */
+ struct efct_io_s *io_to_abort;
+
+ enum efct_els_state_e state;
+ /* Protects els cmds */
+ spinlock_t els_lock;
+
+ /* SCSI Command buffer, used for CDB (initiator) */
+ struct efc_dma_s cmdbuf;
+ /* SCSI Response buffer (i+t) */
+ struct efc_dma_s rspbuf;
+ /* Timeout value in seconds for this IO */
+ u32 timeout;
+ /* CS_CTL priority for this IO */
+ u8 cs_ctl;
+ /* Is io object in freelist > */
+ u8 io_free;
+ u32 app_id;
+};
+
+/**
+ * @brief common IO callback argument
+ *
+ * Callback argument used as common I/O callback argument
+ */
+
+struct efct_io_cb_arg_s {
+ int status; /* completion status */
+ int ext_status; /* extended completion status */
+ void *app; /* application argument */
+};
+
+struct efct_io_pool_s *
+efct_io_pool_create(struct efct_s *efct, u32 num_io, u32 num_sgl);
+extern int
+efct_io_pool_free(struct efct_io_pool_s *io_pool);
+extern u32
+efct_io_pool_allocated(struct efct_io_pool_s *io_pool);
+
+extern struct efct_io_s *
+efct_io_pool_io_alloc(struct efct_io_pool_s *io_pool);
+extern void
+efct_io_pool_io_free(struct efct_io_pool_s *io_pool, struct efct_io_s *io);
+extern struct efct_io_s *
+efct_io_find_tgt_io(struct efct_s *efct, struct efc_node_s *node,
+ u16 ox_id, u16 rx_id);
+#endif /* __EFCT_IO_H__ */
--
2.13.7