/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * * Public License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful. * * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * * TO BE LEGALLY INVALID. See the GNU General Public License for * * more details, a copy of which can be found in the file COPYING * * included with this package. *
********************************************************************/ #include <linux/pci.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/unaligned.h> #include <linux/crc-t10dif.h> #include <net/checksum.h>
/** * __lpfc_nvme_xmt_ls_rsp_cmp - Generic completion handler for the * transmission of an NVME LS response. * @phba: Pointer to HBA context object. * @cmdwqe: Pointer to driver command WQE object. * @rspwqe: Pointer to driver response WQE object. * * The function is called from SLI ring event handler with no * lock held. The function frees memory resources used for the command * used to send the NVME LS RSP.
**/ void
__lpfc_nvme_xmt_ls_rsp_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe)
{ struct lpfc_async_xchg_ctx *axchg = cmdwqe->context_un.axchg; struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl; struct nvmefc_ls_rsp *ls_rsp = &axchg->ls_rsp;
uint32_t status, result;
status = bf_get(lpfc_wcqe_c_status, wcqe);
result = wcqe->parameter;
if (axchg->state != LPFC_NVME_STE_LS_RSP || axchg->entry_cnt != 2) {
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6410 NVMEx LS cmpl state mismatch IO x%x: " "%d %d\n",
axchg->oxid, axchg->state, axchg->entry_cnt);
}
lpfc_nvmeio_data(phba, "NVMEx LS CMPL: xri x%x stat x%x result x%x\n",
axchg->oxid, status, result);
/** * lpfc_nvmet_xmt_ls_rsp_cmp - Completion handler for LS Response * @phba: Pointer to HBA context object. * @cmdwqe: Pointer to driver command WQE object. * @rspwqe: Pointer to driver response WQE object. * * The function is called from SLI ring event handler with no * lock held. This function is the completion handler for NVME LS commands * The function updates any states and statistics, then calls the * generic completion handler to free resources.
**/ staticvoid
lpfc_nvmet_xmt_ls_rsp_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe)
{ struct lpfc_nvmet_tgtport *tgtp;
uint32_t status, result; struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
if (!phba->targetport) goto finish;
status = bf_get(lpfc_wcqe_c_status, wcqe);
result = wcqe->parameter;
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; if (tgtp) { if (status) {
atomic_inc(&tgtp->xmt_ls_rsp_error); if (result == IOERR_ABORT_REQUESTED)
atomic_inc(&tgtp->xmt_ls_rsp_aborted); if (bf_get(lpfc_wcqe_c_xb, wcqe))
atomic_inc(&tgtp->xmt_ls_rsp_xb_set);
} else {
atomic_inc(&tgtp->xmt_ls_rsp_cmpl);
}
}
/** * lpfc_nvmet_ctxbuf_post - Repost a NVMET RQ DMA buffer and clean up context * @phba: HBA buffer is associated with * @ctx_buf: ctx buffer context * * Description: Frees the given DMA buffer in the appropriate way given by * reposting it to its associated RQ so it can be reused. * * Notes: Takes phba->hbalock. Can be called with or without other locks held. * * Returns: None
**/ void
lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
{ #if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) struct lpfc_async_xchg_ctx *ctxp = ctx_buf->context; struct lpfc_nvmet_tgtport *tgtp; struct fc_frame_header *fc_hdr; struct rqb_dmabuf *nvmebuf; struct lpfc_nvmet_ctx_info *infop;
uint32_t size, oxid, sid; int cpu; unsignedlong iflag;
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS /* NOTE: isr time stamp is stale when context is re-assigned*/ if (ctxp->ts_isr_cmd) {
ctxp->ts_cmd_nvme = 0;
ctxp->ts_nvme_data = 0;
ctxp->ts_data_wqput = 0;
ctxp->ts_isr_data = 0;
ctxp->ts_data_nvme = 0;
ctxp->ts_nvme_status = 0;
ctxp->ts_status_wqput = 0;
ctxp->ts_isr_status = 0;
ctxp->ts_status_nvme = 0;
} #endif
atomic_inc(&tgtp->rcv_fcp_cmd_in);
/* Indicate that a replacement buffer has been posted */
spin_lock_irqsave(&ctxp->ctxlock, iflag);
ctxp->flag |= LPFC_NVME_CTX_REUSE_WQ;
spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
if (!queue_work(phba->wq, &ctx_buf->defer_work)) {
atomic_inc(&tgtp->rcv_fcp_cmd_drop);
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6181 Unable to queue deferred work " "for oxid x%x. " "FCP Drop IO [x%x x%x x%x]\n",
ctxp->oxid,
atomic_read(&tgtp->rcv_fcp_cmd_in),
atomic_read(&tgtp->rcv_fcp_cmd_out),
atomic_read(&tgtp->xmt_fcp_release));
/* * Use the CPU context list, from the MRQ the IO was received on * (ctxp->idx), to save context structure.
*/
spin_lock_irqsave(&phba->sli4_hba.t_active_list_lock, iflag);
list_del_init(&ctxp->list);
spin_unlock_irqrestore(&phba->sli4_hba.t_active_list_lock, iflag);
cpu = raw_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
}
if (ctxp->ts_status_nvme < ctxp->ts_isr_cmd) return; if (ctxp->ts_isr_cmd > ctxp->ts_cmd_nvme) return; if (ctxp->ts_cmd_nvme > ctxp->ts_nvme_data) return; if (ctxp->ts_nvme_data > ctxp->ts_data_wqput) return; if (ctxp->ts_data_wqput > ctxp->ts_isr_data) return; if (ctxp->ts_isr_data > ctxp->ts_data_nvme) return; if (ctxp->ts_data_nvme > ctxp->ts_nvme_status) return; if (ctxp->ts_nvme_status > ctxp->ts_status_wqput) return; if (ctxp->ts_status_wqput > ctxp->ts_isr_status) return; if (ctxp->ts_isr_status > ctxp->ts_status_nvme) return; /* * Segment 1 - Time from FCP command received by MSI-X ISR * to FCP command is passed to NVME Layer. * Segment 2 - Time from FCP command payload handed * off to NVME Layer to Driver receives a Command op * from NVME Layer. * Segment 3 - Time from Driver receives a Command op * from NVME Layer to Command is put on WQ. * Segment 4 - Time from Driver WQ put is done * to MSI-X ISR for Command cmpl. * Segment 5 - Time from MSI-X ISR for Command cmpl to * Command cmpl is passed to NVME Layer. * Segment 6 - Time from Command cmpl is passed to NVME * Layer to Driver receives a RSP op from NVME Layer. * Segment 7 - Time from Driver receives a RSP op from * NVME Layer to WQ put is done on TRSP FCP Status. * Segment 8 - Time from Driver WQ put is done on TRSP * FCP Status to MSI-X ISR for TRSP cmpl. * Segment 9 - Time from MSI-X ISR for TRSP cmpl to * TRSP cmpl is passed to NVME Layer. * Segment 10 - Time from FCP command received by * MSI-X ISR to command is completed on wire. * (Segments 1 thru 8) for READDATA / WRITEDATA * (Segments 1 thru 4) for READDATA_RSP
*/
seg1 = ctxp->ts_cmd_nvme - ctxp->ts_isr_cmd;
segsum = seg1;
/** * lpfc_nvmet_xmt_fcp_op_cmp - Completion handler for FCP Response * @phba: Pointer to HBA context object. * @cmdwqe: Pointer to driver command WQE object. * @rspwqe: Pointer to driver response WQE object. * * The function is called from SLI ring event handler with no * lock held. This function is the completion handler for NVME FCP commands * The function frees memory resources used for the NVME commands.
**/ staticvoid
lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe)
{ struct lpfc_nvmet_tgtport *tgtp; struct nvmefc_tgt_fcp_req *rsp; struct lpfc_async_xchg_ctx *ctxp;
uint32_t status, result, op, logerr; struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS int id; #endif
/** * __lpfc_nvme_xmt_ls_rsp - Generic service routine to issue transmit * an NVME LS rsp for a prior NVME LS request that was received. * @axchg: pointer to exchange context for the NVME LS request the response * is for. * @ls_rsp: pointer to the transport LS RSP that is to be sent * @xmt_ls_rsp_cmp: completion routine to call upon RSP transmit done * * This routine is used to format and send a WQE to transmit a NVME LS * Response. The response is for a prior NVME LS request that was * received and posted to the transport. * * Returns: * 0 : if response successfully transmit * non-zero : if response failed to transmit, of the form -Exxx.
**/ int
__lpfc_nvme_xmt_ls_rsp(struct lpfc_async_xchg_ctx *axchg, struct nvmefc_ls_rsp *ls_rsp, void (*xmt_ls_rsp_cmp)(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe))
{ struct lpfc_hba *phba = axchg->phba; struct hbq_dmabuf *nvmebuf = (struct hbq_dmabuf *)axchg->rqb_buffer; struct lpfc_iocbq *nvmewqeq; struct lpfc_dmabuf dmabuf; struct ulp_bde64 bpl; int rc;
if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) return -ENODEV;
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC, "6023 NVMEx LS rsp oxid x%x\n", axchg->oxid);
nvmewqeq = lpfc_nvmet_prep_ls_wqe(phba, axchg, ls_rsp->rspdma,
ls_rsp->rsplen); if (nvmewqeq == NULL) {
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6150 NVMEx LS Drop Rsp x%x: Prep\n",
axchg->oxid);
rc = -ENOMEM; goto out_free_buf;
}
/* Save numBdes for bpl2sgl */
nvmewqeq->num_bdes = 1;
nvmewqeq->hba_wqidx = 0;
nvmewqeq->bpl_dmabuf = &dmabuf;
dmabuf.virt = &bpl;
bpl.addrLow = nvmewqeq->wqe.xmit_sequence.bde.addrLow;
bpl.addrHigh = nvmewqeq->wqe.xmit_sequence.bde.addrHigh;
bpl.tus.f.bdeSize = ls_rsp->rsplen;
bpl.tus.f.bdeFlags = 0;
bpl.tus.w = le32_to_cpu(bpl.tus.w); /* * Note: although we're using stack space for the dmabuf, the * call to lpfc_sli4_issue_wqe is synchronous, so it will not * be referenced after it returns back to this routine.
*/
/* clear to be sure there's no reference */
nvmewqeq->bpl_dmabuf = NULL;
if (rc == WQE_SUCCESS) { /* * Okay to repost buffer here, but wait till cmpl * before freeing ctxp and iocbq.
*/
lpfc_in_buf_free(phba, &nvmebuf->dbuf); return 0;
}
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6151 NVMEx LS RSP x%x: failed to transmit %d\n",
axchg->oxid, rc);
rc = -ENXIO;
lpfc_nlp_put(nvmewqeq->ndlp);
out_free_buf: /* Give back resources */
lpfc_in_buf_free(phba, &nvmebuf->dbuf);
/* * As transport doesn't track completions of responses, if the rsp * fails to send, the transport will effectively ignore the rsp * and consider the LS done. However, the driver has an active * exchange open for the LS - so be sure to abort the exchange * if the response isn't sent.
*/
lpfc_nvme_unsol_ls_issue_abort(phba, axchg, axchg->sid, axchg->oxid); return rc;
}
/** * lpfc_nvmet_xmt_ls_rsp - Transmit NVME LS response * @tgtport: pointer to target port that NVME LS is to be transmit from. * @ls_rsp: pointer to the transport LS RSP that is to be sent * * Driver registers this routine to transmit responses for received NVME * LS requests. * * This routine is used to format and send a WQE to transmit a NVME LS * Response. The ls_rsp is used to reverse-map the LS to the original * NVME LS request sequence, which provides addressing information for * the remote port the LS to be sent to, as well as the exchange id * that is the LS is bound to. * * Returns: * 0 : if response successfully transmit * non-zero : if response failed to transmit, of the form -Exxx.
**/ staticint
lpfc_nvmet_xmt_ls_rsp(struct nvmet_fc_target_port *tgtport, struct nvmefc_ls_rsp *ls_rsp)
{ struct lpfc_async_xchg_ctx *axchg =
container_of(ls_rsp, struct lpfc_async_xchg_ctx, ls_rsp); struct lpfc_nvmet_tgtport *nvmep = tgtport->private; int rc;
if (test_bit(FC_UNLOADING, &axchg->phba->pport->load_flag)) return -ENODEV;
if (rc) {
atomic_inc(&nvmep->xmt_ls_drop); /* * unless the failure is due to having already sent * the response, an abort will be generated for the * exchange if the rsp can't be sent.
*/ if (rc != -EALREADY)
atomic_inc(&nvmep->xmt_ls_abort); return rc;
}
/* Since iaab/iaar are NOT set, we need to check * if the firmware is in process of aborting IO
*/ if (ctxp->flag & (LPFC_NVME_XBUSY | LPFC_NVME_ABORT_OP)) {
spin_unlock_irqrestore(&ctxp->ctxlock, flags); return;
}
ctxp->flag |= LPFC_NVME_ABORT_OP;
/* A state of LPFC_NVME_STE_RCV means we have just received * the NVME command and have not started processing it. * (by issuing any IO WQEs on this exchange yet)
*/ if (ctxp->state == LPFC_NVME_STE_RCV)
lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid,
ctxp->oxid); else
lpfc_nvmet_sol_fcp_issue_abort(phba, ctxp, ctxp->sid,
ctxp->oxid);
}
spin_lock_irqsave(&ctxp->ctxlock, flags); if (ctxp->flag & LPFC_NVME_XBUSY)
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR, "6027 NVMET release with XBUSY flag x%x" " oxid x%x\n",
ctxp->flag, ctxp->oxid); elseif (ctxp->state != LPFC_NVME_STE_DONE &&
ctxp->state != LPFC_NVME_STE_ABORT)
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6413 NVMET release bad state %d %d oxid x%x\n",
ctxp->state, ctxp->entry_cnt, ctxp->oxid);
if ((ctxp->flag & LPFC_NVME_ABORT_OP) ||
(ctxp->flag & LPFC_NVME_XBUSY)) {
aborting = true; /* let the abort path do the real release */
lpfc_nvmet_defer_release(phba, ctxp);
}
spin_unlock_irqrestore(&ctxp->ctxlock, flags);
tgtp = phba->targetport->private; if (tgtp)
atomic_inc(&tgtp->rcv_fcp_cmd_defer);
/* Free the nvmebuf since a new buffer already replaced it */
nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
}
/** * lpfc_nvmet_ls_req_cmp - completion handler for a nvme ls request * @phba: Pointer to HBA context object * @cmdwqe: Pointer to driver command WQE object. * @rspwqe: Pointer to driver response WQE object. * * This function is the completion handler for NVME LS requests. * The function updates any states and statistics, then calls the * generic completion handler to finish completion of the request.
**/ staticvoid
lpfc_nvmet_ls_req_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe)
{ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
__lpfc_nvme_ls_req_cmp(phba, cmdwqe->vport, cmdwqe, wcqe);
}
/** * lpfc_nvmet_ls_req - Issue an Link Service request * @targetport: pointer to target instance registered with nvmet transport. * @hosthandle: hosthandle set by the driver in a prior ls_rqst_rcv. * Driver sets this value to the ndlp pointer. * @pnvme_lsreq: the transport nvme_ls_req structure for the LS * * Driver registers this routine to handle any link service request * from the nvme_fc transport to a remote nvme-aware port. * * Return value : * 0 - Success * non-zero: various error codes, in form of -Exxx
**/ staticint
lpfc_nvmet_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle, struct nvmefc_ls_req *pnvme_lsreq)
{ struct lpfc_nvmet_tgtport *lpfc_nvmet = targetport->private; struct lpfc_hba *phba; struct lpfc_nodelist *ndlp; int ret;
u32 hstate;
if (!lpfc_nvmet) return -EINVAL;
phba = lpfc_nvmet->phba; if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) return -EINVAL;
hstate = atomic_read(&lpfc_nvmet->state); if (hstate == LPFC_NVMET_INV_HOST_ACTIVE) return -EACCES;
ndlp = (struct lpfc_nodelist *)hosthandle;
ret = __lpfc_nvme_ls_req(phba->pport, ndlp, pnvme_lsreq,
lpfc_nvmet_ls_req_cmp);
return ret;
}
/** * lpfc_nvmet_ls_abort - Abort a prior NVME LS request * @targetport: Transport targetport, that LS was issued from. * @hosthandle: hosthandle set by the driver in a prior ls_rqst_rcv. * Driver sets this value to the ndlp pointer. * @pnvme_lsreq: the transport nvme_ls_req structure for LS to be aborted * * Driver registers this routine to abort an NVME LS request that is * in progress (from the transports perspective).
**/ staticvoid
lpfc_nvmet_ls_abort(struct nvmet_fc_target_port *targetport, void *hosthandle, struct nvmefc_ls_req *pnvme_lsreq)
{ struct lpfc_nvmet_tgtport *lpfc_nvmet = targetport->private; struct lpfc_hba *phba; struct lpfc_nodelist *ndlp; int ret;
phba = lpfc_nvmet->phba; if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) return;
ndlp = (struct lpfc_nodelist *)hosthandle;
ret = __lpfc_nvme_ls_abort(phba->pport, ndlp, pnvme_lsreq); if (!ret)
atomic_inc(&lpfc_nvmet->xmt_ls_abort);
}
/* optional features */
.target_features = 0, /* sizes of additional private data for data structures */
.target_priv_sz = sizeof(struct lpfc_nvmet_tgtport),
.lsrqst_priv_sz = 0,
};
staticvoid
lpfc_nvmet_cleanup_io_context(struct lpfc_hba *phba)
{ struct lpfc_nvmet_ctx_info *infop; int i, j;
/* The first context list, MRQ 0 CPU 0 */
infop = phba->sli4_hba.nvmet_ctx_info; if (!infop) return;
/* Cycle the entire CPU context list for every MRQ */ for (i = 0; i < phba->cfg_nvmet_mrq; i++) {
for_each_present_cpu(j) {
infop = lpfc_get_ctx_list(phba, j, i);
__lpfc_nvmet_clean_io_for_cpu(phba, infop);
}
}
kfree(phba->sli4_hba.nvmet_ctx_info);
phba->sli4_hba.nvmet_ctx_info = NULL;
}
staticint
lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
{ struct lpfc_nvmet_ctxbuf *ctx_buf; struct lpfc_iocbq *nvmewqe; union lpfc_wqe128 *wqe; struct lpfc_nvmet_ctx_info *last_infop; struct lpfc_nvmet_ctx_info *infop; int i, j, idx, cpu;
/* * 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.
*/
for_each_possible_cpu(i) { for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
infop = lpfc_get_ctx_list(phba, i, j);
INIT_LIST_HEAD(&infop->nvmet_ctx_list);
spin_lock_init(&infop->nvmet_ctx_list_lock);
infop->nvmet_ctx_list_cnt = 0;
}
}
/* * 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,
cpumask_first(cpu_present_mask),
j); for (i = phba->sli4_hba.num_possible_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 * received command on a per xri basis.
*/
idx = 0;
cpu = cpumask_first(cpu_present_mask); for (i = 0; i < phba->sli4_hba.nvmet_xri_cnt; i++) {
ctx_buf = kzalloc(sizeof(*ctx_buf), GFP_KERNEL); if (!ctx_buf) {
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6404 Ran out of memory for NVMET\n"); return -ENOMEM;
}
ctx_buf->context = kzalloc(sizeof(*ctx_buf->context),
GFP_KERNEL); if (!ctx_buf->context) {
kfree(ctx_buf);
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6405 Ran out of NVMET " "context memory\n"); return -ENOMEM;
}
ctx_buf->context->ctxbuf = ctx_buf;
ctx_buf->context->state = LPFC_NVME_STE_FREE;
ctx_buf->iocbq = lpfc_sli_get_iocbq(phba); if (!ctx_buf->iocbq) {
kfree(ctx_buf->context);
kfree(ctx_buf);
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6406 Ran out of NVMET iocb/WQEs\n"); return -ENOMEM;
}
ctx_buf->iocbq->cmd_flag = LPFC_IO_NVMET;
nvmewqe = ctx_buf->iocbq;
wqe = &nvmewqe->wqe;
ctx_buf->iocbq->cmd_dmabuf = NULL;
spin_lock(&phba->sli4_hba.sgl_list_lock);
ctx_buf->sglq = __lpfc_sli_get_nvmet_sglq(phba, ctx_buf->iocbq);
spin_unlock(&phba->sli4_hba.sgl_list_lock); if (!ctx_buf->sglq) {
lpfc_sli_release_iocbq(phba, ctx_buf->iocbq);
kfree(ctx_buf->context);
kfree(ctx_buf);
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6407 Ran out of NVMET XRIs\n"); return -ENOMEM;
}
INIT_WORK(&ctx_buf->defer_work, lpfc_nvmet_fcp_rqst_defer_work);
/* * Add ctx to MRQidx context list. Our initial assumption * is MRQidx will be associated with CPUidx. This association * can change on the fly.
*/
infop = lpfc_get_ctx_list(phba, cpu, 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;
cpu = cpumask_first(cpu_present_mask); continue;
}
cpu = lpfc_next_present_cpu(cpu);
}
for_each_present_cpu(i) { for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
infop = lpfc_get_ctx_list(phba, i, j);
lpfc_printf_log(phba, KERN_INFO, LOG_NVME | LOG_INIT, "6408 TOTAL NVMET ctx for CPU %d " "MRQ %d: cnt %d nextcpu x%px\n",
i, j, infop->nvmet_ctx_list_cnt,
infop->nvmet_ctx_next_cpu);
}
} return 0;
}
int
lpfc_nvmet_create_targetport(struct lpfc_hba *phba)
{ struct lpfc_vport *vport = phba->pport; struct lpfc_nvmet_tgtport *tgtp; struct nvmet_fc_port_info pinfo; int error;
if (phba->targetport) return 0;
error = lpfc_nvmet_setup_io_context(phba); if (error) return error;
/* We need to tell the transport layer + 1 because it takes page * alignment into account. When space for the SGL is allocated we * allocate + 3, one for cmd, one for rsp and one for this alignment
*/
lpfc_tgttemplate.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1;
lpfc_tgttemplate.max_hw_queues = phba->cfg_hdw_queue;
lpfc_tgttemplate.target_features = NVMET_FCTGTFEAT_READDATA_RSP;
spin_lock_irqsave(&ctxp->ctxlock, iflag); /* Check if we already received a free context call * and we have completed processing an abort situation.
*/ if (ctxp->flag & LPFC_NVME_CTX_RLS &&
!(ctxp->flag & LPFC_NVME_ABORT_OP)) {
spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
list_del_init(&ctxp->list);
spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
released = true;
}
ctxp->flag &= ~LPFC_NVME_XBUSY;
spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
/** * lpfc_nvmet_handle_lsreq - Process an NVME LS request * @phba: pointer to lpfc hba data structure. * @axchg: pointer to exchange context for the NVME LS request * * This routine is used for processing an asynchronously received NVME LS * request. Any remaining validation is done and the LS is then forwarded * to the nvmet-fc transport via nvmet_fc_rcv_ls_req(). * * The calling sequence should be: nvmet_fc_rcv_ls_req() -> (processing) * -> lpfc_nvmet_xmt_ls_rsp/cmp -> req->done. * lpfc_nvme_xmt_ls_rsp_cmp should free the allocated axchg. * * Returns 0 if LS was handled and delivered to the transport * Returns 1 if LS failed to be handled and should be dropped
*/ int
lpfc_nvmet_handle_lsreq(struct lpfc_hba *phba, struct lpfc_async_xchg_ctx *axchg)
{ #if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) struct lpfc_nvmet_tgtport *tgtp = phba->targetport->private;
uint32_t *payload = axchg->payload; int rc;
atomic_inc(&tgtp->rcv_ls_req_in);
/* * Driver passes the ndlp as the hosthandle argument allowing * the transport to generate LS requests for any associateions * that are created.
*/
rc = nvmet_fc_rcv_ls_req(phba->targetport, axchg->ndlp, &axchg->ls_rsp,
axchg->payload, axchg->size);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.