From 49f618cea5736a6c65f0610037aadddf179b72fb Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 28 Dec 2020 22:56:20 +0000 Subject: [PATCH] qla2x00t-32gbit: Add IOCB resource tracking This patch tracks number of IOCB resources used in the I/O fast path. If the number of used IOCBs reach a high water limit, driver would return the I/O as busy and let upper layer retry. This prevents over subscription of IOCB resources where any future error recovery command is unable to cut through. Enable IOCB throttling by default. Link: https://lore.kernel.org/r/20200904045128.23631-12-njavali@marvell.com Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Arun Easi Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen See also upstream commit 89c72f4245a8510ffdb0d1980944210b8bc6e49f. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@9266 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- qla2x00t-32gbit/qla_def.h | 17 +++++++++++ qla2x00t-32gbit/qla_dfs.c | 14 +++++++++ qla2x00t-32gbit/qla_gbl.h | 3 ++ qla2x00t-32gbit/qla_init.c | 26 +++++++++++++++++ qla2x00t-32gbit/qla_inline.h | 55 ++++++++++++++++++++++++++++++++++++ qla2x00t-32gbit/qla_iocb.c | 28 ++++++++++++++++++ qla2x00t-32gbit/qla_isr.c | 2 ++ qla2x00t-32gbit/qla_os.c | 6 ++++ 8 files changed, 151 insertions(+) diff --git a/qla2x00t-32gbit/qla_def.h b/qla2x00t-32gbit/qla_def.h index 5b5decc3b..f78a1f1ab 100644 --- a/qla2x00t-32gbit/qla_def.h +++ b/qla2x00t-32gbit/qla_def.h @@ -645,6 +645,12 @@ enum { TYPE_TGT_TMCMD, /* task management */ }; +struct iocb_resource { + u8 res_type; + u8 pad; + u16 iocb_cnt; +}; + typedef struct srb { /* * Do not move cmd_type field, it needs to @@ -652,6 +658,7 @@ typedef struct srb { */ uint8_t cmd_type; uint8_t pad[3]; + struct iocb_resource iores; struct kref cmd_kref; /* need to migrate ref_count over to this */ void *priv; wait_queue_head_t nvme_ls_waitq; @@ -3603,6 +3610,15 @@ struct req_que { uint8_t req_pkt[REQUEST_ENTRY_SIZE]; }; +struct qla_fw_resources { + u16 iocbs_total; + u16 iocbs_limit; + u16 iocbs_qp_limit; + u16 iocbs_used; +}; + +#define QLA_IOCB_PCT_LIMIT 95 + /*Queue pair data structure */ struct qla_qpair { spinlock_t qp_lock; @@ -3655,6 +3671,7 @@ struct qla_qpair { uint64_t retry_term_jiff; struct qla_tgt_counters tgt_counters; uint16_t cpuid; + struct qla_fw_resources fwres ____cacheline_aligned; }; /* Place holder for FW buffer parameters */ diff --git a/qla2x00t-32gbit/qla_dfs.c b/qla2x00t-32gbit/qla_dfs.c index 6de10f5dd..f89ad3274 100644 --- a/qla2x00t-32gbit/qla_dfs.c +++ b/qla2x00t-32gbit/qla_dfs.c @@ -260,6 +260,8 @@ qla_dfs_fw_resource_cnt_show(struct seq_file *s, void *unused) struct scsi_qla_host *vha = s->private; uint16_t mb[MAX_IOCB_MB_REG]; int rc; + struct qla_hw_data *ha = vha->hw; + u16 iocbs_used, i; rc = qla24xx_res_count_wait(vha, mb, SIZEOF_IOCB_MB_REG); if (rc != QLA_SUCCESS) { @@ -284,6 +286,18 @@ qla_dfs_fw_resource_cnt_show(struct seq_file *s, void *unused) mb[23]); } + if (ql2xenforce_iocb_limit) { + /* lock is not require. It's an estimate. */ + iocbs_used = ha->base_qpair->fwres.iocbs_used; + for (i = 0; i < ha->max_qpairs; i++) { + if (ha->queue_pair_map[i]) + iocbs_used += ha->queue_pair_map[i]->fwres.iocbs_used; + } + + seq_printf(s, "Driver: estimate iocb used [%d] high water limit [%d]\n", + iocbs_used, ha->base_qpair->fwres.iocbs_limit); + } + return 0; } diff --git a/qla2x00t-32gbit/qla_gbl.h b/qla2x00t-32gbit/qla_gbl.h index 94941fd30..727a70a66 100644 --- a/qla2x00t-32gbit/qla_gbl.h +++ b/qla2x00t-32gbit/qla_gbl.h @@ -128,6 +128,8 @@ int qla2x00_reserve_mgmt_server_loop_id(scsi_qla_host_t *); void qla_rscn_replay(fc_port_t *fcport); void qla24xx_free_purex_item(struct purex_item *item); extern bool qla24xx_risc_firmware_invalid(uint32_t *); +void qla_init_iocb_limit(scsi_qla_host_t *); + /* * Global Data in qla_os.c source file. @@ -178,6 +180,7 @@ extern int qla2xuseresexchforels; extern int ql2xexlogins; extern int ql2xdifbundlinginternalbuffers; extern int ql2xfulldump_on_mpifail; +extern int ql2xenforce_iocb_limit; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); diff --git a/qla2x00t-32gbit/qla_init.c b/qla2x00t-32gbit/qla_init.c index a4ff8e5a3..e331bd919 100644 --- a/qla2x00t-32gbit/qla_init.c +++ b/qla2x00t-32gbit/qla_init.c @@ -3631,6 +3631,31 @@ out: return ha->flags.lr_detected; } +void qla_init_iocb_limit(scsi_qla_host_t *vha) +{ + u16 i, num_qps; + u32 limit; + struct qla_hw_data *ha = vha->hw; + + num_qps = ha->num_qpairs + 1; + limit = (ha->orig_fw_iocb_count * QLA_IOCB_PCT_LIMIT) / 100; + + ha->base_qpair->fwres.iocbs_total = ha->orig_fw_iocb_count; + ha->base_qpair->fwres.iocbs_limit = limit; + ha->base_qpair->fwres.iocbs_qp_limit = limit / num_qps; + ha->base_qpair->fwres.iocbs_used = 0; + for (i = 0; i < ha->max_qpairs; i++) { + if (ha->queue_pair_map[i]) { + ha->queue_pair_map[i]->fwres.iocbs_total = + ha->orig_fw_iocb_count; + ha->queue_pair_map[i]->fwres.iocbs_limit = limit; + ha->queue_pair_map[i]->fwres.iocbs_qp_limit = + limit / num_qps; + ha->queue_pair_map[i]->fwres.iocbs_used = 0; + } + } +} + /** * qla2x00_setup_chip() - Load and start RISC firmware. * @vha: HA context @@ -3730,6 +3755,7 @@ enable_82xx_npiv: MIN_MULTI_ID_FABRIC - 1; } qla2x00_get_resource_cnts(vha); + qla_init_iocb_limit(vha); /* * Allocate the array of outstanding commands diff --git a/qla2x00t-32gbit/qla_inline.h b/qla2x00t-32gbit/qla_inline.h index f7f13a6e4..ca33521e2 100644 --- a/qla2x00t-32gbit/qla_inline.h +++ b/qla2x00t-32gbit/qla_inline.h @@ -385,3 +385,58 @@ qla2xxx_get_fc4_priority(struct scsi_qla_host *vha) return (data >> 6) & BIT_0 ? FC4_PRIORITY_FCP : FC4_PRIORITY_NVME; } + +enum { + RESOURCE_NONE, + RESOURCE_INI, +}; + +static inline int +qla_get_iocbs(struct qla_qpair *qp, struct iocb_resource *iores) +{ + u16 iocbs_used, i; + struct qla_hw_data *ha = qp->vha->hw; + + if (!ql2xenforce_iocb_limit) { + iores->res_type = RESOURCE_NONE; + return 0; + } + + if ((iores->iocb_cnt + qp->fwres.iocbs_used) < qp->fwres.iocbs_qp_limit) { + qp->fwres.iocbs_used += iores->iocb_cnt; + return 0; + } else { + /* no need to acquire qpair lock. It's just rough calculation */ + iocbs_used = ha->base_qpair->fwres.iocbs_used; + for (i = 0; i < ha->max_qpairs; i++) { + if (ha->queue_pair_map[i]) + iocbs_used += ha->queue_pair_map[i]->fwres.iocbs_used; + } + + if ((iores->iocb_cnt + iocbs_used) < qp->fwres.iocbs_limit) { + qp->fwres.iocbs_used += iores->iocb_cnt; + return 0; + } else { + iores->res_type = RESOURCE_NONE; + return -ENOSPC; + } + } +} + +static inline void +qla_put_iocbs(struct qla_qpair *qp, struct iocb_resource *iores) +{ + switch (iores->res_type) { + case RESOURCE_NONE: + break; + default: + if (qp->fwres.iocbs_used >= iores->iocb_cnt) { + qp->fwres.iocbs_used -= iores->iocb_cnt; + } else { + // should not happen + qp->fwres.iocbs_used = 0; + } + break; + } + iores->res_type = RESOURCE_NONE; +} diff --git a/qla2x00t-32gbit/qla_iocb.c b/qla2x00t-32gbit/qla_iocb.c index 2b59c18dd..009ab8b6a 100644 --- a/qla2x00t-32gbit/qla_iocb.c +++ b/qla2x00t-32gbit/qla_iocb.c @@ -1642,6 +1642,12 @@ qla24xx_start_scsi(srb_t *sp) tot_dsds = nseg; req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + + sp->iores.res_type = RESOURCE_INI; + sp->iores.iocb_cnt = req_cnt; + if (qla_get_iocbs(sp->qpair, &sp->iores)) + goto queuing_error; + if (req->cnt < (req_cnt + 2)) { cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : rd_reg_dword_relaxed(req->req_q_out); @@ -1714,6 +1720,7 @@ queuing_error: if (tot_dsds) scsi_dma_unmap(cmd); + qla_put_iocbs(sp->qpair, &sp->iores); spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_FUNCTION_FAILED; @@ -1827,6 +1834,12 @@ qla24xx_dif_start_scsi(srb_t *sp) /* Total Data and protection sg segment(s) */ tot_prot_dsds = nseg; tot_dsds += nseg; + + sp->iores.res_type = RESOURCE_INI; + sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + if (qla_get_iocbs(sp->qpair, &sp->iores)) + goto queuing_error; + if (req->cnt < (req_cnt + 2)) { cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : rd_reg_dword_relaxed(req->req_q_out); @@ -1901,6 +1914,7 @@ queuing_error: } /* Cleanup will be performed by the caller (queuecommand) */ + qla_put_iocbs(sp->qpair, &sp->iores); spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_FUNCTION_FAILED; } @@ -1962,6 +1976,12 @@ qla2xxx_start_scsi_mq(srb_t *sp) tot_dsds = nseg; req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + + sp->iores.res_type = RESOURCE_INI; + sp->iores.iocb_cnt = req_cnt; + if (qla_get_iocbs(sp->qpair, &sp->iores)) + goto queuing_error; + if (req->cnt < (req_cnt + 2)) { cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : rd_reg_dword_relaxed(req->req_q_out); @@ -2034,6 +2054,7 @@ queuing_error: if (tot_dsds) scsi_dma_unmap(cmd); + qla_put_iocbs(sp->qpair, &sp->iores); spin_unlock_irqrestore(&qpair->qp_lock, flags); return QLA_FUNCTION_FAILED; @@ -2162,6 +2183,12 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp) /* Total Data and protection sg segment(s) */ tot_prot_dsds = nseg; tot_dsds += nseg; + + sp->iores.res_type = RESOURCE_INI; + sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + if (qla_get_iocbs(sp->qpair, &sp->iores)) + goto queuing_error; + if (req->cnt < (req_cnt + 2)) { cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : rd_reg_dword_relaxed(req->req_q_out); @@ -2239,6 +2266,7 @@ queuing_error: } /* Cleanup will be performed by the caller (queuecommand) */ + qla_put_iocbs(sp->qpair, &sp->iores); spin_unlock_irqrestore(&qpair->qp_lock, flags); return QLA_FUNCTION_FAILED; } diff --git a/qla2x00t-32gbit/qla_isr.c b/qla2x00t-32gbit/qla_isr.c index 1d5c62635..264e2a3ab 100644 --- a/qla2x00t-32gbit/qla_isr.c +++ b/qla2x00t-32gbit/qla_isr.c @@ -2935,6 +2935,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) } return; } + qla_put_iocbs(sp->qpair, &sp->iores); if (sp->cmd_type != TYPE_SRB) { req->outstanding_cmds[handle] = NULL; @@ -3347,6 +3348,7 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt) default: sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (sp) { + qla_put_iocbs(sp->qpair, &sp->iores); sp->done(sp, res); return 0; } diff --git a/qla2x00t-32gbit/qla_os.c b/qla2x00t-32gbit/qla_os.c index 47f9f3e26..892e42a36 100644 --- a/qla2x00t-32gbit/qla_os.c +++ b/qla2x00t-32gbit/qla_os.c @@ -52,6 +52,11 @@ module_param(ql2xfulldump_on_mpifail, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ql2xfulldump_on_mpifail, "Set this to take full dump on MPI hang."); +int ql2xenforce_iocb_limit = 1; +module_param(ql2xenforce_iocb_limit, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ql2xenforce_iocb_limit, + "Enforce IOCB throttling, to avoid FW congestion. (default: 0)"); + /* * CT6 CTX allocation cache */ @@ -3363,6 +3368,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) for (i = 0; i < ha->max_qpairs; i++) qla2xxx_create_qpair(base_vha, 5, 0, startit); } + qla_init_iocb_limit(base_vha); if (ha->flags.running_gold_fw) goto skip_dpc;