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@xxxxxxxxxxxx> Signed-off-by: James Smart <jsmart2021@xxxxxxxxx> --- 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