/******************************************************************* * 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);
payload = (uint32_t *)(nvmebuf->dbuf.virt);
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
ctxp->flag |= LPFC_NVME_TNOTIFY; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS if (ctxp->ts_isr_cmd)
ctxp->ts_cmd_nvme = ktime_get_ns(); #endif /* * The calling sequence should be: * nvmet_fc_rcv_fcp_req->lpfc_nvmet_xmt_fcp_op/cmp- req->done * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp. * When we return from nvmet_fc_rcv_fcp_req, all relevant info * the NVME command / FC header is stored. * A buffer has already been reposted for this IO, so just free * the nvmebuf.
*/
rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->hdlrctx.fcp_req,
payload, ctxp->size); /* Process FCP command */ if (rc == 0) {
atomic_inc(&tgtp->rcv_fcp_cmd_out);
spin_lock_irqsave(&ctxp->ctxlock, iflags); if ((ctxp->flag & LPFC_NVME_CTX_REUSE_WQ) ||
(nvmebuf != ctxp->rqb_buffer)) {
spin_unlock_irqrestore(&ctxp->ctxlock, iflags); return;
}
ctxp->rqb_buffer = NULL;
spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */ return;
}
/* * 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_possible_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,
¤t_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);
/* 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;
}
#endif /* Nothing found, all contexts for the MRQ are in-flight */ return NULL;
}
/** * lpfc_nvmet_unsol_fcp_buffer - Process an unsolicited event data buffer * @phba: pointer to lpfc hba data structure. * @idx: relative index of MRQ vector * @nvmebuf: pointer to lpfc nvme command HBQ data structure. * @isr_timestamp: in jiffies. * @cqflag: cq processing information regarding workload. * * This routine is used for processing the WQE associated with a unsolicited * event. It first determines whether there is an existing ndlp that matches * the DID from the unsolicited WQE. If not, it will create a new one with * the DID from the unsolicited WQE. The ELS command from the unsolicited * WQE is then used to invoke the proper routine and to set up proper state * of the discovery state machine.
**/ staticvoid
lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
uint32_t idx, struct rqb_dmabuf *nvmebuf,
uint64_t isr_timestamp,
uint8_t cqflag)
{ struct lpfc_async_xchg_ctx *ctxp; struct lpfc_nvmet_tgtport *tgtp; struct fc_frame_header *fc_hdr; struct lpfc_nvmet_ctxbuf *ctx_buf; struct lpfc_nvmet_ctx_info *current_infop;
uint32_t size, oxid, sid, qno; unsignedlong iflag; int current_cpu;
if (!IS_ENABLED(CONFIG_NVME_TARGET_FC)) return;
ctx_buf = NULL; if (!nvmebuf || !phba->targetport) {
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6157 NVMET FCP Drop IO\n"); if (nvmebuf)
lpfc_rq_buf_free(phba, &nvmebuf->hbuf); return;
}
/* * Get a pointer to the context list for this MRQ based on * 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 = raw_smp_processor_id();
current_infop = lpfc_get_ctx_list(phba, current_cpu, idx);
spin_lock_irqsave(¤t_infop->nvmet_ctx_list_lock, iflag); if (current_infop->nvmet_ctx_list_cnt) {
list_remove_head(¤t_infop->nvmet_ctx_list,
ctx_buf, struct lpfc_nvmet_ctxbuf, list);
current_infop->nvmet_ctx_list_cnt--;
} else {
ctx_buf = lpfc_nvmet_replenish_context(phba, current_infop);
}
spin_unlock_irqrestore(¤t_infop->nvmet_ctx_list_lock, iflag);
if (!ctx_buf) { /* Queue this NVME IO to process later */
spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
list_add_tail(&nvmebuf->hbuf.list,
&phba->sli4_hba.lpfc_nvmet_io_wait_list);
phba->sli4_hba.nvmet_io_wait_cnt++;
phba->sli4_hba.nvmet_io_wait_total++;
spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock,
iflag);
/* Post a brand new DMA buffer to RQ */
qno = nvmebuf->idx;
lpfc_post_rq_buffer(
phba, phba->sli4_hba.nvmet_mrq_hdr[qno],
phba->sli4_hba.nvmet_mrq_data[qno], 1, qno);
/** * lpfc_nvmet_unsol_fcp_event - Process an unsolicited event from an nvme nport * @phba: pointer to lpfc hba data structure. * @idx: relative index of MRQ vector * @nvmebuf: pointer to received nvme data structure. * @isr_timestamp: in jiffies. * @cqflag: cq processing information regarding workload. * * This routine is used to process an unsolicited event received from a SLI * (Service Level Interface) ring. The actual processing of the data buffer * associated with the unsolicited event is done by invoking the routine * lpfc_nvmet_unsol_fcp_buffer() after properly set up the buffer from the * SLI RQ on which the unsolicited event was received.
**/ void
lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba,
uint32_t idx, struct rqb_dmabuf *nvmebuf,
uint64_t isr_timestamp,
uint8_t cqflag)
{ if (!nvmebuf) {
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "3167 NVMET FCP Drop IO\n"); return;
} if (phba->nvmet_support == 0) {
lpfc_rq_buf_free(phba, &nvmebuf->hbuf); return;
}
lpfc_nvmet_unsol_fcp_buffer(phba, idx, nvmebuf, isr_timestamp, cqflag);
}
/** * lpfc_nvmet_prep_ls_wqe - Allocate and prepare a lpfc wqe data structure * @phba: pointer to a host N_Port data structure. * @ctxp: Context info for NVME LS Request * @rspbuf: DMA buffer of NVME command. * @rspsize: size of the NVME command. * * This routine is used for allocating a lpfc-WQE data structure from * the driver lpfc-WQE free-list and prepare the WQE with the parameters * passed into the routine for discovery state machine to issue an Extended * Link Service (NVME) commands. It is a generic lpfc-WQE allocation * and preparation routine that is used by all the discovery state machine * routines and the NVME command-specific fields will be later set up by * the individual discovery machine routines after calling this routine * allocating and preparing a generic WQE data structure. It fills in the * Buffer Descriptor Entries (BDEs), allocates buffers for both command * payload and response payload (if expected). The reference count on the * ndlp is incremented by 1 and the reference to the ndlp is put into * context1 of the WQE data structure for this WQE to hold the ndlp * reference for the command's callback function to access later. * * Return code * Pointer to the newly allocated/prepared nvme wqe data structure * NULL - when nvme wqe data structure allocation/preparation failed
**/ staticstruct lpfc_iocbq *
lpfc_nvmet_prep_ls_wqe(struct lpfc_hba *phba, struct lpfc_async_xchg_ctx *ctxp,
dma_addr_t rspbuf, uint16_t rspsize)
{ struct lpfc_nodelist *ndlp; struct lpfc_iocbq *nvmewqe; union lpfc_wqe128 *wqe;
if (!lpfc_is_link_up(phba)) {
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6104 NVMET prep LS wqe: link err: " "NPORT x%x oxid:x%x ste %d\n",
ctxp->sid, ctxp->oxid, ctxp->state); return NULL;
}
/* Allocate buffer for command wqe */
nvmewqe = lpfc_sli_get_iocbq(phba); if (nvmewqe == NULL) {
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6105 NVMET prep LS wqe: No WQE: " "NPORT x%x oxid x%x ste %d\n",
ctxp->sid, ctxp->oxid, ctxp->state); return NULL;
}
/* Word 8 */
wqe->xmit_sequence.wqe_com.abort_tag = nvmewqe->iotag;
/* Word 9 */
bf_set(wqe_reqtag, &wqe->xmit_sequence.wqe_com, nvmewqe->iotag); /* Needs to be set by caller */
bf_set(wqe_rcvoxid, &wqe->xmit_sequence.wqe_com, ctxp->oxid);
case NVMET_FCOP_WRITEDATA: /* From the treceive template, initialize words 3 - 11 */
memcpy(&wqe->words[3],
&lpfc_treceive_cmd_template.words[3], sizeof(uint32_t) * 9);
/* Words 0 - 2 : First SGE is skipped, set invalid BDE type */
wqe->fcp_treceive.bde.tus.f.bdeFlags = LPFC_SGE_TYPE_SKIP;
wqe->fcp_treceive.bde.tus.f.bdeSize = 0;
wqe->fcp_treceive.bde.addrLow = 0;
wqe->fcp_treceive.bde.addrHigh = 0;
/* Word 4 */
wqe->fcp_treceive.relative_offset = ctxp->offset;
/* Word 6 */
bf_set(wqe_ctxt_tag, &wqe->fcp_treceive.wqe_com,
phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
bf_set(wqe_xri_tag, &wqe->fcp_treceive.wqe_com,
nvmewqe->sli4_xritag);
/* Word 7 */
/* Word 8 */
wqe->fcp_treceive.wqe_com.abort_tag = nvmewqe->iotag;
/* Word 9 */
bf_set(wqe_reqtag, &wqe->fcp_treceive.wqe_com, nvmewqe->iotag);
bf_set(wqe_rcvoxid, &wqe->fcp_treceive.wqe_com, ctxp->oxid);
/* Word 10 - in template xc=1 */ if (!xc)
bf_set(wqe_xc, &wqe->fcp_treceive.wqe_com, 0);
/* Word 11 - check for pbde */ if (nsegs == 1 && phba->cfg_enable_pbde) {
use_pbde = true; /* Word 11 - PBDE bit already preset by template */
} else { /* Overwrite default template setting */
bf_set(wqe_pbde, &wqe->fcp_treceive.wqe_com, 0);
}
/* Word 12 */
wqe->fcp_tsend.fcp_data_len = rsp->transfer_length;
case NVMET_FCOP_RSP: /* From the treceive template, initialize words 4 - 11 */
memcpy(&wqe->words[4],
&lpfc_trsp_cmd_template.words[4], sizeof(uint32_t) * 8);
/** * lpfc_nvmet_sol_fcp_abort_cmp - Completion handler for ABTS * @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 ABTS for FCP cmds * The function frees memory resources used for the NVME commands.
**/ staticvoid
lpfc_nvmet_sol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe)
{ struct lpfc_async_xchg_ctx *ctxp; struct lpfc_nvmet_tgtport *tgtp;
uint32_t result; unsignedlong flags; bool released = false; struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
ctxp = cmdwqe->context_un.axchg;
result = wcqe->parameter;
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; if (ctxp->flag & LPFC_NVME_ABORT_OP)
atomic_inc(&tgtp->xmt_fcp_abort_cmpl);
/* 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_XBUSY)) {
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_ABORT_OP;
spin_unlock_irqrestore(&ctxp->ctxlock, flags);
atomic_inc(&tgtp->xmt_abort_rsp);
cmdwqe->rsp_dmabuf = NULL;
cmdwqe->bpl_dmabuf = NULL; /* * if transport has released ctx, then can reuse it. Otherwise, * will be recycled by transport release call.
*/ if (released)
lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
/* This is the iocbq for the abort, not the command */
lpfc_sli_release_iocbq(phba, cmdwqe);
/* Since iaab/iaar are NOT set, there is no work left. * For LPFC_NVME_XBUSY, lpfc_sli4_nvmet_xri_aborted * should have been called already.
*/
}
/** * lpfc_nvmet_unsol_fcp_abort_cmp - Completion handler for ABTS * @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 ABTS for FCP cmds * The function frees memory resources used for the NVME commands.
**/ staticvoid
lpfc_nvmet_unsol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe)
{ struct lpfc_async_xchg_ctx *ctxp; struct lpfc_nvmet_tgtport *tgtp; unsignedlong flags;
uint32_t result; bool released = false; struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
ctxp = cmdwqe->context_un.axchg;
result = wcqe->parameter;
if (!ctxp) { /* if context is clear, related io alrady complete */
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, "6070 ABTS cmpl: WCQE: %08x %08x %08x %08x\n",
wcqe->word0, wcqe->total_data_placed,
result, wcqe->word3); return;
}
/* Check if we already received a free context call * and we have completed processing an abort situation.
*/
ctxp->state = LPFC_NVME_STE_DONE; if ((ctxp->flag & LPFC_NVME_CTX_RLS) &&
!(ctxp->flag & LPFC_NVME_XBUSY)) {
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_ABORT_OP;
spin_unlock_irqrestore(&ctxp->ctxlock, flags);
atomic_inc(&tgtp->xmt_abort_rsp);
cmdwqe->rsp_dmabuf = NULL;
cmdwqe->bpl_dmabuf = NULL; /* * if transport has released ctx, then can reuse it. Otherwise, * will be recycled by transport release call.
*/ if (released)
lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
/* Since iaab/iaar are NOT set, there is no work left. * For LPFC_NVME_XBUSY, lpfc_sli4_nvmet_xri_aborted * should have been called already.
*/
}
/** * lpfc_nvmet_xmt_ls_abort_cmp - Completion handler for ABTS * @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 ABTS for LS cmds * The function frees memory resources used for the NVME commands.
**/ staticvoid
lpfc_nvmet_xmt_ls_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe)
{ struct lpfc_async_xchg_ctx *ctxp; struct lpfc_nvmet_tgtport *tgtp;
uint32_t result; struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
ctxp = cmdwqe->context_un.axchg;
result = wcqe->parameter;
if (phba->nvmet_support) {
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
atomic_inc(&tgtp->xmt_ls_abort_cmpl);
}
/* * Since we zero the whole WQE, we need to ensure we set the WQE fields * that were initialized in lpfc_sli4_nvmet_alloc.
*/
memset(wqe_abts, 0, sizeof(union lpfc_wqe));
/* Word 8 */
wqe_abts->xmit_sequence.wqe_com.abort_tag = abts_wqeq->iotag;
/* Word 9 */
bf_set(wqe_reqtag, &wqe_abts->xmit_sequence.wqe_com, abts_wqeq->iotag); /* Needs to be set by caller */
bf_set(wqe_rcvoxid, &wqe_abts->xmit_sequence.wqe_com, xri);
/* No failure to an ABTS request. */
spin_lock_irqsave(&ctxp->ctxlock, flags);
ctxp->flag &= ~LPFC_NVME_ABORT_OP;
spin_unlock_irqrestore(&ctxp->ctxlock, flags); return 0;
}
/* Issue ABTS for this WQE based on iotag */
ctxp->abort_wqeq = lpfc_sli_get_iocbq(phba);
spin_lock_irqsave(&ctxp->ctxlock, flags); if (!ctxp->abort_wqeq) {
atomic_inc(&tgtp->xmt_abort_rsp_error);
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6161 ABORT failed: No wqeqs: " "xri: x%x\n", ctxp->oxid); /* No failure to an ABTS request. */
ctxp->flag &= ~LPFC_NVME_ABORT_OP;
spin_unlock_irqrestore(&ctxp->ctxlock, flags); return 0;
}
abts_wqeq = ctxp->abort_wqeq;
ctxp->state = LPFC_NVME_STE_ABORT;
ia = (ctxp->flag & LPFC_NVME_ABTS_RCV) ? true : false;
spin_unlock_irqrestore(&ctxp->ctxlock, flags);
/* Announce entry to new IO submit field. */
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, "6162 ABORT Request to rport DID x%06x " "for xri x%x x%x\n",
ctxp->sid, ctxp->oxid, ctxp->wqeq->sli4_xritag);
/* If the hba is getting reset, this flag is set. It is * cleared when the reset is complete and rings reestablished.
*/ /* driver queued commands are in process of being flushed */ if (test_bit(HBA_IOQ_FLUSH, &phba->hba_flag)) {
atomic_inc(&tgtp->xmt_abort_rsp_error);
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6163 Driver in reset cleanup - flushing " "NVME Req now. hba_flag x%lx oxid x%x\n",
phba->hba_flag, ctxp->oxid);
lpfc_sli_release_iocbq(phba, abts_wqeq);
spin_lock_irqsave(&ctxp->ctxlock, flags);
ctxp->flag &= ~LPFC_NVME_ABORT_OP;
spin_unlock_irqrestore(&ctxp->ctxlock, flags); return 0;
}
/* ABTS WQE must go to the same WQ as the WQE to be aborted */
abts_wqeq->hba_wqidx = ctxp->wqeq->hba_wqidx;
abts_wqeq->cmd_cmpl = lpfc_nvmet_sol_fcp_abort_cmp;
abts_wqeq->cmd_flag |= LPFC_IO_NVME;
abts_wqeq->context_un.axchg = ctxp;
abts_wqeq->vport = phba->pport; if (!ctxp->hdwq)
ctxp->hdwq = &phba->sli4_hba.hdwq[abts_wqeq->hba_wqidx];
atomic_inc(&tgtp->xmt_abort_rsp_error);
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6135 Failed to Issue ABTS for oxid x%x. Status x%x " "(%x)\n",
ctxp->oxid, rc, released); if (released)
lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf); return 1;
}
/** * lpfc_nvme_unsol_ls_issue_abort - issue ABTS on an exchange received * via async frame receive where the frame is not handled. * @phba: pointer to adapter structure * @ctxp: pointer to the asynchronously received received sequence * @sid: address of the remote port to send the ABTS to * @xri: oxid value to for the ABTS (other side's exchange id).
**/ int
lpfc_nvme_unsol_ls_issue_abort(struct lpfc_hba *phba, struct lpfc_async_xchg_ctx *ctxp,
uint32_t sid, uint16_t xri)
{ struct lpfc_nvmet_tgtport *tgtp = NULL; struct lpfc_iocbq *abts_wqeq; unsignedlong flags; int rc;
if (phba->nvmet_support && phba->targetport)
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
if (!ctxp->wqeq) { /* Issue ABTS for this WQE based on iotag */
ctxp->wqeq = lpfc_sli_get_iocbq(phba); if (!ctxp->wqeq) {
lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "6068 Abort failed: No wqeqs: " "xri: x%x\n", xri); /* No failure to an ABTS request. */
kfree(ctxp); return 0;
}
}
abts_wqeq = ctxp->wqeq;
/** * lpfc_nvmet_invalidate_host * * @phba: pointer to the driver instance bound to an adapter port. * @ndlp: pointer to an lpfc_nodelist type * * This routine upcalls the nvmet transport to invalidate an NVME * host to which this target instance had active connections.
*/ void
lpfc_nvmet_invalidate_host(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
{
u32 ndlp_has_hh; struct lpfc_nvmet_tgtport *tgtp;
/* Do not invalidate any nodes that do not have a hosthandle. * The host_release callbk will cause a node reference * count imbalance and a crash.
*/ if (!ndlp_has_hh) {
lpfc_printf_log(phba, KERN_INFO,
LOG_NVME | LOG_NVME_ABTS | LOG_NVME_DISC, "6204 Skip invalidate on node x%px DID x%x\n",
ndlp, ndlp->nlp_DID); return;
}
#if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) /* Need to get the nvmet_fc_target_port pointer here.*/
nvmet_fc_invalidate_host(phba->targetport, ndlp); #endif
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.41 Sekunden
(vorverarbeitet am 2026-04-29)
¤
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.