Commit 66d7ce93 authored by Dick Kennedy's avatar Dick Kennedy Committed by Martin K. Petersen

scsi: lpfc: Fix MRQ > 1 context list handling

Various oops including cpu LOCKUPs were seen.

For asynchronously received ius where the driver must assign exchange
resources, the resources were on a single get (free) list and put list
(finished, waiting to be put on get list). As all cpus are sharing the
lists, an interrupt for a receive frame may have to wait for all the
other cpus to place their done work onto the put list before it can
acquire the lock to pull from the list.

Fix by breaking the resource lists into per-cpu lists or at least more
than 1 list with cpu's sharing the lists). A cpu would allocate from the
free list for its own cpu, and put its done work on the its own put list
- avoiding the contention. As cpu load may vary, when empty, a cpu may
grab from another cpu, thereby changing resource distribution.  But
searching for a resource only occurs on 1 or a few cpus until a single
resource can be allocated. if the condition reoccurs, it starts looking
at a different cpu.
Signed-off-by: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarJames Smart <james.smart@broadcom.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent e3e2863d
...@@ -245,13 +245,10 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, ...@@ -245,13 +245,10 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
atomic_read(&tgtp->xmt_abort_rsp), atomic_read(&tgtp->xmt_abort_rsp),
atomic_read(&tgtp->xmt_abort_rsp_error)); atomic_read(&tgtp->xmt_abort_rsp_error));
spin_lock(&phba->sli4_hba.nvmet_ctx_get_lock); /* Calculate outstanding IOs */
spin_lock(&phba->sli4_hba.nvmet_ctx_put_lock); tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
tot = phba->sli4_hba.nvmet_xri_cnt - tot += atomic_read(&tgtp->xmt_fcp_release);
(phba->sli4_hba.nvmet_ctx_get_cnt + tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot;
phba->sli4_hba.nvmet_ctx_put_cnt);
spin_unlock(&phba->sli4_hba.nvmet_ctx_put_lock);
spin_unlock(&phba->sli4_hba.nvmet_ctx_get_lock);
len += snprintf(buf + len, PAGE_SIZE - len, len += snprintf(buf + len, PAGE_SIZE - len,
"IO_CTX: %08x WAIT: cur %08x tot %08x\n" "IO_CTX: %08x WAIT: cur %08x tot %08x\n"
......
...@@ -556,9 +556,8 @@ int lpfc_nvmet_update_targetport(struct lpfc_hba *phba); ...@@ -556,9 +556,8 @@ int lpfc_nvmet_update_targetport(struct lpfc_hba *phba);
void lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba); void lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba);
void lpfc_nvmet_unsol_ls_event(struct lpfc_hba *phba, void lpfc_nvmet_unsol_ls_event(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, struct lpfc_iocbq *piocb); struct lpfc_sli_ring *pring, struct lpfc_iocbq *piocb);
void lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba, void lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba, uint32_t idx,
struct lpfc_sli_ring *pring, struct rqb_dmabuf *nvmebuf, uint64_t isr_ts);
struct rqb_dmabuf *nvmebuf, uint64_t isr_ts);
void lpfc_nvme_mod_param_dep(struct lpfc_hba *phba); void lpfc_nvme_mod_param_dep(struct lpfc_hba *phba);
void lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba, void lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocb, struct lpfc_iocbq *cmdiocb,
......
...@@ -848,13 +848,10 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) ...@@ -848,13 +848,10 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
} }
spin_lock(&phba->sli4_hba.nvmet_ctx_get_lock); /* Calculate outstanding IOs */
spin_lock(&phba->sli4_hba.nvmet_ctx_put_lock); tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
tot = phba->sli4_hba.nvmet_xri_cnt - tot += atomic_read(&tgtp->xmt_fcp_release);
(phba->sli4_hba.nvmet_ctx_get_cnt + tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot;
phba->sli4_hba.nvmet_ctx_put_cnt);
spin_unlock(&phba->sli4_hba.nvmet_ctx_put_lock);
spin_unlock(&phba->sli4_hba.nvmet_ctx_get_lock);
len += snprintf(buf + len, size - len, len += snprintf(buf + len, size - len,
"IO_CTX: %08x WAIT: cur %08x tot %08x\n" "IO_CTX: %08x WAIT: cur %08x tot %08x\n"
......
...@@ -1253,6 +1253,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) ...@@ -1253,6 +1253,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
unsigned long time_elapsed; unsigned long time_elapsed;
uint32_t tick_cqe, max_cqe, val; uint32_t tick_cqe, max_cqe, val;
uint64_t tot, data1, data2, data3; uint64_t tot, data1, data2, data3;
struct lpfc_nvmet_tgtport *tgtp;
struct lpfc_register reg_data; struct lpfc_register reg_data;
void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr; void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr;
...@@ -1281,13 +1282,11 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) ...@@ -1281,13 +1282,11 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
/* Check outstanding IO count */ /* Check outstanding IO count */
if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
if (phba->nvmet_support) { if (phba->nvmet_support) {
spin_lock(&phba->sli4_hba.nvmet_ctx_get_lock); tgtp = phba->targetport->private;
spin_lock(&phba->sli4_hba.nvmet_ctx_put_lock); /* Calculate outstanding IOs */
tot = phba->sli4_hba.nvmet_xri_cnt - tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
(phba->sli4_hba.nvmet_ctx_get_cnt + tot += atomic_read(&tgtp->xmt_fcp_release);
phba->sli4_hba.nvmet_ctx_put_cnt); tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot;
spin_unlock(&phba->sli4_hba.nvmet_ctx_put_lock);
spin_unlock(&phba->sli4_hba.nvmet_ctx_get_lock);
} else { } else {
tot = atomic_read(&phba->fc4NvmeIoCmpls); tot = atomic_read(&phba->fc4NvmeIoCmpls);
data1 = atomic_read( data1 = atomic_read(
...@@ -5937,8 +5936,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) ...@@ -5937,8 +5936,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
spin_lock_init(&phba->sli4_hba.abts_nvme_buf_list_lock); spin_lock_init(&phba->sli4_hba.abts_nvme_buf_list_lock);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvme_buf_list); INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvme_buf_list);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list); INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_ctx_get_list);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_ctx_put_list);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_io_wait_list); INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_io_wait_list);
/* Fast-path XRI aborted CQ Event work queue list */ /* Fast-path XRI aborted CQ Event work queue list */
...@@ -5947,8 +5944,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) ...@@ -5947,8 +5944,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
/* This abort list used by worker thread */ /* This abort list used by worker thread */
spin_lock_init(&phba->sli4_hba.sgl_list_lock); spin_lock_init(&phba->sli4_hba.sgl_list_lock);
spin_lock_init(&phba->sli4_hba.nvmet_ctx_get_lock);
spin_lock_init(&phba->sli4_hba.nvmet_ctx_put_lock);
spin_lock_init(&phba->sli4_hba.nvmet_io_wait_lock); spin_lock_init(&phba->sli4_hba.nvmet_io_wait_lock);
/* /*
......
...@@ -170,8 +170,10 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf) ...@@ -170,8 +170,10 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
struct lpfc_nvmet_tgtport *tgtp; struct lpfc_nvmet_tgtport *tgtp;
struct fc_frame_header *fc_hdr; struct fc_frame_header *fc_hdr;
struct rqb_dmabuf *nvmebuf; struct rqb_dmabuf *nvmebuf;
struct lpfc_nvmet_ctx_info *infop;
uint32_t *payload; uint32_t *payload;
uint32_t size, oxid, sid, rc; uint32_t size, oxid, sid, rc;
int cpu;
unsigned long iflag; unsigned long iflag;
if (ctxp->txrdy) { if (ctxp->txrdy) {
...@@ -267,11 +269,16 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf) ...@@ -267,11 +269,16 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
} }
spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock, iflag); spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
spin_lock_irqsave(&phba->sli4_hba.nvmet_ctx_put_lock, iflag); /*
list_add_tail(&ctx_buf->list, * Use the CPU context list, from the MRQ the IO was received on
&phba->sli4_hba.lpfc_nvmet_ctx_put_list); * (ctxp->idx), to save context structure.
phba->sli4_hba.nvmet_ctx_put_cnt++; */
spin_unlock_irqrestore(&phba->sli4_hba.nvmet_ctx_put_lock, iflag); cpu = smp_processor_id();
infop = lpfc_get_ctx_list(phba, cpu, ctxp->idx);
spin_lock_irqsave(&infop->nvmet_ctx_list_lock, iflag);
list_add_tail(&ctx_buf->list, &infop->nvmet_ctx_list);
infop->nvmet_ctx_list_cnt++;
spin_unlock_irqrestore(&infop->nvmet_ctx_list_lock, iflag);
#endif #endif
} }
...@@ -860,51 +867,54 @@ static struct nvmet_fc_target_template lpfc_tgttemplate = { ...@@ -860,51 +867,54 @@ static struct nvmet_fc_target_template lpfc_tgttemplate = {
}; };
static void static void
lpfc_nvmet_cleanup_io_context(struct lpfc_hba *phba) __lpfc_nvmet_clean_io_for_cpu(struct lpfc_hba *phba,
struct lpfc_nvmet_ctx_info *infop)
{ {
struct lpfc_nvmet_ctxbuf *ctx_buf, *next_ctx_buf; struct lpfc_nvmet_ctxbuf *ctx_buf, *next_ctx_buf;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&phba->sli4_hba.nvmet_ctx_get_lock, flags); spin_lock_irqsave(&infop->nvmet_ctx_list_lock, flags);
spin_lock(&phba->sli4_hba.nvmet_ctx_put_lock);
list_for_each_entry_safe(ctx_buf, next_ctx_buf, list_for_each_entry_safe(ctx_buf, next_ctx_buf,
&phba->sli4_hba.lpfc_nvmet_ctx_get_list, list) { &infop->nvmet_ctx_list, list) {
spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
list_del_init(&ctx_buf->list); list_del_init(&ctx_buf->list);
spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
__lpfc_clear_active_sglq(phba,
ctx_buf->sglq->sli4_lxritag); __lpfc_clear_active_sglq(phba, ctx_buf->sglq->sli4_lxritag);
ctx_buf->sglq->state = SGL_FREED; ctx_buf->sglq->state = SGL_FREED;
ctx_buf->sglq->ndlp = NULL; ctx_buf->sglq->ndlp = NULL;
spin_lock(&phba->sli4_hba.sgl_list_lock); spin_lock(&phba->sli4_hba.sgl_list_lock);
list_add_tail(&ctx_buf->sglq->list, list_add_tail(&ctx_buf->sglq->list,
&phba->sli4_hba.lpfc_nvmet_sgl_list); &phba->sli4_hba.lpfc_nvmet_sgl_list);
spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock(&phba->sli4_hba.sgl_list_lock);
lpfc_sli_release_iocbq(phba, ctx_buf->iocbq); lpfc_sli_release_iocbq(phba, ctx_buf->iocbq);
kfree(ctx_buf->context); kfree(ctx_buf->context);
} }
list_for_each_entry_safe(ctx_buf, next_ctx_buf, spin_unlock_irqrestore(&infop->nvmet_ctx_list_lock, flags);
&phba->sli4_hba.lpfc_nvmet_ctx_put_list, list) { }
spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
list_del_init(&ctx_buf->list);
spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
__lpfc_clear_active_sglq(phba,
ctx_buf->sglq->sli4_lxritag);
ctx_buf->sglq->state = SGL_FREED;
ctx_buf->sglq->ndlp = NULL;
spin_lock(&phba->sli4_hba.sgl_list_lock); static void
list_add_tail(&ctx_buf->sglq->list, lpfc_nvmet_cleanup_io_context(struct lpfc_hba *phba)
&phba->sli4_hba.lpfc_nvmet_sgl_list); {
spin_unlock(&phba->sli4_hba.sgl_list_lock); struct lpfc_nvmet_ctx_info *infop;
int i, j;
lpfc_sli_release_iocbq(phba, ctx_buf->iocbq); /* The first context list, MRQ 0 CPU 0 */
kfree(ctx_buf->context); infop = phba->sli4_hba.nvmet_ctx_info;
if (!infop)
return;
/* Cycle the the entire CPU context list for every MRQ */
for (i = 0; i < phba->cfg_nvmet_mrq; i++) {
for (j = 0; j < phba->sli4_hba.num_present_cpu; j++) {
__lpfc_nvmet_clean_io_for_cpu(phba, infop);
infop++; /* next */
}
} }
spin_unlock(&phba->sli4_hba.nvmet_ctx_put_lock); kfree(phba->sli4_hba.nvmet_ctx_info);
spin_unlock_irqrestore(&phba->sli4_hba.nvmet_ctx_get_lock, flags); phba->sli4_hba.nvmet_ctx_info = NULL;
} }
static int static int
...@@ -913,15 +923,71 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) ...@@ -913,15 +923,71 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
struct lpfc_nvmet_ctxbuf *ctx_buf; struct lpfc_nvmet_ctxbuf *ctx_buf;
struct lpfc_iocbq *nvmewqe; struct lpfc_iocbq *nvmewqe;
union lpfc_wqe128 *wqe; union lpfc_wqe128 *wqe;
int i; struct lpfc_nvmet_ctx_info *last_infop;
struct lpfc_nvmet_ctx_info *infop;
int i, j, idx;
lpfc_printf_log(phba, KERN_INFO, LOG_NVME, lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
"6403 Allocate NVMET resources for %d XRIs\n", "6403 Allocate NVMET resources for %d XRIs\n",
phba->sli4_hba.nvmet_xri_cnt); phba->sli4_hba.nvmet_xri_cnt);
phba->sli4_hba.nvmet_ctx_info = kcalloc(
phba->sli4_hba.num_present_cpu * phba->cfg_nvmet_mrq,
sizeof(struct lpfc_nvmet_ctx_info), GFP_KERNEL);
if (!phba->sli4_hba.nvmet_ctx_info) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"6419 Failed allocate memory for "
"nvmet context lists\n");
return -ENOMEM;
}
/*
* Assuming X CPUs in the system, and Y MRQs, allocate some
* lpfc_nvmet_ctx_info structures as follows:
*
* cpu0/mrq0 cpu1/mrq0 ... cpuX/mrq0
* cpu0/mrq1 cpu1/mrq1 ... cpuX/mrq1
* ...
* cpuX/mrqY cpuX/mrqY ... cpuX/mrqY
*
* Each line represents a MRQ "silo" containing an entry for
* every CPU.
*
* MRQ X is initially assumed to be associated with CPU X, thus
* contexts are initially distributed across all MRQs using
* the MRQ index (N) as follows cpuN/mrqN. When contexts are
* freed, the are freed to the MRQ silo based on the CPU number
* of the IO completion. Thus a context that was allocated for MRQ A
* whose IO completed on CPU B will be freed to cpuB/mrqA.
*/
infop = phba->sli4_hba.nvmet_ctx_info;
for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
INIT_LIST_HEAD(&infop->nvmet_ctx_list);
spin_lock_init(&infop->nvmet_ctx_list_lock);
infop->nvmet_ctx_list_cnt = 0;
infop++;
}
}
/*
* Setup the next CPU context info ptr for each MRQ.
* MRQ 0 will cycle thru CPUs 0 - X separately from
* MRQ 1 cycling thru CPUs 0 - X, and so on.
*/
for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
last_infop = lpfc_get_ctx_list(phba, 0, j);
for (i = phba->sli4_hba.num_present_cpu - 1; i >= 0; i--) {
infop = lpfc_get_ctx_list(phba, i, j);
infop->nvmet_ctx_next_cpu = last_infop;
last_infop = infop;
}
}
/* For all nvmet xris, allocate resources needed to process a /* For all nvmet xris, allocate resources needed to process a
* received command on a per xri basis. * received command on a per xri basis.
*/ */
idx = 0;
for (i = 0; i < phba->sli4_hba.nvmet_xri_cnt; i++) { for (i = 0; i < phba->sli4_hba.nvmet_xri_cnt; i++) {
ctx_buf = kzalloc(sizeof(*ctx_buf), GFP_KERNEL); ctx_buf = kzalloc(sizeof(*ctx_buf), GFP_KERNEL);
if (!ctx_buf) { if (!ctx_buf) {
...@@ -976,12 +1042,35 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) ...@@ -976,12 +1042,35 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
"6407 Ran out of NVMET XRIs\n"); "6407 Ran out of NVMET XRIs\n");
return -ENOMEM; return -ENOMEM;
} }
spin_lock(&phba->sli4_hba.nvmet_ctx_get_lock);
list_add_tail(&ctx_buf->list, /*
&phba->sli4_hba.lpfc_nvmet_ctx_get_list); * Add ctx to MRQidx context list. Our initial assumption
spin_unlock(&phba->sli4_hba.nvmet_ctx_get_lock); * is MRQidx will be associated with CPUidx. This association
* can change on the fly.
*/
infop = lpfc_get_ctx_list(phba, idx, idx);
spin_lock(&infop->nvmet_ctx_list_lock);
list_add_tail(&ctx_buf->list, &infop->nvmet_ctx_list);
infop->nvmet_ctx_list_cnt++;
spin_unlock(&infop->nvmet_ctx_list_lock);
/* Spread ctx structures evenly across all MRQs */
idx++;
if (idx >= phba->cfg_nvmet_mrq)
idx = 0;
}
infop = phba->sli4_hba.nvmet_ctx_info;
for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
lpfc_printf_log(phba, KERN_INFO, LOG_NVME | LOG_INIT,
"6408 TOTAL NVMET ctx for CPU %d "
"MRQ %d: cnt %d nextcpu %p\n",
i, j, infop->nvmet_ctx_list_cnt,
infop->nvmet_ctx_next_cpu);
infop++;
}
} }
phba->sli4_hba.nvmet_ctx_get_cnt = phba->sli4_hba.nvmet_xri_cnt;
return 0; return 0;
} }
...@@ -1346,10 +1435,65 @@ lpfc_nvmet_unsol_ls_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ...@@ -1346,10 +1435,65 @@ lpfc_nvmet_unsol_ls_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
#endif #endif
} }
static struct lpfc_nvmet_ctxbuf *
lpfc_nvmet_replenish_context(struct lpfc_hba *phba,
struct lpfc_nvmet_ctx_info *current_infop)
{
struct lpfc_nvmet_ctxbuf *ctx_buf = NULL;
struct lpfc_nvmet_ctx_info *get_infop;
int i;
/*
* The current_infop for the MRQ a NVME command IU was received
* on is empty. Our goal is to replenish this MRQs context
* list from a another CPUs.
*
* First we need to pick a context list to start looking on.
* nvmet_ctx_start_cpu has available context the last time
* we needed to replenish this CPU where nvmet_ctx_next_cpu
* is just the next sequential CPU for this MRQ.
*/
if (current_infop->nvmet_ctx_start_cpu)
get_infop = current_infop->nvmet_ctx_start_cpu;
else
get_infop = current_infop->nvmet_ctx_next_cpu;
for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
if (get_infop == current_infop) {
get_infop = get_infop->nvmet_ctx_next_cpu;
continue;
}
spin_lock(&get_infop->nvmet_ctx_list_lock);
/* Just take the entire context list, if there are any */
if (get_infop->nvmet_ctx_list_cnt) {
list_splice_init(&get_infop->nvmet_ctx_list,
&current_infop->nvmet_ctx_list);
current_infop->nvmet_ctx_list_cnt =
get_infop->nvmet_ctx_list_cnt - 1;
get_infop->nvmet_ctx_list_cnt = 0;
spin_unlock(&get_infop->nvmet_ctx_list_lock);
current_infop->nvmet_ctx_start_cpu = get_infop;
list_remove_head(&current_infop->nvmet_ctx_list,
ctx_buf, struct lpfc_nvmet_ctxbuf,
list);
return ctx_buf;
}
/* Otherwise, move on to the next CPU for this MRQ */
spin_unlock(&get_infop->nvmet_ctx_list_lock);
get_infop = get_infop->nvmet_ctx_next_cpu;
}
/* Nothing found, all contexts for the MRQ are in-flight */
return NULL;
}
/** /**
* lpfc_nvmet_unsol_fcp_buffer - Process an unsolicited event data buffer * lpfc_nvmet_unsol_fcp_buffer - Process an unsolicited event data buffer
* @phba: pointer to lpfc hba data structure. * @phba: pointer to lpfc hba data structure.
* @pring: pointer to a SLI ring. * @idx: relative index of MRQ vector
* @nvmebuf: pointer to lpfc nvme command HBQ data structure. * @nvmebuf: pointer to lpfc nvme command HBQ data structure.
* *
* This routine is used for processing the WQE associated with a unsolicited * This routine is used for processing the WQE associated with a unsolicited
...@@ -1361,7 +1505,7 @@ lpfc_nvmet_unsol_ls_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ...@@ -1361,7 +1505,7 @@ lpfc_nvmet_unsol_ls_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
**/ **/
static void static void
lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint32_t idx,
struct rqb_dmabuf *nvmebuf, struct rqb_dmabuf *nvmebuf,
uint64_t isr_timestamp) uint64_t isr_timestamp)
{ {
...@@ -1370,9 +1514,11 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, ...@@ -1370,9 +1514,11 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
struct lpfc_nvmet_tgtport *tgtp; struct lpfc_nvmet_tgtport *tgtp;
struct fc_frame_header *fc_hdr; struct fc_frame_header *fc_hdr;
struct lpfc_nvmet_ctxbuf *ctx_buf; struct lpfc_nvmet_ctxbuf *ctx_buf;
struct lpfc_nvmet_ctx_info *current_infop;
uint32_t *payload; uint32_t *payload;
uint32_t size, oxid, sid, rc, qno; uint32_t size, oxid, sid, rc, qno;
unsigned long iflag; unsigned long iflag;
int current_cpu;
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
uint32_t id; uint32_t id;
#endif #endif
...@@ -1388,31 +1534,24 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, ...@@ -1388,31 +1534,24 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
goto dropit; goto dropit;
} }
spin_lock_irqsave(&phba->sli4_hba.nvmet_ctx_get_lock, iflag); /*
if (phba->sli4_hba.nvmet_ctx_get_cnt) { * Get a pointer to the context list for this MRQ based on
list_remove_head(&phba->sli4_hba.lpfc_nvmet_ctx_get_list, * the CPU this MRQ IRQ is associated with. If the CPU association
* changes from our initial assumption, the context list could
* be empty, thus it would need to be replenished with the
* context list from another CPU for this MRQ.
*/
current_cpu = smp_processor_id();
current_infop = lpfc_get_ctx_list(phba, current_cpu, idx);
spin_lock_irqsave(&current_infop->nvmet_ctx_list_lock, iflag);
if (current_infop->nvmet_ctx_list_cnt) {
list_remove_head(&current_infop->nvmet_ctx_list,
ctx_buf, struct lpfc_nvmet_ctxbuf, list); ctx_buf, struct lpfc_nvmet_ctxbuf, list);
phba->sli4_hba.nvmet_ctx_get_cnt--; current_infop->nvmet_ctx_list_cnt--;
} else { } else {
spin_lock(&phba->sli4_hba.nvmet_ctx_put_lock); ctx_buf = lpfc_nvmet_replenish_context(phba, current_infop);
if (phba->sli4_hba.nvmet_ctx_put_cnt) {
list_splice(&phba->sli4_hba.lpfc_nvmet_ctx_put_list,
&phba->sli4_hba.lpfc_nvmet_ctx_get_list);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_ctx_put_list);
phba->sli4_hba.nvmet_ctx_get_cnt =
phba->sli4_hba.nvmet_ctx_put_cnt;
phba->sli4_hba.nvmet_ctx_put_cnt = 0;
spin_unlock(&phba->sli4_hba.nvmet_ctx_put_lock);
list_remove_head(
&phba->sli4_hba.lpfc_nvmet_ctx_get_list,
ctx_buf, struct lpfc_nvmet_ctxbuf, list);
phba->sli4_hba.nvmet_ctx_get_cnt--;
} else {
spin_unlock(&phba->sli4_hba.nvmet_ctx_put_lock);
}
} }
spin_unlock_irqrestore(&phba->sli4_hba.nvmet_ctx_get_lock, iflag); spin_unlock_irqrestore(&current_infop->nvmet_ctx_list_lock, iflag);
fc_hdr = (struct fc_frame_header *)(nvmebuf->hbuf.virt); fc_hdr = (struct fc_frame_header *)(nvmebuf->hbuf.virt);
oxid = be16_to_cpu(fc_hdr->fh_ox_id); oxid = be16_to_cpu(fc_hdr->fh_ox_id);
...@@ -1464,6 +1603,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, ...@@ -1464,6 +1603,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
ctxp->size = size; ctxp->size = size;
ctxp->oxid = oxid; ctxp->oxid = oxid;
ctxp->sid = sid; ctxp->sid = sid;
ctxp->idx = idx;
ctxp->state = LPFC_NVMET_STE_RCV; ctxp->state = LPFC_NVMET_STE_RCV;
ctxp->entry_cnt = 1; ctxp->entry_cnt = 1;
ctxp->flag = 0; ctxp->flag = 0;
...@@ -1561,7 +1701,7 @@ lpfc_nvmet_unsol_ls_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ...@@ -1561,7 +1701,7 @@ lpfc_nvmet_unsol_ls_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/** /**
* lpfc_nvmet_unsol_fcp_event - Process an unsolicited event from an nvme nport * lpfc_nvmet_unsol_fcp_event - Process an unsolicited event from an nvme nport
* @phba: pointer to lpfc hba data structure. * @phba: pointer to lpfc hba data structure.
* @pring: pointer to a SLI ring. * @idx: relative index of MRQ vector
* @nvmebuf: pointer to received nvme data structure. * @nvmebuf: pointer to received nvme data structure.
* *
* This routine is used to process an unsolicited event received from a SLI * This routine is used to process an unsolicited event received from a SLI
...@@ -1572,7 +1712,7 @@ lpfc_nvmet_unsol_ls_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ...@@ -1572,7 +1712,7 @@ lpfc_nvmet_unsol_ls_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
**/ **/
void void
lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba, lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint32_t idx,
struct rqb_dmabuf *nvmebuf, struct rqb_dmabuf *nvmebuf,
uint64_t isr_timestamp) uint64_t isr_timestamp)
{ {
...@@ -1580,7 +1720,7 @@ lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba, ...@@ -1580,7 +1720,7 @@ lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba,
lpfc_rq_buf_free(phba, &nvmebuf->hbuf); lpfc_rq_buf_free(phba, &nvmebuf->hbuf);
return; return;
} }
lpfc_nvmet_unsol_fcp_buffer(phba, pring, nvmebuf, lpfc_nvmet_unsol_fcp_buffer(phba, idx, nvmebuf,
isr_timestamp); isr_timestamp);
} }
......
...@@ -73,6 +73,19 @@ struct lpfc_nvmet_tgtport { ...@@ -73,6 +73,19 @@ struct lpfc_nvmet_tgtport {
atomic_t xmt_abort_rsp_error; atomic_t xmt_abort_rsp_error;
}; };
struct lpfc_nvmet_ctx_info {
struct list_head nvmet_ctx_list;
spinlock_t nvmet_ctx_list_lock; /* lock per CPU */
struct lpfc_nvmet_ctx_info *nvmet_ctx_next_cpu;
struct lpfc_nvmet_ctx_info *nvmet_ctx_start_cpu;
uint16_t nvmet_ctx_list_cnt;
char pad[16]; /* pad to a cache-line */
};
/* This retrieves the context info associated with the specified cpu / mrq */
#define lpfc_get_ctx_list(phba, cpu, mrq) \
(phba->sli4_hba.nvmet_ctx_info + ((cpu * phba->cfg_nvmet_mrq) + mrq))
struct lpfc_nvmet_rcv_ctx { struct lpfc_nvmet_rcv_ctx {
union { union {
struct nvmefc_tgt_ls_req ls_req; struct nvmefc_tgt_ls_req ls_req;
...@@ -91,6 +104,7 @@ struct lpfc_nvmet_rcv_ctx { ...@@ -91,6 +104,7 @@ struct lpfc_nvmet_rcv_ctx {
uint16_t size; uint16_t size;
uint16_t entry_cnt; uint16_t entry_cnt;
uint16_t cpu; uint16_t cpu;
uint16_t idx;
uint16_t state; uint16_t state;
/* States */ /* States */
#define LPFC_NVMET_STE_LS_RCV 1 #define LPFC_NVMET_STE_LS_RCV 1
......
...@@ -13291,7 +13291,7 @@ lpfc_sli4_nvmet_handle_rcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, ...@@ -13291,7 +13291,7 @@ lpfc_sli4_nvmet_handle_rcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
if (fc_hdr->fh_type == FC_TYPE_FCP) { if (fc_hdr->fh_type == FC_TYPE_FCP) {
dma_buf->bytes_recv = bf_get(lpfc_rcqe_length, rcqe); dma_buf->bytes_recv = bf_get(lpfc_rcqe_length, rcqe);
lpfc_nvmet_unsol_fcp_event( lpfc_nvmet_unsol_fcp_event(
phba, phba->sli4_hba.els_wq->pring, dma_buf, phba, idx, dma_buf,
cq->assoc_qp->isr_timestamp); cq->assoc_qp->isr_timestamp);
return false; return false;
} }
......
...@@ -622,8 +622,6 @@ struct lpfc_sli4_hba { ...@@ -622,8 +622,6 @@ struct lpfc_sli4_hba {
uint16_t scsi_xri_start; uint16_t scsi_xri_start;
uint16_t els_xri_cnt; uint16_t els_xri_cnt;
uint16_t nvmet_xri_cnt; uint16_t nvmet_xri_cnt;
uint16_t nvmet_ctx_get_cnt;
uint16_t nvmet_ctx_put_cnt;
uint16_t nvmet_io_wait_cnt; uint16_t nvmet_io_wait_cnt;
uint16_t nvmet_io_wait_total; uint16_t nvmet_io_wait_total;
struct list_head lpfc_els_sgl_list; struct list_head lpfc_els_sgl_list;
...@@ -632,9 +630,8 @@ struct lpfc_sli4_hba { ...@@ -632,9 +630,8 @@ struct lpfc_sli4_hba {
struct list_head lpfc_abts_nvmet_ctx_list; struct list_head lpfc_abts_nvmet_ctx_list;
struct list_head lpfc_abts_scsi_buf_list; struct list_head lpfc_abts_scsi_buf_list;
struct list_head lpfc_abts_nvme_buf_list; struct list_head lpfc_abts_nvme_buf_list;
struct list_head lpfc_nvmet_ctx_get_list;
struct list_head lpfc_nvmet_ctx_put_list;
struct list_head lpfc_nvmet_io_wait_list; struct list_head lpfc_nvmet_io_wait_list;
struct lpfc_nvmet_ctx_info *nvmet_ctx_info;
struct lpfc_sglq **lpfc_sglq_active_list; struct lpfc_sglq **lpfc_sglq_active_list;
struct list_head lpfc_rpi_hdr_list; struct list_head lpfc_rpi_hdr_list;
unsigned long *rpi_bmask; unsigned long *rpi_bmask;
...@@ -665,8 +662,6 @@ struct lpfc_sli4_hba { ...@@ -665,8 +662,6 @@ struct lpfc_sli4_hba {
spinlock_t abts_nvme_buf_list_lock; /* list of aborted SCSI IOs */ spinlock_t abts_nvme_buf_list_lock; /* list of aborted SCSI IOs */
spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */ spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
spinlock_t sgl_list_lock; /* list of aborted els IOs */ spinlock_t sgl_list_lock; /* list of aborted els IOs */
spinlock_t nvmet_ctx_get_lock; /* list of avail XRI contexts */
spinlock_t nvmet_ctx_put_lock; /* list of avail XRI contexts */
spinlock_t nvmet_io_wait_lock; /* IOs waiting for ctx resources */ spinlock_t nvmet_io_wait_lock; /* IOs waiting for ctx resources */
uint32_t physical_port; uint32_t physical_port;
......
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