Commit 6e8e1c14 authored by James Smart's avatar James Smart Committed by Martin K. Petersen

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: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarJames Smart <james.smart@broadcom.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 8ae33701
...@@ -254,6 +254,7 @@ void lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, ...@@ -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 *, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 { ...@@ -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, ...@@ -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, ...@@ -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 { ...@@ -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 { ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment