mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-27 04:47:05 +00:00
scsi: qla2xxx: Fix exchange oversubscription
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: 89c72f4245
("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>
This commit is contained in:
parent
c75e6aef50
commit
41e5afe51f
7 changed files with 88 additions and 36 deletions
|
@ -660,7 +660,7 @@ enum {
|
|||
|
||||
struct iocb_resource {
|
||||
u8 res_type;
|
||||
u8 pad;
|
||||
u8 exch_cnt;
|
||||
u16 iocb_cnt;
|
||||
};
|
||||
|
||||
|
@ -3721,6 +3721,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
|
||||
|
|
|
@ -2989,9 +2989,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)) {
|
||||
|
@ -3185,7 +3186,7 @@ qla28xx_start_scsi_edif(srb_t *sp)
|
|||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -2000,6 +2002,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;
|
||||
}
|
||||
}
|
||||
|
@ -3943,6 +3946,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 =
|
||||
|
@ -3951,6 +3960,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 @@ qla24xx_start_scsi(srb_t *sp)
|
|||
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 @@ qla24xx_dif_start_scsi(srb_t *sp)
|
|||
}
|
||||
/* 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 @@ qla2xxx_start_scsi_mq(srb_t *sp)
|
|||
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 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
|
|||
}
|
||||
/* 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;
|
||||
|
|
|
@ -3197,7 +3197,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;
|
||||
|
@ -3618,7 +3618,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;
|
||||
}
|
||||
|
|
|
@ -428,13 +428,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)
|
||||
|
@ -583,6 +594,8 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
|
|||
qla24xx_process_response_queue(vha, rsp);
|
||||
|
||||
queuing_error:
|
||||
if (rval)
|
||||
qla_put_fw_resources(sp->qpair, &sp->iores);
|
||||
spin_unlock_irqrestore(&qpair->qp_lock, flags);
|
||||
|
||||
return rval;
|
||||
|
|
Loading…
Reference in a new issue