@@ -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)
@@ -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
@@ -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);
@@ -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)
@@ -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();
}
@@ -5,6 +5,7 @@
*/
#include "qla_def.h"
#include "qla_target.h"
+#include "qla_gbl.h"
#include <linux/delay.h>
#include <linux/slab.h>
@@ -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);
@@ -3439,6 +3441,12 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
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);
}
@@ -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);
@@ -245,6 +245,12 @@ 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 +290,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 +596,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 +750,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);
+}
@@ -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 *);