[PATCH 28/32] elx: efct: IO timeout handling routines

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

This patch continues the efct driver population.

This patch adds driver definitions for:
Add support for a WQE timer to handle the wqe and IO timeouts.

Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
---
drivers/scsi/elx/efct/efct_hw.c | 209 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 209 insertions(+)

diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c
index 48cdbeebd058..751edbd2ddf9 100644
--- a/drivers/scsi/elx/efct/efct_hw.c
+++ b/drivers/scsi/elx/efct/efct_hw.c
@@ -5716,3 +5716,212 @@ efct_hw_get_num_eq(struct efct_hw_s *hw)
{
return hw->eq_count;
}
+
+/**
+ * @brief HW async call context structure.
+ */
+struct efct_hw_async_call_ctx_s {
+ efct_hw_async_cb_t callback;
+ void *arg;
+ u8 cmd[SLI4_BMBX_SIZE];
+};
+
+/**
+ * @brief HW async callback handler
+ *
+ * @par Description
+ * This function is called when the NOP mbox cmd completes. The callback stored
+ * in the requesting context is invoked.
+ *
+ * @param hw Pointer to HW object.
+ * @param status Completion status.
+ * @param mqe Pointer to mailbox completion queue entry.
+ * @param arg Caller-provided argument.
+ *
+ * @return None.
+ */
+static void
+efct_hw_async_cb(struct efct_hw_s *hw, int status, u8 *mqe, void *arg)
+{
+ struct efct_hw_async_call_ctx_s *ctx = arg;
+
+ if (ctx) {
+ if (ctx->callback)
+ (*ctx->callback)(hw, status, mqe, ctx->arg);
+
+ kfree(ctx);
+ }
+}
+
+/**
+ * @brief Make an async callback using NOP mailbox command
+ *
+ * @par Description
+ * Post a NOP mbox cmd; the callback with argument is invoked upon completion
+ * while in the event processing context.
+ *
+ * @param hw Pointer to HW object.
+ * @param callback Pointer to callback function.
+ * @param arg Caller-provided callback.
+ *
+ * @return Returns 0 on success, or a negative error code value on failure.
+ */
+int
+efct_hw_async_call(struct efct_hw_s *hw,
+ efct_hw_async_cb_t callback, void *arg)
+{
+ int rc = 0;
+ struct efct_hw_async_call_ctx_s *ctx;
+
+ /*
+ * Allocate a callback context (which includes the mbox cmd buffer),
+ * we need this to be persistent as the mbox cmd submission may be
+ * queued and executed later execution.
+ */
+ ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
+ if (!ctx)
+ return EFCT_HW_RTN_NO_MEMORY;
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->callback = callback;
+ ctx->arg = arg;
+
+ /* Build and send a NOP mailbox command */
+ if (!sli_cmd_common_nop(&hw->sli, ctx->cmd,
+ sizeof(ctx->cmd), 0) == 0) {
+ efc_log_err(hw->os, "COMMON_NOP format failure\n");
+ kfree(ctx);
+ rc = -1;
+ }
+
+ if (efct_hw_command(hw, ctx->cmd, EFCT_CMD_NOWAIT, efct_hw_async_cb,
+ ctx)) {
+ efc_log_err(hw->os, "COMMON_NOP command failure\n");
+ kfree(ctx);
+ rc = -1;
+ }
+ return rc;
+}
+
+static int
+target_wqe_timer_nop_cb(struct efct_hw_s *hw, int status,
+ u8 *mqe, void *arg)
+{
+ struct efct_hw_io_s *io = NULL;
+ struct efct_hw_io_s *io_next = NULL;
+ u64 ticks_current = jiffies_64;
+ u32 sec_elapsed;
+ struct sli4_mbox_command_header_s *hdr =
+ (struct sli4_mbox_command_header_s *)mqe;
+ unsigned long flags = 0;
+
+ if (status || le16_to_cpu(hdr->status)) {
+ efc_log_debug(hw->os, "bad status st=%x hdr=%x\n",
+ status,
+ le16_to_cpu(hdr->status));
+ /* go ahead and proceed with wqe timer checks... */
+ }
+
+ /* loop through active WQE list and check for timeouts */
+ spin_lock_irqsave(&hw->io_lock, flags);
+ list_for_each_entry_safe(io, io_next, &hw->io_timed_wqe, wqe_link) {
+ sec_elapsed = ((u32)(ticks_current - io->submit_ticks) / HZ);
+
+ /*
+ * If elapsed time > timeout, abort it. No need to check type
+ * since it wouldn't be on this list unless it was a target WQE
+ */
+ if (sec_elapsed > io->tgt_wqe_timeout) {
+ efc_log_test(hw->os,
+ "IO timeout xri=0x%x tag=0x%x type=%d\n",
+ io->indicator, io->reqtag, io->type);
+
+ /*
+ * remove from active_wqe list so won't try to abort
+ * again
+ */
+ list_del(&io->list_entry);
+
+ /* save status of timed_out for when abort completes */
+ io->status_saved = true;
+ io->saved_status =
+ SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT;
+ io->saved_ext = 0;
+ io->saved_len = 0;
+
+ /* now abort outstanding IO */
+ efct_hw_io_abort(hw, io, false, NULL, NULL);
+ }
+ /*
+ * need to go through entire list since each IO could have a
+ * different timeout value
+ */
+ }
+ spin_unlock_irqrestore(&hw->io_lock, flags);
+
+ /* if we're not in the middle of shutting down, schedule next timer */
+ if (!hw->active_wqe_timer_shutdown) {
+ timer_setup(&hw->wqe_timer,
+ &target_wqe_timer_cb, 0);
+
+ mod_timer(&hw->wqe_timer,
+ jiffies +
+ msecs_to_jiffies(EFCT_HW_WQ_TIMER_PERIOD_MS));
+ }
+ hw->in_active_wqe_timer = false;
+ return 0;
+}
+
+static void
+target_wqe_timer_cb(struct timer_list *t)
+{
+ struct efct_hw_s *hw = from_timer(hw, t, wqe_timer);
+
+ /*
+ * delete existing timer; will kick off new timer after checking wqe
+ * timeouts
+ */
+ hw->in_active_wqe_timer = true;
+ del_timer(&hw->wqe_timer);
+
+ /*
+ * Forward timer callback to execute in the mailbox completion
+ * processing context
+ */
+ if (efct_hw_async_call(hw, target_wqe_timer_nop_cb, hw))
+ efc_log_test(hw->os, "efct_hw_async_call failed\n");
+}
+
+static void
+shutdown_target_wqe_timer(struct efct_hw_s *hw)
+{
+ u32 iters = 100;
+
+ if (hw->config.emulate_tgt_wqe_timeout) {
+ /*
+ * request active wqe timer shutdown, then wait for it to
+ * complete
+ */
+ hw->active_wqe_timer_shutdown = true;
+
+ /*
+ * delete WQE timer and wait for timer handler to complete
+ * (if necessary)
+ */
+ del_timer(&hw->wqe_timer);
+
+ /* now wait for timer handler to complete (if necessary) */
+ while (hw->in_active_wqe_timer && iters) {
+ /*
+ * if we happen to have just sent NOP mbox cmn, make
+ * sure completions are being processed
+ */
+ efct_hw_flush(hw);
+ iters--;
+ }
+
+ if (iters == 0)
+ efc_log_test(hw->os,
+ "Failed to shutdown active wqe timer\n");
+ }
+}
--
2.13.7