scsi: qla2xxx: Fix exchange oversubscription
authorQuinn Tran <qutran@marvell.com>
Mon, 19 Dec 2022 11:07:41 +0000 (03:07 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 08:39:22 +0000 (09:39 +0100)
[ Upstream commit 41e5afe51f75f2858f5563145348f6c26d307b8f ]

In large environment, it is possible to experience command timeout and
escalation of path recovery. Currently the driver does not track the number
of exchanges/commands sent to FW. If there is a delay for commands at the
head of the queue, then this will create back pressure for commands at the
back of the queue.

Check for exchange availability before command submission.

Fixes: 89c72f4245a8 ("scsi: qla2xxx: Add IOCB resource tracking")
Signed-off-by: Quinn Tran <qutran@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_edif.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_inline.h
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_nvme.c

index 307ffdfe048beb0566daa6b0e1a5f391f7825ec2..5b499b0e2c8673e26e49d30cded8b5d2059de020 100644 (file)
@@ -655,7 +655,7 @@ enum {
 
 struct iocb_resource {
        u8 res_type;
-       u8 pad;
+       u8  exch_cnt;
        u16 iocb_cnt;
 };
 
@@ -3707,6 +3707,10 @@ struct qla_fw_resources {
        u16 iocbs_limit;
        u16 iocbs_qp_limit;
        u16 iocbs_used;
+       u16 exch_total;
+       u16 exch_limit;
+       u16 exch_used;
+       u16 pad;
 };
 
 #define QLA_IOCB_PCT_LIMIT 95
index a7e2118b3a841c124dcb8c546f3e94fc39289474..f81cf85dcdc7bb924249842c106447f1180e1ec6 100644 (file)
@@ -2899,9 +2899,10 @@ qla28xx_start_scsi_edif(srb_t *sp)
        tot_dsds = nseg;
        req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 
-       sp->iores.res_type = RESOURCE_INI;
+       sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
+       sp->iores.exch_cnt = 1;
        sp->iores.iocb_cnt = req_cnt;
-       if (qla_get_iocbs(sp->qpair, &sp->iores))
+       if (qla_get_fw_resources(sp->qpair, &sp->iores))
                goto queuing_error;
 
        if (req->cnt < (req_cnt + 2)) {
@@ -3095,7 +3096,7 @@ queuing_error:
                mempool_free(sp->u.scmd.ct6_ctx, ha->ctx_mempool);
                sp->u.scmd.ct6_ctx = NULL;
        }
-       qla_put_iocbs(sp->qpair, &sp->iores);
+       qla_put_fw_resources(sp->qpair, &sp->iores);
        spin_unlock_irqrestore(lock, flags);
 
        return QLA_FUNCTION_FAILED;
index 30798ab84db91d6903f4948e10ab9eb0b99350e5..b61aa5a1a21dc535c5664ae141ea3d335ff58d1f 100644 (file)
@@ -128,12 +128,14 @@ static void qla24xx_abort_iocb_timeout(void *data)
                    sp->cmd_sp)) {
                        qpair->req->outstanding_cmds[handle] = NULL;
                        cmdsp_found = 1;
+                       qla_put_fw_resources(qpair, &sp->cmd_sp->iores);
                }
 
                /* removing the abort */
                if (qpair->req->outstanding_cmds[handle] == sp) {
                        qpair->req->outstanding_cmds[handle] = NULL;
                        sp_found = 1;
+                       qla_put_fw_resources(qpair, &sp->iores);
                        break;
                }
        }
@@ -2002,6 +2004,7 @@ qla2x00_tmf_iocb_timeout(void *data)
                for (h = 1; h < sp->qpair->req->num_outstanding_cmds; h++) {
                        if (sp->qpair->req->outstanding_cmds[h] == sp) {
                                sp->qpair->req->outstanding_cmds[h] = NULL;
+                               qla_put_fw_resources(sp->qpair, &sp->iores);
                                break;
                        }
                }
@@ -3945,6 +3948,12 @@ void qla_init_iocb_limit(scsi_qla_host_t *vha)
        ha->base_qpair->fwres.iocbs_limit = limit;
        ha->base_qpair->fwres.iocbs_qp_limit = limit / num_qps;
        ha->base_qpair->fwres.iocbs_used = 0;
+
+       ha->base_qpair->fwres.exch_total = ha->orig_fw_xcb_count;
+       ha->base_qpair->fwres.exch_limit = (ha->orig_fw_xcb_count *
+                                           QLA_IOCB_PCT_LIMIT) / 100;
+       ha->base_qpair->fwres.exch_used  = 0;
+
        for (i = 0; i < ha->max_qpairs; i++) {
                if (ha->queue_pair_map[i])  {
                        ha->queue_pair_map[i]->fwres.iocbs_total =
@@ -3953,6 +3962,10 @@ void qla_init_iocb_limit(scsi_qla_host_t *vha)
                        ha->queue_pair_map[i]->fwres.iocbs_qp_limit =
                                limit / num_qps;
                        ha->queue_pair_map[i]->fwres.iocbs_used = 0;
+                       ha->queue_pair_map[i]->fwres.exch_total = ha->orig_fw_xcb_count;
+                       ha->queue_pair_map[i]->fwres.exch_limit =
+                               (ha->orig_fw_xcb_count * QLA_IOCB_PCT_LIMIT) / 100;
+                       ha->queue_pair_map[i]->fwres.exch_used = 0;
                }
        }
 }
index 5185dc5daf80d47cbf279faf95ce0e0a777603f8..2d5a275d8b00078a51597227554e7dd8c4320e0f 100644 (file)
@@ -380,13 +380,16 @@ qla2xxx_get_fc4_priority(struct scsi_qla_host *vha)
 
 enum {
        RESOURCE_NONE,
-       RESOURCE_INI,
+       RESOURCE_IOCB  = BIT_0,
+       RESOURCE_EXCH = BIT_1,  /* exchange */
+       RESOURCE_FORCE = BIT_2,
 };
 
 static inline int
-qla_get_iocbs(struct qla_qpair *qp, struct iocb_resource *iores)
+qla_get_fw_resources(struct qla_qpair *qp, struct iocb_resource *iores)
 {
        u16 iocbs_used, i;
+       u16 exch_used;
        struct qla_hw_data *ha = qp->vha->hw;
 
        if (!ql2xenforce_iocb_limit) {
@@ -394,10 +397,7 @@ qla_get_iocbs(struct qla_qpair *qp, struct iocb_resource *iores)
                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 {
+       if ((iores->iocb_cnt + qp->fwres.iocbs_used) >= qp->fwres.iocbs_qp_limit) {
                /* 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++) {
@@ -405,30 +405,48 @@ qla_get_iocbs(struct qla_qpair *qp, struct iocb_resource *iores)
                                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 {
+               if ((iores->iocb_cnt + iocbs_used) >= qp->fwres.iocbs_limit) {
+                       iores->res_type = RESOURCE_NONE;
+                       return -ENOSPC;
+               }
+       }
+
+       if (iores->res_type & RESOURCE_EXCH) {
+               exch_used = ha->base_qpair->fwres.exch_used;
+               for (i = 0; i < ha->max_qpairs; i++) {
+                       if (ha->queue_pair_map[i])
+                               exch_used += ha->queue_pair_map[i]->fwres.exch_used;
+               }
+
+               if ((exch_used + iores->exch_cnt) >= qp->fwres.exch_limit) {
                        iores->res_type = RESOURCE_NONE;
                        return -ENOSPC;
                }
        }
+       qp->fwres.iocbs_used += iores->iocb_cnt;
+       qp->fwres.exch_used += iores->exch_cnt;
+       return 0;
 }
 
 static inline void
-qla_put_iocbs(struct qla_qpair *qp, struct iocb_resource *iores)
+qla_put_fw_resources(struct qla_qpair *qp, struct iocb_resource *iores)
 {
-       switch (iores->res_type) {
-       case RESOURCE_NONE:
-               break;
-       default:
+       if (iores->res_type & RESOURCE_IOCB) {
                if (qp->fwres.iocbs_used >= iores->iocb_cnt) {
                        qp->fwres.iocbs_used -= iores->iocb_cnt;
                } else {
-                       // should not happen
+                       /* should not happen */
                        qp->fwres.iocbs_used = 0;
                }
-               break;
+       }
+
+       if (iores->res_type & RESOURCE_EXCH) {
+               if (qp->fwres.exch_used >= iores->exch_cnt) {
+                       qp->fwres.exch_used -= iores->exch_cnt;
+               } else {
+                       /* should not happen */
+                       qp->fwres.exch_used = 0;
+               }
        }
        iores->res_type = RESOURCE_NONE;
 }
index 42ce4e1fe7441fe8299e63a5ebaaeaebfe22992e..399ec8da2d73c3354cde61e96036f490efda6142 100644 (file)
@@ -1589,9 +1589,10 @@ 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.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
+       sp->iores.exch_cnt = 1;
        sp->iores.iocb_cnt = req_cnt;
-       if (qla_get_iocbs(sp->qpair, &sp->iores))
+       if (qla_get_fw_resources(sp->qpair, &sp->iores))
                goto queuing_error;
 
        if (req->cnt < (req_cnt + 2)) {
@@ -1678,7 +1679,7 @@ queuing_error:
        if (tot_dsds)
                scsi_dma_unmap(cmd);
 
-       qla_put_iocbs(sp->qpair, &sp->iores);
+       qla_put_fw_resources(sp->qpair, &sp->iores);
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
        return QLA_FUNCTION_FAILED;
@@ -1793,9 +1794,10 @@ qla24xx_dif_start_scsi(srb_t *sp)
        tot_prot_dsds = nseg;
        tot_dsds += nseg;
 
-       sp->iores.res_type = RESOURCE_INI;
+       sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
+       sp->iores.exch_cnt = 1;
        sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
-       if (qla_get_iocbs(sp->qpair, &sp->iores))
+       if (qla_get_fw_resources(sp->qpair, &sp->iores))
                goto queuing_error;
 
        if (req->cnt < (req_cnt + 2)) {
@@ -1883,7 +1885,7 @@ queuing_error:
        }
        /* Cleanup will be performed by the caller (queuecommand) */
 
-       qla_put_iocbs(sp->qpair, &sp->iores);
+       qla_put_fw_resources(sp->qpair, &sp->iores);
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
        return QLA_FUNCTION_FAILED;
@@ -1952,9 +1954,10 @@ 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.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
+       sp->iores.exch_cnt = 1;
        sp->iores.iocb_cnt = req_cnt;
-       if (qla_get_iocbs(sp->qpair, &sp->iores))
+       if (qla_get_fw_resources(sp->qpair, &sp->iores))
                goto queuing_error;
 
        if (req->cnt < (req_cnt + 2)) {
@@ -2041,7 +2044,7 @@ queuing_error:
        if (tot_dsds)
                scsi_dma_unmap(cmd);
 
-       qla_put_iocbs(sp->qpair, &sp->iores);
+       qla_put_fw_resources(sp->qpair, &sp->iores);
        spin_unlock_irqrestore(&qpair->qp_lock, flags);
 
        return QLA_FUNCTION_FAILED;
@@ -2171,9 +2174,10 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
        tot_prot_dsds = nseg;
        tot_dsds += nseg;
 
-       sp->iores.res_type = RESOURCE_INI;
+       sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
+       sp->iores.exch_cnt = 1;
        sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
-       if (qla_get_iocbs(sp->qpair, &sp->iores))
+       if (qla_get_fw_resources(sp->qpair, &sp->iores))
                goto queuing_error;
 
        if (req->cnt < (req_cnt + 2)) {
@@ -2260,7 +2264,7 @@ queuing_error:
        }
        /* Cleanup will be performed by the caller (queuecommand) */
 
-       qla_put_iocbs(sp->qpair, &sp->iores);
+       qla_put_fw_resources(sp->qpair, &sp->iores);
        spin_unlock_irqrestore(&qpair->qp_lock, flags);
 
        return QLA_FUNCTION_FAILED;
index 59f5918dca95f676de977147417f958b8e6b4849..c545f39362ffce2d0eda65a25a24b1e067ff2c98 100644 (file)
@@ -3184,7 +3184,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                }
                return;
        }
-       qla_put_iocbs(sp->qpair, &sp->iores);
+       qla_put_fw_resources(sp->qpair, &sp->iores);
 
        if (sp->cmd_type != TYPE_SRB) {
                req->outstanding_cmds[handle] = NULL;
@@ -3605,7 +3605,6 @@ 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;
                }
index 3e167dc4eec723d11ff71bd251bffc7fc791ce67..b66e19a8545c9062d4571c9b5a958e519525340a 100644 (file)
@@ -438,13 +438,24 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
                goto queuing_error;
        }
        req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+
+       sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
+       sp->iores.exch_cnt = 1;
+       sp->iores.iocb_cnt = req_cnt;
+       if (qla_get_fw_resources(sp->qpair, &sp->iores)) {
+               rval = -EBUSY;
+               goto queuing_error;
+       }
+
        if (req->cnt < (req_cnt + 2)) {
                if (IS_SHADOW_REG_CAPABLE(ha)) {
                        cnt = *req->out_ptr;
                } else {
                        cnt = rd_reg_dword_relaxed(req->req_q_out);
-                       if (qla2x00_check_reg16_for_disconnect(vha, cnt))
+                       if (qla2x00_check_reg16_for_disconnect(vha, cnt)) {
+                               rval = -EBUSY;
                                goto queuing_error;
+                       }
                }
 
                if (req->ring_index < cnt)
@@ -589,6 +600,8 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
        wrt_reg_dword(req->req_q_in, req->ring_index);
 
 queuing_error:
+       if (rval)
+               qla_put_fw_resources(sp->qpair, &sp->iores);
        spin_unlock_irqrestore(&qpair->qp_lock, flags);
 
        return rval;