scsi: lpfc: Add WQ Full Logic for NVME Target
I/O conditions on the nvme target may have the driver submitting to a full hardware wq. The hardware wq is a shared resource among all nvme controllers. When the driver hit a full wq, it failed the io posting back to the nvme-fc transport, which then escalated it into errors. Correct by maintaining a sideband queue within the driver that is added to when the WQ full condition is hit, and drained from as soon as new WQ space opens up. Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com> Signed-off-by: James Smart <james.smart@broadcom.com> Reviewed-by: Hannes Reinecke <hare@suse.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
8ae3370136
commit
6e8e1c14c6
@ -254,6 +254,7 @@ void lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba,
|
|||||||
struct lpfc_nvmet_ctxbuf *ctxp);
|
struct lpfc_nvmet_ctxbuf *ctxp);
|
||||||
int lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
|
int lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
|
||||||
struct fc_frame_header *fc_hdr);
|
struct fc_frame_header *fc_hdr);
|
||||||
|
void lpfc_nvmet_wqfull_process(struct lpfc_hba *phba, struct lpfc_queue *wq);
|
||||||
void lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba);
|
void lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba);
|
||||||
void lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba);
|
void lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba);
|
||||||
void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *,
|
void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *,
|
||||||
|
@ -71,6 +71,8 @@ static int lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *,
|
|||||||
static int lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *,
|
static int lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *,
|
||||||
struct lpfc_nvmet_rcv_ctx *,
|
struct lpfc_nvmet_rcv_ctx *,
|
||||||
uint32_t, uint16_t);
|
uint32_t, uint16_t);
|
||||||
|
static void lpfc_nvmet_wqfull_flush(struct lpfc_hba *, struct lpfc_queue *,
|
||||||
|
struct lpfc_nvmet_rcv_ctx *);
|
||||||
|
|
||||||
void
|
void
|
||||||
lpfc_nvmet_defer_release(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp)
|
lpfc_nvmet_defer_release(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp)
|
||||||
@ -741,7 +743,10 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport,
|
|||||||
struct lpfc_nvmet_rcv_ctx *ctxp =
|
struct lpfc_nvmet_rcv_ctx *ctxp =
|
||||||
container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req);
|
container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req);
|
||||||
struct lpfc_hba *phba = ctxp->phba;
|
struct lpfc_hba *phba = ctxp->phba;
|
||||||
|
struct lpfc_queue *wq;
|
||||||
struct lpfc_iocbq *nvmewqeq;
|
struct lpfc_iocbq *nvmewqeq;
|
||||||
|
struct lpfc_sli_ring *pring;
|
||||||
|
unsigned long iflags;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (phba->pport->load_flag & FC_UNLOADING) {
|
if (phba->pport->load_flag & FC_UNLOADING) {
|
||||||
@ -820,6 +825,21 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rc == -EBUSY) {
|
||||||
|
/*
|
||||||
|
* WQ was full, so queue nvmewqeq to be sent after
|
||||||
|
* WQE release CQE
|
||||||
|
*/
|
||||||
|
ctxp->flag |= LPFC_NVMET_DEFER_WQFULL;
|
||||||
|
wq = phba->sli4_hba.nvme_wq[rsp->hwqid];
|
||||||
|
pring = wq->pring;
|
||||||
|
spin_lock_irqsave(&pring->ring_lock, iflags);
|
||||||
|
list_add_tail(&nvmewqeq->list, &wq->wqfull_list);
|
||||||
|
wq->q_flag |= HBA_NVMET_WQFULL;
|
||||||
|
spin_unlock_irqrestore(&pring->ring_lock, iflags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Give back resources */
|
/* Give back resources */
|
||||||
atomic_inc(&lpfc_nvmep->xmt_fcp_drop);
|
atomic_inc(&lpfc_nvmep->xmt_fcp_drop);
|
||||||
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
|
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
|
||||||
@ -851,6 +871,7 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
|
|||||||
struct lpfc_nvmet_rcv_ctx *ctxp =
|
struct lpfc_nvmet_rcv_ctx *ctxp =
|
||||||
container_of(req, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req);
|
container_of(req, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req);
|
||||||
struct lpfc_hba *phba = ctxp->phba;
|
struct lpfc_hba *phba = ctxp->phba;
|
||||||
|
struct lpfc_queue *wq;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (phba->pport->load_flag & FC_UNLOADING)
|
if (phba->pport->load_flag & FC_UNLOADING)
|
||||||
@ -880,6 +901,14 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
|
|||||||
}
|
}
|
||||||
ctxp->flag |= LPFC_NVMET_ABORT_OP;
|
ctxp->flag |= LPFC_NVMET_ABORT_OP;
|
||||||
|
|
||||||
|
if (ctxp->flag & LPFC_NVMET_DEFER_WQFULL) {
|
||||||
|
lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid,
|
||||||
|
ctxp->oxid);
|
||||||
|
wq = phba->sli4_hba.nvme_wq[ctxp->wqeq->hba_wqidx];
|
||||||
|
lpfc_nvmet_wqfull_flush(phba, wq, ctxp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* An state of LPFC_NVMET_STE_RCV means we have just received
|
/* An state of LPFC_NVMET_STE_RCV means we have just received
|
||||||
* the NVME command and have not started processing it.
|
* the NVME command and have not started processing it.
|
||||||
* (by issuing any IO WQEs on this exchange yet)
|
* (by issuing any IO WQEs on this exchange yet)
|
||||||
@ -1435,16 +1464,103 @@ lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lpfc_nvmet_wqfull_flush(struct lpfc_hba *phba, struct lpfc_queue *wq,
|
||||||
|
struct lpfc_nvmet_rcv_ctx *ctxp)
|
||||||
|
{
|
||||||
|
struct lpfc_sli_ring *pring;
|
||||||
|
struct lpfc_iocbq *nvmewqeq;
|
||||||
|
struct lpfc_iocbq *next_nvmewqeq;
|
||||||
|
unsigned long iflags;
|
||||||
|
struct lpfc_wcqe_complete wcqe;
|
||||||
|
struct lpfc_wcqe_complete *wcqep;
|
||||||
|
|
||||||
|
pring = wq->pring;
|
||||||
|
wcqep = &wcqe;
|
||||||
|
|
||||||
|
/* Fake an ABORT error code back to cmpl routine */
|
||||||
|
memset(wcqep, 0, sizeof(struct lpfc_wcqe_complete));
|
||||||
|
bf_set(lpfc_wcqe_c_status, wcqep, IOSTAT_LOCAL_REJECT);
|
||||||
|
wcqep->parameter = IOERR_ABORT_REQUESTED;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&pring->ring_lock, iflags);
|
||||||
|
list_for_each_entry_safe(nvmewqeq, next_nvmewqeq,
|
||||||
|
&wq->wqfull_list, list) {
|
||||||
|
if (ctxp) {
|
||||||
|
/* Checking for a specific IO to flush */
|
||||||
|
if (nvmewqeq->context2 == ctxp) {
|
||||||
|
list_del(&nvmewqeq->list);
|
||||||
|
spin_unlock_irqrestore(&pring->ring_lock,
|
||||||
|
iflags);
|
||||||
|
lpfc_nvmet_xmt_fcp_op_cmp(phba, nvmewqeq,
|
||||||
|
wcqep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
/* Flush all IOs */
|
||||||
|
list_del(&nvmewqeq->list);
|
||||||
|
spin_unlock_irqrestore(&pring->ring_lock, iflags);
|
||||||
|
lpfc_nvmet_xmt_fcp_op_cmp(phba, nvmewqeq, wcqep);
|
||||||
|
spin_lock_irqsave(&pring->ring_lock, iflags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ctxp)
|
||||||
|
wq->q_flag &= ~HBA_NVMET_WQFULL;
|
||||||
|
spin_unlock_irqrestore(&pring->ring_lock, iflags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
lpfc_nvmet_wqfull_process(struct lpfc_hba *phba,
|
||||||
|
struct lpfc_queue *wq)
|
||||||
|
{
|
||||||
|
#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
|
||||||
|
struct lpfc_sli_ring *pring;
|
||||||
|
struct lpfc_iocbq *nvmewqeq;
|
||||||
|
unsigned long iflags;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some WQE slots are available, so try to re-issue anything
|
||||||
|
* on the WQ wqfull_list.
|
||||||
|
*/
|
||||||
|
pring = wq->pring;
|
||||||
|
spin_lock_irqsave(&pring->ring_lock, iflags);
|
||||||
|
while (!list_empty(&wq->wqfull_list)) {
|
||||||
|
list_remove_head(&wq->wqfull_list, nvmewqeq, struct lpfc_iocbq,
|
||||||
|
list);
|
||||||
|
spin_unlock_irqrestore(&pring->ring_lock, iflags);
|
||||||
|
rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, nvmewqeq);
|
||||||
|
spin_lock_irqsave(&pring->ring_lock, iflags);
|
||||||
|
if (rc == -EBUSY) {
|
||||||
|
/* WQ was full again, so put it back on the list */
|
||||||
|
list_add(&nvmewqeq->list, &wq->wqfull_list);
|
||||||
|
spin_unlock_irqrestore(&pring->ring_lock, iflags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wq->q_flag &= ~HBA_NVMET_WQFULL;
|
||||||
|
spin_unlock_irqrestore(&pring->ring_lock, iflags);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba)
|
lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba)
|
||||||
{
|
{
|
||||||
#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
|
#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
|
||||||
struct lpfc_nvmet_tgtport *tgtp;
|
struct lpfc_nvmet_tgtport *tgtp;
|
||||||
|
struct lpfc_queue *wq;
|
||||||
|
uint32_t qidx;
|
||||||
|
|
||||||
if (phba->nvmet_support == 0)
|
if (phba->nvmet_support == 0)
|
||||||
return;
|
return;
|
||||||
if (phba->targetport) {
|
if (phba->targetport) {
|
||||||
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
|
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
|
||||||
|
for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) {
|
||||||
|
wq = phba->sli4_hba.nvme_wq[qidx];
|
||||||
|
lpfc_nvmet_wqfull_flush(phba, wq, NULL);
|
||||||
|
}
|
||||||
init_completion(&tgtp->tport_unreg_done);
|
init_completion(&tgtp->tport_unreg_done);
|
||||||
nvmet_fc_unregister_targetport(phba->targetport);
|
nvmet_fc_unregister_targetport(phba->targetport);
|
||||||
wait_for_completion_timeout(&tgtp->tport_unreg_done, 5);
|
wait_for_completion_timeout(&tgtp->tport_unreg_done, 5);
|
||||||
|
@ -132,6 +132,7 @@ struct lpfc_nvmet_rcv_ctx {
|
|||||||
#define LPFC_NVMET_CTX_RLS 0x8 /* ctx free requested */
|
#define LPFC_NVMET_CTX_RLS 0x8 /* ctx free requested */
|
||||||
#define LPFC_NVMET_ABTS_RCV 0x10 /* ABTS received on exchange */
|
#define LPFC_NVMET_ABTS_RCV 0x10 /* ABTS received on exchange */
|
||||||
#define LPFC_NVMET_DEFER_RCV_REPOST 0x20 /* repost to RQ on defer rcv */
|
#define LPFC_NVMET_DEFER_RCV_REPOST 0x20 /* repost to RQ on defer rcv */
|
||||||
|
#define LPFC_NVMET_DEFER_WQFULL 0x40 /* Waiting on a free WQE */
|
||||||
struct rqb_dmabuf *rqb_buffer;
|
struct rqb_dmabuf *rqb_buffer;
|
||||||
struct lpfc_nvmet_ctxbuf *ctxbuf;
|
struct lpfc_nvmet_ctxbuf *ctxbuf;
|
||||||
|
|
||||||
|
@ -13232,6 +13232,8 @@ lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
|
|||||||
if (childwq->queue_id == hba_wqid) {
|
if (childwq->queue_id == hba_wqid) {
|
||||||
lpfc_sli4_wq_release(childwq,
|
lpfc_sli4_wq_release(childwq,
|
||||||
bf_get(lpfc_wcqe_r_wqe_index, wcqe));
|
bf_get(lpfc_wcqe_r_wqe_index, wcqe));
|
||||||
|
if (childwq->q_flag & HBA_NVMET_WQFULL)
|
||||||
|
lpfc_nvmet_wqfull_process(phba, childwq);
|
||||||
wqid_matched = true;
|
wqid_matched = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -13950,6 +13952,7 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size,
|
|||||||
|
|
||||||
INIT_LIST_HEAD(&queue->list);
|
INIT_LIST_HEAD(&queue->list);
|
||||||
INIT_LIST_HEAD(&queue->wq_list);
|
INIT_LIST_HEAD(&queue->wq_list);
|
||||||
|
INIT_LIST_HEAD(&queue->wqfull_list);
|
||||||
INIT_LIST_HEAD(&queue->page_list);
|
INIT_LIST_HEAD(&queue->page_list);
|
||||||
INIT_LIST_HEAD(&queue->child_list);
|
INIT_LIST_HEAD(&queue->child_list);
|
||||||
|
|
||||||
|
@ -145,6 +145,7 @@ struct lpfc_rqb {
|
|||||||
struct lpfc_queue {
|
struct lpfc_queue {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct list_head wq_list;
|
struct list_head wq_list;
|
||||||
|
struct list_head wqfull_list;
|
||||||
enum lpfc_sli4_queue_type type;
|
enum lpfc_sli4_queue_type type;
|
||||||
enum lpfc_sli4_queue_subtype subtype;
|
enum lpfc_sli4_queue_subtype subtype;
|
||||||
struct lpfc_hba *phba;
|
struct lpfc_hba *phba;
|
||||||
@ -173,9 +174,11 @@ struct lpfc_queue {
|
|||||||
#define LPFC_EXPANDED_PAGE_SIZE 16384
|
#define LPFC_EXPANDED_PAGE_SIZE 16384
|
||||||
#define LPFC_DEFAULT_PAGE_SIZE 4096
|
#define LPFC_DEFAULT_PAGE_SIZE 4096
|
||||||
uint16_t chann; /* IO channel this queue is associated with */
|
uint16_t chann; /* IO channel this queue is associated with */
|
||||||
uint16_t db_format;
|
uint8_t db_format;
|
||||||
#define LPFC_DB_RING_FORMAT 0x01
|
#define LPFC_DB_RING_FORMAT 0x01
|
||||||
#define LPFC_DB_LIST_FORMAT 0x02
|
#define LPFC_DB_LIST_FORMAT 0x02
|
||||||
|
uint8_t q_flag;
|
||||||
|
#define HBA_NVMET_WQFULL 0x1 /* We hit WQ Full condition for NVMET */
|
||||||
void __iomem *db_regaddr;
|
void __iomem *db_regaddr;
|
||||||
/* For q stats */
|
/* For q stats */
|
||||||
uint32_t q_cnt_1;
|
uint32_t q_cnt_1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user