From a04658594399e1fa25f984601b77ee840e6aaf01 Mon Sep 17 00:00:00 2001 From: Bikash Hazarika Date: Mon, 11 Jan 2021 01:31:31 -0800 Subject: [PATCH] scsi: qla2xxx: Wait for ABTS response on I/O timeouts for NVMe FW needs to wait for an ABTS response before completing the I/O. Link: https://lore.kernel.org/r/20210111093134.1206-5-njavali@marvell.com Reviewed-by: Himanshu Madhani Signed-off-by: Bikash Hazarika Signed-off-by: Saurav Kashyap Signed-off-by: Arun Easi Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_def.h | 12 ++++++ drivers/scsi/qla2xxx/qla_fw.h | 27 ++++++++++-- drivers/scsi/qla2xxx/qla_gbl.h | 6 +++ drivers/scsi/qla2xxx/qla_init.c | 4 ++ drivers/scsi/qla2xxx/qla_iocb.c | 6 +++ drivers/scsi/qla2xxx/qla_isr.c | 9 +++- drivers/scsi/qla2xxx/qla_mbx.c | 6 +++ drivers/scsi/qla2xxx/qla_nvme.c | 91 ++++++++++++++++++++++++++++++++++++++++- drivers/scsi/qla2xxx/qla_os.c | 5 +++ 9 files changed, 160 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index ca67be8..49b42b4 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2101,6 +2101,7 @@ typedef struct { #define CS_COMPLETE_CHKCOND 0x30 /* Error? */ #define CS_IOCB_ERROR 0x31 /* Generic error for IOCB request failure */ +#define CS_REJECT_RECEIVED 0x4E /* Reject received */ #define CS_BAD_PAYLOAD 0x80 /* Driver defined */ #define CS_UNKNOWN 0x81 /* Driver defined */ #define CS_RETRY 0x82 /* Driver defined */ @@ -4150,6 +4151,17 @@ struct qla_hw_data { /* Bit 21 of fw_attributes decides the MCTP capabilities */ #define IS_MCTP_CAPABLE(ha) (IS_QLA2031(ha) && \ ((ha)->fw_attributes_ext[0] & BIT_0)) +#define QLA_ABTS_FW_ENABLED(_ha) ((_ha)->fw_attributes_ext[0] & BIT_14) +#define QLA_SRB_NVME_LS(_sp) ((_sp)->type == SRB_NVME_LS) +#define QLA_SRB_NVME_CMD(_sp) ((_sp)->type == SRB_NVME_CMD) +#define QLA_NVME_IOS(_sp) (QLA_SRB_NVME_CMD(_sp) || QLA_SRB_NVME_LS(_sp)) +#define QLA_LS_ABTS_WAIT_ENABLED(_sp) \ + (QLA_SRB_NVME_LS(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw)) +#define QLA_CMD_ABTS_WAIT_ENABLED(_sp) \ + (QLA_SRB_NVME_CMD(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw)) +#define QLA_ABTS_WAIT_ENABLED(_sp) \ + (QLA_NVME_IOS(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw)) + #define IS_PI_UNINIT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) #define IS_PI_IPGUARD_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) #define IS_PI_DIFB_DIX0_CAPABLE(ha) (0) diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 12b689e..49df418 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -982,11 +982,18 @@ struct abort_entry_24xx { uint32_t handle; /* System handle. */ - __le16 nport_handle; /* N_PORT handle. */ - /* or Completion status. */ + union { + __le16 nport_handle; /* N_PORT handle. */ + __le16 comp_status; /* Completion status. */ + }; __le16 options; /* Options. */ #define AOF_NO_ABTS BIT_0 /* Do not send any ABTS. */ +#define AOF_NO_RRQ BIT_1 /* Do not send RRQ. */ +#define AOF_ABTS_TIMEOUT BIT_2 /* Disable logout on ABTS timeout. */ +#define AOF_ABTS_RTY_CNT BIT_3 /* Use driver specified retry count. */ +#define AOF_RSP_TIMEOUT BIT_4 /* Use specified response timeout. */ + uint32_t handle_to_abort; /* System handle to abort. */ @@ -995,8 +1002,20 @@ struct abort_entry_24xx { uint8_t port_id[3]; /* PortID of destination port. */ uint8_t vp_index; - - uint8_t reserved_2[12]; + u8 reserved_2[4]; + union { + struct { + __le16 abts_rty_cnt; + __le16 rsp_timeout; + } drv; + struct { + u8 ba_rjt_vendorUnique; + u8 ba_rjt_reasonCodeExpl; + u8 ba_rjt_reasonCode; + u8 reserved_3; + } fw; + }; + u8 reserved_4[4]; }; #define ABTS_RCV_TYPE 0x54 diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 708f823..6486f97 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -177,6 +177,7 @@ extern int ql2xexlogins; extern int ql2xdifbundlinginternalbuffers; extern int ql2xfulldump_on_mpifail; extern int ql2xenforce_iocb_limit; +extern int ql2xabts_wait_nvme; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -941,6 +942,11 @@ int qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode); extern void qla24xx_process_purex_list(struct purex_list *); extern void qla2x00_dfs_create_rport(scsi_qla_host_t *vha, struct fc_port *fp); extern void qla2x00_dfs_remove_rport(scsi_qla_host_t *vha, struct fc_port *fp); +extern void qla_wait_nvme_release_cmd_kref(srb_t *sp); +extern void qla_nvme_abort_set_option + (struct abort_entry_24xx *abt, srb_t *sp); +extern void qla_nvme_abort_process_comp_status + (struct abort_entry_24xx *abt, srb_t *sp); /* nvme.c */ void qla_nvme_unregister_remote_port(struct fc_port *fcport); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 9689426..ca844e3 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -136,6 +136,10 @@ static void qla24xx_abort_iocb_timeout(void *data) static void qla24xx_abort_sp_done(srb_t *sp, int res) { struct srb_iocb *abt = &sp->u.iocb_cmd; + srb_t *orig_sp = sp->cmd_sp; + + if (orig_sp) + qla_wait_nvme_release_cmd_kref(orig_sp); del_timer(&sp->u.iocb_cmd.timer); if (sp->flags & SRB_WAKEUP_ON_COMP) diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index c532c74..e27359b 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -3571,6 +3571,7 @@ qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb) struct srb_iocb *aio = &sp->u.iocb_cmd; scsi_qla_host_t *vha = sp->vha; struct req_que *req = sp->qpair->req; + srb_t *orig_sp = sp->cmd_sp; memset(abt_iocb, 0, sizeof(struct abort_entry_24xx)); abt_iocb->entry_type = ABORT_IOCB_TYPE; @@ -3587,6 +3588,11 @@ qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb) aio->u.abt.cmd_hndl); abt_iocb->vp_index = vha->vp_idx; abt_iocb->req_que_no = aio->u.abt.req_que_no; + + /* need to pass original sp */ + if (orig_sp) + qla_nvme_abort_set_option(abt_iocb, orig_sp); + /* Send the command to the firmware */ wmb(); } diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index bfc8bba..5ceb45d 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -5,6 +5,7 @@ */ #include "qla_def.h" #include "qla_target.h" +#include "qla_gbl.h" #include #include @@ -3431,6 +3432,7 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, { const char func[] = "ABT_IOCB"; srb_t *sp; + srb_t *orig_sp = NULL; struct srb_iocb *abt; sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); @@ -3438,7 +3440,12 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, return; abt = &sp->u.iocb_cmd; - abt->u.abt.comp_status = pkt->nport_handle; + abt->u.abt.comp_status = le16_to_cpu(pkt->comp_status); + orig_sp = sp->cmd_sp; + /* Need to pass original sp */ + if (orig_sp) + qla_nvme_abort_process_comp_status(pkt, orig_sp); + sp->done(sp, 0); } diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index f438cde..629af6f 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -3243,6 +3243,8 @@ qla24xx_abort_command(srb_t *sp) abt->vp_index = fcport->vha->vp_idx; abt->req_que_no = cpu_to_le16(req->id); + /* Need to pass original sp */ + qla_nvme_abort_set_option(abt, sp); rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0); if (rval != QLA_SUCCESS) { @@ -3265,6 +3267,10 @@ qla24xx_abort_command(srb_t *sp) ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091, "Done %s.\n", __func__); } + if (rval == QLA_SUCCESS) + qla_nvme_abort_process_comp_status(abt, sp); + + qla_wait_nvme_release_cmd_kref(sp); dma_pool_free(ha->s_dma_pool, abt, abt_dma); diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index eab559b..017b4c2 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -245,6 +245,13 @@ static void qla_nvme_abort_work(struct work_struct *work) __func__, (rval != QLA_SUCCESS) ? "Failed to abort" : "Aborted", sp, sp->handle, fcport, rval); + /* + * Returned before decreasing kref so that I/O requests + * are waited until ABTS complete. This kref is decreased + * at qla24xx_abort_sp_done function. + */ + if (ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(sp)) + return; out: /* kref_get was done before work was schedule. */ kref_put(&sp->cmd_kref, sp->put_fn); @@ -284,7 +291,6 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport, struct qla_hw_data *ha; srb_t *sp; - if (!fcport || (fcport && fcport->deleted)) return rval; @@ -591,6 +597,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport, sp->put_fn = qla_nvme_release_fcp_cmd_kref; sp->qpair = qpair; sp->vha = vha; + sp->cmd_sp = sp; nvme = &sp->u.iocb_cmd; nvme->u.nvme.desc = fd; @@ -744,3 +751,85 @@ int qla_nvme_register_hba(struct scsi_qla_host *vha) return ret; } + +void qla_nvme_abort_set_option(struct abort_entry_24xx *abt, srb_t *orig_sp) +{ + struct qla_hw_data *ha; + + if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp))) + return; + + ha = orig_sp->fcport->vha->hw; + + WARN_ON_ONCE(abt->options & cpu_to_le16(BIT_0)); + /* Use Driver Specified Retry Count */ + abt->options |= cpu_to_le16(AOF_ABTS_RTY_CNT); + abt->drv.abts_rty_cnt = cpu_to_le16(2); + /* Use specified response timeout */ + abt->options |= cpu_to_le16(AOF_RSP_TIMEOUT); + /* set it to 2 * r_a_tov in secs */ + abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10)); +} + +void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *orig_sp) +{ + u16 comp_status; + struct scsi_qla_host *vha; + + if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp))) + return; + + vha = orig_sp->fcport->vha; + + comp_status = le16_to_cpu(abt->comp_status); + switch (comp_status) { + case CS_RESET: /* reset event aborted */ + case CS_ABORTED: /* IOCB was cleaned */ + /* N_Port handle is not currently logged in */ + case CS_TIMEOUT: + /* N_Port handle was logged out while waiting for ABTS to complete */ + case CS_PORT_UNAVAILABLE: + /* Firmware found that the port name changed */ + case CS_PORT_LOGGED_OUT: + /* BA_RJT was received for the ABTS */ + case CS_PORT_CONFIG_CHG: + ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09d, + "Abort I/O IOCB completed with error, comp_status=%x\n", + comp_status); + break; + + /* BA_RJT was received for the ABTS */ + case CS_REJECT_RECEIVED: + ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09e, + "BA_RJT was received for the ABTS rjt_vendorUnique = %u", + abt->fw.ba_rjt_vendorUnique); + ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09e, + "ba_rjt_reasonCodeExpl = %u, ba_rjt_reasonCode = %u\n", + abt->fw.ba_rjt_reasonCodeExpl, abt->fw.ba_rjt_reasonCode); + break; + + case CS_COMPLETE: + ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09f, + "IOCB request is completed successfully comp_status=%x\n", + comp_status); + break; + + case CS_IOCB_ERROR: + ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf0a0, + "IOCB request is failed, comp_status=%x\n", comp_status); + break; + + default: + ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf0a1, + "Invalid Abort IO IOCB Completion Status %x\n", + comp_status); + break; + } +} + +inline void qla_wait_nvme_release_cmd_kref(srb_t *orig_sp) +{ + if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp))) + return; + kref_put(&orig_sp->cmd_kref, orig_sp->put_fn); +} diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index a760cb3..3cfd83fc 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -327,6 +327,11 @@ MODULE_PARM_DESC(ql2xrdpenable, "Enables RDP responses. " "0 - no RDP responses (default). " "1 - provide RDP responses."); +int ql2xabts_wait_nvme = 1; +module_param(ql2xabts_wait_nvme, int, 0444); +MODULE_PARM_DESC(ql2xabts_wait_nvme, + "To wait for ABTS response on I/O timeouts for NVMe. (default: 1)"); + static void qla2x00_clear_drv_active(struct qla_hw_data *); static void qla2x00_free_device(scsi_qla_host_t *); -- 2.7.4