/******************************************************************* * 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) 2009-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * * * * 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. *
*******************************************************************/
/** * lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler * @phba: Pointer to HBA context object. * @cmdiocbq: Pointer to command iocb. * @rspiocbq: Pointer to response iocb. * * This function is the completion handler for iocbs issued using * lpfc_bsg_send_mgmt_cmd function. This function is called by the * ring event handler function without any lock held. This function * can be called from both worker thread context and interrupt * context. This function also can be called from another thread which * cleans up the SLI layer objects. * This function copies the contents of the response iocb to the * response iocb memory object provided by the caller of * lpfc_sli_issue_iocb_wait and then wakes up the thread which * sleeps for the iocb completion.
**/ staticvoid
lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq, struct lpfc_iocbq *rspiocbq)
{ struct bsg_job_data *dd_data; struct bsg_job *job; struct fc_bsg_reply *bsg_reply; struct lpfc_dmabuf *bmp, *cmp, *rmp; struct lpfc_nodelist *ndlp; struct lpfc_bsg_iocb *iocb; unsignedlong flags; int rc = 0;
u32 ulp_status, ulp_word4, total_data_placed;
dd_data = cmdiocbq->context_un.dd_data;
/* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
job = dd_data->set_job; if (job) {
bsg_reply = job->reply; /* Prevent timeout handling from trying to abort job */
job->dd_data = NULL;
}
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* Close the timeout handler abort window */
spin_lock_irqsave(&phba->hbalock, flags);
cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
spin_unlock_irqrestore(&phba->hbalock, flags);
iocb_stat = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); if (iocb_stat == IOCB_SUCCESS) {
spin_lock_irqsave(&phba->hbalock, flags); /* make sure the I/O had not been completed yet */ if (cmdiocbq->cmd_flag & LPFC_IO_LIBDFC) { /* open up abort window to timeout handler */
cmdiocbq->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
}
spin_unlock_irqrestore(&phba->hbalock, flags); return 0; /* done for now */
} elseif (iocb_stat == IOCB_BUSY) {
rc = -EAGAIN;
} else {
rc = -EIO;
}
/* iocb failed so cleanup */
lpfc_nlp_put(ndlp);
free_rmp:
lpfc_free_bsg_buffers(phba, rmp);
free_cmp:
lpfc_free_bsg_buffers(phba, cmp);
free_bmp: if (bmp->virt)
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp);
free_cmdiocbq:
lpfc_sli_release_iocbq(phba, cmdiocbq);
free_dd:
kfree(dd_data);
no_dd_data: /* make error code available to userspace */
bsg_reply->result = rc;
job->dd_data = NULL; return rc;
}
/** * lpfc_bsg_rport_els_cmp - lpfc_bsg_rport_els's completion handler * @phba: Pointer to HBA context object. * @cmdiocbq: Pointer to command iocb. * @rspiocbq: Pointer to response iocb. * * This function is the completion handler for iocbs issued using * lpfc_bsg_rport_els_cmp function. This function is called by the * ring event handler function without any lock held. This function * can be called from both worker thread context and interrupt * context. This function also can be called from other thread which * cleans up the SLI layer objects. * This function copies the contents of the response iocb to the * response iocb memory object provided by the caller of * lpfc_sli_issue_iocb_wait and then wakes up the thread which * sleeps for the iocb completion.
**/ staticvoid
lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq, struct lpfc_iocbq *rspiocbq)
{ struct bsg_job_data *dd_data; struct bsg_job *job; struct fc_bsg_reply *bsg_reply; struct lpfc_nodelist *ndlp; struct lpfc_dmabuf *pcmd = NULL, *prsp = NULL; struct fc_bsg_ctels_reply *els_reply;
uint8_t *rjt_data; unsignedlong flags; unsignedint rsp_size; int rc = 0;
u32 ulp_status, ulp_word4, total_data_placed;
if (!lpfc_nlp_get(ndlp)) {
rc = -ENODEV; goto free_dd_data;
}
/* We will use the allocated dma buffers by prep els iocb for command * and response to ensure if the job times out and the request is freed, * we won't be dma into memory that is no longer allocated to for the * request.
*/
cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
ndlp->nlp_DID, elscmd); if (!cmdiocbq) {
rc = -EIO; goto release_ndlp;
}
/* Transfer the request payload to allocated command dma buffer */
sg_copy_to_buffer(job->request_payload.sg_list,
job->request_payload.sg_cnt,
cmdiocbq->cmd_dmabuf->virt,
cmdsize);
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); if (rc == IOCB_SUCCESS) {
spin_lock_irqsave(&phba->hbalock, flags); /* make sure the I/O had not been completed/released */ if (cmdiocbq->cmd_flag & LPFC_IO_LIBDFC) { /* open up abort window to timeout handler */
cmdiocbq->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
}
spin_unlock_irqrestore(&phba->hbalock, flags); return 0; /* done for now */
} elseif (rc == IOCB_BUSY) {
rc = -EAGAIN;
} else {
rc = -EIO;
}
/* I/O issue failed. Cleanup resources. */
linkdown_err:
lpfc_els_free_iocb(phba, cmdiocbq);
release_ndlp:
lpfc_nlp_put(ndlp);
free_dd_data:
kfree(dd_data);
no_dd_data: /* make error code available to userspace */
bsg_reply->result = rc;
job->dd_data = NULL; return rc;
}
/** * lpfc_bsg_event_free - frees an allocated event structure * @kref: Pointer to a kref. * * Called from kref_put. Back cast the kref into an event structure address. * Free any events to get, delete associated nodes, free any events to see, * free any data then free the event itself.
**/ staticvoid
lpfc_bsg_event_free(struct kref *kref)
{ struct lpfc_bsg_event *evt = container_of(kref, struct lpfc_bsg_event,
kref); struct event_data *ed;
list_del(&evt->node);
while (!list_empty(&evt->events_to_get)) {
ed = list_entry(evt->events_to_get.next, typeof(*ed), node);
list_del(&ed->node);
kfree(ed->data);
kfree(ed);
}
while (!list_empty(&evt->events_to_see)) {
ed = list_entry(evt->events_to_see.next, typeof(*ed), node);
list_del(&ed->node);
kfree(ed->data);
kfree(ed);
}
kfree(evt->dd_data);
kfree(evt);
}
/** * lpfc_bsg_event_ref - increments the kref for an event * @evt: Pointer to an event structure.
**/ staticinlinevoid
lpfc_bsg_event_ref(struct lpfc_bsg_event *evt)
{
kref_get(&evt->kref);
}
/** * lpfc_bsg_event_unref - Uses kref_put to free an event structure * @evt: Pointer to an event structure.
**/ staticinlinevoid
lpfc_bsg_event_unref(struct lpfc_bsg_event *evt)
{
kref_put(&evt->kref, lpfc_bsg_event_free);
}
dd_data = (struct bsg_job_data *)evt->dd_data;
job = dd_data->set_job;
dd_data->set_job = NULL;
lpfc_bsg_event_unref(evt); if (job) {
bsg_reply = job->reply;
bsg_reply->reply_payload_rcv_len = size; /* make error code available to userspace */
bsg_reply->result = 0;
job->dd_data = NULL; /* complete the job back to userspace */
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
spin_lock_irqsave(&phba->ct_ev_lock, flags);
}
}
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
error_ct_unsol_exit: if (!list_empty(&head))
list_del(&head); if ((phba->sli_rev < LPFC_SLI_REV4) &&
(evt_req_id == SLI_CT_ELX_LOOPBACK)) return 0; return 1;
}
/** * lpfc_bsg_ct_unsol_abort - handler ct abort to management plane * @phba: Pointer to HBA context object. * @dmabuf: pointer to a dmabuf that describes the FC sequence * * This function handles abort to the CT command toward management plane * for SLI4 port. * * If the pending context of a CT command to management plane present, clears * such context and returns 1 for handled; otherwise, it returns 0 indicating * no context exists.
**/ int
lpfc_bsg_ct_unsol_abort(struct lpfc_hba *phba, struct hbq_dmabuf *dmabuf)
{ struct fc_frame_header fc_hdr; struct fc_frame_header *fc_hdr_ptr = &fc_hdr; int ctx_idx, handled = 0;
uint16_t oxid, rxid;
uint32_t sid;
/* The app may continue to ask for event data until it gets * an error indicating that there isn't anymore
*/ if (evt_dat == NULL) {
bsg_reply->reply_payload_rcv_len = 0;
rc = -ENOENT; goto job_error;
}
if (evt_dat->len > job->request_payload.payload_len) {
evt_dat->len = job->request_payload.payload_len;
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2618 Truncated event data at %d " "bytes\n",
job->request_payload.payload_len);
}
/** * lpfc_issue_ct_rsp_cmp - lpfc_issue_ct_rsp's completion handler * @phba: Pointer to HBA context object. * @cmdiocbq: Pointer to command iocb. * @rspiocbq: Pointer to response iocb. * * This function is the completion handler for iocbs issued using * lpfc_issue_ct_rsp_cmp function. This function is called by the * ring event handler function without any lock held. This function * can be called from both worker thread context and interrupt * context. This function also can be called from other thread which * cleans up the SLI layer objects. * This function copy the contents of the response iocb to the * response iocb memory object provided by the caller of * lpfc_sli_issue_iocb_wait and then wakes up the thread which * sleeps for the iocb completion.
**/ staticvoid
lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq, struct lpfc_iocbq *rspiocbq)
{ struct bsg_job_data *dd_data; struct bsg_job *job; struct fc_bsg_reply *bsg_reply; struct lpfc_dmabuf *bmp, *cmp; struct lpfc_nodelist *ndlp; unsignedlong flags; int rc = 0;
u32 ulp_status, ulp_word4;
dd_data = cmdiocbq->context_un.dd_data;
/* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
job = dd_data->set_job; if (job) { /* Prevent timeout handling from trying to abort job */
job->dd_data = NULL;
}
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* Close the timeout handler abort window */
spin_lock_irqsave(&phba->hbalock, flags);
cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
spin_unlock_irqrestore(&phba->hbalock, flags);
/* Allocate buffer for command iocb */
ctiocb = lpfc_sli_get_iocbq(phba); if (!ctiocb) {
rc = -ENOMEM; goto no_ctiocb;
}
if (phba->sli_rev == LPFC_SLI_REV4) { /* Do not issue unsol response if oxid not marked as valid */ if (phba->ct_ctx[tag].valid != UNSOL_VALID) {
rc = IOCB_ERROR; goto issue_ct_rsp_exit;
}
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0); if (rc == IOCB_SUCCESS) {
spin_lock_irqsave(&phba->hbalock, flags); /* make sure the I/O had not been completed/released */ if (ctiocb->cmd_flag & LPFC_IO_LIBDFC) { /* open up abort window to timeout handler */
ctiocb->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
}
spin_unlock_irqrestore(&phba->hbalock, flags); return 0; /* done for now */
}
/* iocb failed so cleanup */
job->dd_data = NULL;
lpfc_nlp_put(ndlp);
/** * lpfc_bsg_diag_mode_exit - exit process from device diag loopback mode * @phba: Pointer to HBA context object. * * This function is responsible for driver exit processing of setting up * diag loopback mode on device.
*/ staticvoid
lpfc_bsg_diag_mode_exit(struct lpfc_hba *phba)
{ struct Scsi_Host *shost; struct lpfc_vport **vports; int i;
vports = lpfc_create_vport_work_array(phba); if (vports) { for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
shost = lpfc_shost_from_vport(vports[i]);
scsi_unblock_requests(shost);
}
lpfc_destroy_vport_work_array(phba, vports);
} else {
shost = lpfc_shost_from_vport(phba->pport);
scsi_unblock_requests(shost);
} return;
}
/** * lpfc_sli3_bsg_diag_loopback_mode - process an sli3 bsg vendor command * @phba: Pointer to HBA context object. * @job: LPFC_BSG_VENDOR_DIAG_MODE * * This function is responsible for placing an sli3 port into diagnostic * loopback mode in order to perform a diagnostic loopback test. * All new scsi requests are blocked, a small delay is used to allow the * scsi requests to complete then the link is brought down. If the link is * is placed in loopback mode then scsi requests are again allowed * so the scsi mid-layer doesn't give up on the port. * All of this is done in-line.
*/ staticint
lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job)
{ struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply; struct diag_mode_set *loopback_mode;
uint32_t link_flags;
uint32_t timeout;
LPFC_MBOXQ_t *pmboxq = NULL; int mbxstatus = MBX_SUCCESS; int i = 0; int rc = 0;
/* no data to return just the return code */
bsg_reply->reply_payload_rcv_len = 0;
if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0)) { /* wait for link down before proceeding */
i = 0; while (phba->link_state != LPFC_LINK_DOWN) { if (i++ > timeout) {
rc = -ETIMEDOUT; goto loopback_mode_exit;
}
msleep(10);
}
/* * Let SLI layer release mboxq if mbox command completed after timeout.
*/ if (pmboxq && mbxstatus != MBX_TIMEOUT)
mempool_free(pmboxq, phba->mbox_mem_pool);
job_error: /* make error code available to userspace */
bsg_reply->result = rc; /* complete the job back to userspace if no error */ if (rc == 0)
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len); return rc;
}
/** * lpfc_sli4_bsg_set_link_diag_state - set sli4 link diag state * @phba: Pointer to HBA context object. * @diag: Flag for set link to diag or nomral operation state. * * This function is responsible for issuing a sli4 mailbox command for setting * link to either diag state or normal operation state.
*/ staticint
lpfc_sli4_bsg_set_link_diag_state(struct lpfc_hba *phba, uint32_t diag)
{
LPFC_MBOXQ_t *pmboxq; struct lpfc_mbx_set_link_diag_state *link_diag_state;
uint32_t req_len, alloc_len; int mbxstatus = MBX_SUCCESS, rc;
pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmboxq) return -ENOMEM;
req_len = (sizeof(struct lpfc_mbx_set_link_diag_state) - sizeof(struct lpfc_sli4_cfg_mhdr));
alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE,
req_len, LPFC_SLI4_MBX_EMBED); if (alloc_len != req_len) {
rc = -ENOMEM; goto link_diag_state_set_out;
}
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "3128 Set link to diagnostic state:x%x (x%x/x%x)\n",
diag, phba->sli4_hba.lnk_info.lnk_tp,
phba->sli4_hba.lnk_info.lnk_no);
link_diag_state_set_out: if (pmboxq && (mbxstatus != MBX_TIMEOUT))
mempool_free(pmboxq, phba->mbox_mem_pool);
return rc;
}
/** * lpfc_sli4_bsg_set_loopback_mode - set sli4 internal loopback diagnostic * @phba: Pointer to HBA context object. * @mode: loopback mode to set * @link_no: link number for loopback mode to set * * This function is responsible for issuing a sli4 mailbox command for setting * up loopback diagnostic for a link.
*/ staticint
lpfc_sli4_bsg_set_loopback_mode(struct lpfc_hba *phba, int mode,
uint32_t link_no)
{
LPFC_MBOXQ_t *pmboxq;
uint32_t req_len, alloc_len; struct lpfc_mbx_set_link_diag_loopback *link_diag_loopback; int mbxstatus = MBX_SUCCESS, rc = 0;
/** * lpfc_sli4_diag_fcport_reg_setup - setup port registrations for diagnostic * @phba: Pointer to HBA context object. * * This function set up SLI4 FC port registrations for diagnostic run, which * includes all the rpis, vfi, and also vpi.
*/ staticint
lpfc_sli4_diag_fcport_reg_setup(struct lpfc_hba *phba)
{ if (test_bit(FC_VFI_REGISTERED, &phba->pport->fc_flag)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "3136 Port still had vfi registered: " "mydid:x%x, fcfi:%d, vfi:%d, vpi:%d\n",
phba->pport->fc_myDID, phba->fcf.fcfi,
phba->sli4_hba.vfi_ids[phba->pport->vfi],
phba->vpi_ids[phba->pport->vpi]); return -EINVAL;
} return lpfc_issue_reg_vfi(phba->pport);
}
/** * lpfc_sli4_bsg_diag_loopback_mode - process an sli4 bsg vendor command * @phba: Pointer to HBA context object. * @job: LPFC_BSG_VENDOR_DIAG_MODE * * This function is responsible for placing an sli4 port into diagnostic * loopback mode in order to perform a diagnostic loopback test.
*/ staticint
lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job)
{ struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply; struct diag_mode_set *loopback_mode;
uint32_t link_flags, timeout, link_no; int i, rc = 0;
/* no data to return just the return code */
bsg_reply->reply_payload_rcv_len = 0;
if (link_flags == DISABLE_LOOP_BACK) {
rc = lpfc_sli4_bsg_set_loopback_mode(phba,
LPFC_DIAG_LOOPBACK_TYPE_DISABLE,
link_no); if (!rc) { /* Unset the need disable bit */
phba->sli4_hba.conf_trunk &= ~((1 << link_no) << 4);
} goto job_done;
} else { /* Check if we need to disable the loopback state */ if (phba->sli4_hba.conf_trunk & ((1 << link_no) << 4)) {
rc = -EPERM; goto job_done;
}
}
rc = lpfc_bsg_diag_mode_enter(phba); if (rc) goto job_done;
/* indicate we are in loobpack diagnostic mode */
spin_lock_irq(&phba->hbalock);
phba->link_flag |= LS_LOOPBACK_MODE;
spin_unlock_irq(&phba->hbalock);
/* reset port to start frome scratch */
rc = lpfc_selective_reset(phba); if (rc) goto job_done;
/* bring the link to diagnostic mode */
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "3129 Bring link to diagnostic state.\n");
rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1); if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "3130 Failed to bring link to diagnostic " "state, rc:x%x\n", rc); goto loopback_mode_exit;
}
/* wait for link down before proceeding */
i = 0; while (phba->link_state != LPFC_LINK_DOWN) { if (i++ > timeout) {
rc = -ETIMEDOUT;
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "3131 Timeout waiting for link to " "diagnostic mode, timeout:%d ms\n",
timeout * 10); goto loopback_mode_exit;
}
msleep(10);
}
/* set up loopback mode */
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "3132 Set up loopback mode:x%x\n", link_flags);
switch (link_flags) { case INTERNAL_LOOP_BACK: if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
rc = lpfc_sli4_bsg_set_loopback_mode(phba,
LPFC_DIAG_LOOPBACK_TYPE_INTERNAL,
link_no);
} else { /* Trunk is configured, but link is not in this trunk */ if (phba->sli4_hba.conf_trunk) {
rc = -ELNRNG; goto loopback_mode_exit;
}
if (!rc) { /* Set the need disable bit */
phba->sli4_hba.conf_trunk |= (1 << link_no) << 4;
}
break; case EXTERNAL_LOOP_BACK: if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
rc = lpfc_sli4_bsg_set_loopback_mode(phba,
LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL_TRUNKED,
link_no);
} else { /* Trunk is configured, but link is not in this trunk */ if (phba->sli4_hba.conf_trunk) {
rc = -ELNRNG; goto loopback_mode_exit;
}
if (!rc) { /* wait for the link attention interrupt */
msleep(100);
i = 0; while (phba->link_state < LPFC_LINK_UP) { if (i++ > timeout) {
rc = -ETIMEDOUT;
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "3137 Timeout waiting for link up " "in loopback mode, timeout:%d ms\n",
timeout * 10); break;
}
msleep(10);
}
}
/* port resource registration setup for loopback diagnostic */ if (!rc) { /* set up a none zero myDID for loopback test */
phba->pport->fc_myDID = 1;
rc = lpfc_sli4_diag_fcport_reg_setup(phba);
} else goto loopback_mode_exit;
if (!rc) { /* wait for the port ready */
msleep(100);
i = 0; while (phba->link_state != LPFC_HBA_READY) { if (i++ > timeout) {
rc = -ETIMEDOUT;
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "3133 Timeout waiting for port " "loopback mode ready, timeout:%d ms\n",
timeout * 10); break;
}
msleep(10);
}
}
job_done: /* make error code available to userspace */
bsg_reply->result = rc; /* complete the job back to userspace if no error */ if (rc == 0)
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len); return rc;
}
/** * lpfc_bsg_diag_loopback_mode - bsg vendor command for diag loopback mode * @job: LPFC_BSG_VENDOR_DIAG_MODE * * This function is responsible for responding to check and dispatch bsg diag * command from the user to proper driver action routines.
*/ staticint
lpfc_bsg_diag_loopback_mode(struct bsg_job *job)
{ struct Scsi_Host *shost; struct lpfc_vport *vport; struct lpfc_hba *phba; int rc;
shost = fc_bsg_to_shost(job); if (!shost) return -ENODEV;
vport = shost_priv(shost); if (!vport) return -ENODEV;
phba = vport->phba; if (!phba) return -ENODEV;
rc = lpfc_sli4_bsg_set_link_diag_state(phba, 0); if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "3139 Failed to bring link to diagnostic " "state, rc:x%x\n", rc); goto loopback_mode_end_exit;
}
/* wait for link down before proceeding */
i = 0; while (phba->link_state != LPFC_LINK_DOWN) { if (i++ > timeout) {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "3140 Timeout waiting for link to " "diagnostic mode_end, timeout:%d ms\n",
timeout * 10); /* there is nothing much we can do here */ break;
}
msleep(10);
}
loopback_mode_end_exit: /* make return code available to userspace */
bsg_reply->result = rc; /* complete the job back to userspace if no error */ if (rc == 0)
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len); return rc;
}
/** * lpfc_sli4_bsg_link_diag_test - sli4 bsg vendor command for diag link test * @job: LPFC_BSG_VENDOR_DIAG_LINK_TEST * * This function is to perform SLI4 diag link test request from the user * applicaiton.
*/ staticint
lpfc_sli4_bsg_link_diag_test(struct bsg_job *job)
{ struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply; struct Scsi_Host *shost; struct lpfc_vport *vport; struct lpfc_hba *phba;
LPFC_MBOXQ_t *pmboxq; struct sli4_link_diag *link_diag_test_cmd;
uint32_t req_len, alloc_len; struct lpfc_mbx_run_link_diag_test *run_link_diag_test; union lpfc_sli4_cfg_shdr *shdr;
uint32_t shdr_status, shdr_add_status; struct diag_status *diag_status_reply; int mbxstatus, rc = -ENODEV, rc1 = 0;
shost = fc_bsg_to_shost(job); if (!shost) goto job_error;
vport = shost_priv(shost); if (!vport) goto job_error;
phba = vport->phba; if (!phba) goto job_error;
if (phba->sli_rev < LPFC_SLI_REV4) goto job_error;
if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2) goto job_error;
if (job->request_len < sizeof(struct fc_bsg_request) + sizeof(struct sli4_link_diag)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "3013 Received LINK DIAG TEST request " " size:%d below the minimum size:%d\n",
job->request_len,
(int)(sizeof(struct fc_bsg_request) + sizeof(struct sli4_link_diag)));
rc = -EINVAL; goto job_error;
}
rc = lpfc_bsg_diag_mode_enter(phba); if (rc) goto job_error;
if (pmboxq)
mempool_free(pmboxq, phba->mbox_mem_pool);
lpfc_bsg_diag_mode_exit(phba);
job_error: /* make error code available to userspace */ if (rc1 && !rc)
rc = rc1;
bsg_reply->result = rc; /* complete the job back to userspace if no error */ if (rc == 0)
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len); return rc;
}
/** * lpfcdiag_loop_self_reg - obtains a remote port login id * @phba: Pointer to HBA context object * @rpi: Pointer to a remote port login id * * This function obtains a remote port login id so the diag loopback test * can send and receive its own unsolicited CT command.
**/ staticint lpfcdiag_loop_self_reg(struct lpfc_hba *phba, uint16_t *rpi)
{
LPFC_MBOXQ_t *mbox; struct lpfc_dmabuf *dmabuff; int status;
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM;
if (phba->sli_rev < LPFC_SLI_REV4)
status = lpfc_reg_rpi(phba, 0, phba->pport->fc_myDID,
(uint8_t *)&phba->pport->fc_sparam,
mbox, *rpi); else {
*rpi = lpfc_sli4_alloc_rpi(phba); if (*rpi == LPFC_RPI_ALLOC_ERROR) {
mempool_free(mbox, phba->mbox_mem_pool); return -EBUSY;
}
status = lpfc_reg_rpi(phba, phba->pport->vpi,
phba->pport->fc_myDID,
(uint8_t *)&phba->pport->fc_sparam,
mbox, *rpi);
}
if (status) {
mempool_free(mbox, phba->mbox_mem_pool); if (phba->sli_rev == LPFC_SLI_REV4)
lpfc_sli4_free_rpi(phba, *rpi); return -ENOMEM;
}
/** * lpfcdiag_loop_self_unreg - unregs from the rpi * @phba: Pointer to HBA context object * @rpi: Remote port login id * * This function unregisters the rpi obtained in lpfcdiag_loop_self_reg
**/ staticint lpfcdiag_loop_self_unreg(struct lpfc_hba *phba, uint16_t rpi)
{
LPFC_MBOXQ_t *mbox; int status;
status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) { if (status != MBX_TIMEOUT)
mempool_free(mbox, phba->mbox_mem_pool); return -EIO;
}
mempool_free(mbox, phba->mbox_mem_pool); if (phba->sli_rev == LPFC_SLI_REV4)
lpfc_sli4_free_rpi(phba, rpi); return 0;
}
/** * lpfcdiag_loop_get_xri - obtains the transmit and receive ids * @phba: Pointer to HBA context object * @rpi: Remote port login id * @txxri: Pointer to transmit exchange id * @rxxri: Pointer to response exchabge id * * This function obtains the transmit and receive ids required to send * an unsolicited ct command with a payload. A special lpfc FsType and CmdRsp * flags are used to the unsolicited response handler is able to process * the ct command sent on the same port.
**/ staticint lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
uint16_t *txxri, uint16_t * rxxri)
{ struct lpfc_bsg_event *evt; struct lpfc_iocbq *cmdiocbq, *rspiocbq; struct lpfc_dmabuf *dmabuf; struct ulp_bde64 *bpl = NULL; struct lpfc_sli_ct_request *ctreq = NULL; int ret_val = 0; int time_left; int iocb_stat = IOCB_SUCCESS; unsignedlong flags;
u32 status;
if (!dmabuf->virt) {
kfree(dmabuf); return NULL;
}
return dmabuf;
}
/** * lpfc_bsg_dma_page_free - free a bsg mbox page sized dma buffer * @phba: Pointer to HBA context object. * @dmabuf: Pointer to the bsg mbox page sized dma buffer descriptor. * * This routine just simply frees a dma buffer and its associated buffer * descriptor referred by @dmabuf.
**/ staticvoid
lpfc_bsg_dma_page_free(struct lpfc_hba *phba, struct lpfc_dmabuf *dmabuf)
{ struct pci_dev *pcidev = phba->pcidev;
if (!dmabuf) return;
if (dmabuf->virt)
dma_free_coherent(&pcidev->dev, BSG_MBOX_SIZE,
dmabuf->virt, dmabuf->phys);
kfree(dmabuf); return;
}
/** * lpfc_bsg_dma_page_list_free - free a list of bsg mbox page sized dma buffers * @phba: Pointer to HBA context object. * @dmabuf_list: Pointer to a list of bsg mbox page sized dma buffer descs. * * This routine just simply frees all dma buffers and their associated buffer * descriptors referred by @dmabuf_list.
**/ staticvoid
lpfc_bsg_dma_page_list_free(struct lpfc_hba *phba, struct list_head *dmabuf_list)
{ struct lpfc_dmabuf *dmabuf, *next_dmabuf;
/** * diag_cmd_data_alloc - fills in a bde struct with dma buffers * @phba: Pointer to HBA context object * @bpl: Pointer to 64 bit bde structure * @size: Number of bytes to process * @nocopydata: Flag to copy user data into the allocated buffer * * This function allocates page size buffers and populates an lpfc_dmabufext. * If allowed the user data pointed to with indataptr is copied into the kernel * memory. The chained list of page size buffers is returned.
**/ staticstruct lpfc_dmabufext *
diag_cmd_data_alloc(struct lpfc_hba *phba, struct ulp_bde64 *bpl, uint32_t size, int nocopydata)
{ struct lpfc_dmabufext *mlist = NULL; struct lpfc_dmabufext *dmp; int cnt, offset = 0, i = 0; struct pci_dev *pcidev;
pcidev = phba->pcidev;
while (size) { /* We get chunks of 4K */ if (size > BUF_SZ_4K)
cnt = BUF_SZ_4K; else
cnt = size;
/* The iocb was freed by lpfc_sli_issue_iocb */
cmdiocbq = lpfc_sli_get_iocbq(phba); if (!cmdiocbq) {
dmp = list_entry(next, struct lpfc_dmabuf, list);
ret_val = -EIO; goto err_post_rxbufs_exit;
}
cmd = &cmdiocbq->iocb;
i = 0;
}
list_del(&head);
err_post_rxbufs_exit:
if (rxbmp) { if (rxbmp->virt)
lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys);
kfree(rxbmp);
}
if (cmdiocbq)
lpfc_sli_release_iocbq(phba, cmdiocbq); return ret_val;
}
/** * lpfc_bsg_diag_loopback_run - run loopback on a port by issue ct cmd to itself * @job: LPFC_BSG_VENDOR_DIAG_TEST fc_bsg_job * * This function receives a user data buffer to be transmitted and received on * the same port, the link must be up and in loopback mode prior * to being called. * 1. A kernel buffer is allocated to copy the user data into. * 2. The port registers with "itself". * 3. The transmit and receive exchange ids are obtained. * 4. The receive exchange id is posted. * 5. A new els loopback event is created. * 6. The command and response iocbs are allocated. * 7. The cmd iocb FsType is set to elx loopback and the CmdRsp to looppback. * * This function is meant to be called n times while the port is in loopback * so it is the apps responsibility to issue a reset to take the port out * of loopback mode.
**/ staticint
lpfc_bsg_diag_loopback_run(struct bsg_job *job)
{ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job)); struct fc_bsg_reply *bsg_reply = job->reply; struct lpfc_hba *phba = vport->phba; struct lpfc_bsg_event *evt; struct event_data *evdat; struct lpfc_sli *psli = &phba->sli;
uint32_t size;
uint32_t full_size;
size_t segment_len = 0, segment_offset = 0, current_offset = 0;
uint16_t rpi = 0; struct lpfc_iocbq *cmdiocbq, *rspiocbq = NULL; union lpfc_wqe128 *cmdwqe, *rspwqe; struct lpfc_sli_ct_request *ctreq; struct lpfc_dmabuf *txbmp; struct ulp_bde64 *txbpl = NULL; struct lpfc_dmabufext *txbuffer = NULL; struct list_head head; struct lpfc_dmabuf *curr;
uint16_t txxri = 0, rxxri;
uint32_t num_bde;
uint8_t *ptr = NULL, *rx_databuf = NULL; int rc = 0; int time_left; int iocb_stat = IOCB_SUCCESS; unsignedlong flags; void *dataout = NULL;
uint32_t total_mem;
/* in case no data is returned return just the return code */
bsg_reply->reply_payload_rcv_len = 0;
if (job->request_len < sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_test)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2739 Received DIAG TEST request below minimum " "size\n");
rc = -EINVAL; goto loopback_test_exit;
}
if (full_size >= BUF_SZ_4K) { /* * Allocate memory for ioctl data. If buffer is bigger than 64k, * then we allocate 64k and re-use that buffer over and over to * xfer the whole block. This is because Linux kernel has a * problem allocating more than 120k of kernel space memory. Saw * problem with GET_FCPTARGETMAPPING...
*/ if (size <= (64 * 1024))
total_mem = full_size; else
total_mem = 64 * 1024;
} else /* Allocate memory for ioctl data */
total_mem = BUF_SZ_4K;
if ((cmdiocbq != NULL) && (iocb_stat != IOCB_TIMEDOUT))
lpfc_sli_release_iocbq(phba, cmdiocbq);
if (rspiocbq != NULL)
lpfc_sli_release_iocbq(phba, rspiocbq);
if (txbmp != NULL) { if (txbpl != NULL) { if (txbuffer != NULL)
diag_cmd_data_free(phba, txbuffer);
lpfc_mbuf_free(phba, txbmp->virt, txbmp->phys);
}
kfree(txbmp);
}
loopback_test_exit:
kfree(dataout); /* make error code available to userspace */
bsg_reply->result = rc;
job->dd_data = NULL; /* complete the job back to userspace if no error */ if (rc == IOCB_SUCCESS)
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len); return rc;
}
/** * lpfc_bsg_issue_mbox_cmpl - lpfc_bsg_issue_mbox mbox completion handler * @phba: Pointer to HBA context object. * @pmboxq: Pointer to mailbox command. * * This is completion handler function for mailbox commands issued from * lpfc_bsg_issue_mbox function. This function is called by the * mailbox event handler function with no lock held. This function * will wake up thread waiting on the wait queue pointed by dd_data * of the mailbox.
**/ staticvoid
lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{ struct bsg_job_data *dd_data; struct fc_bsg_reply *bsg_reply; struct bsg_job *job;
uint32_t size; unsignedlong flags;
uint8_t *pmb, *pmb_buf;
dd_data = pmboxq->ctx_u.dd_data;
/* * The outgoing buffer is readily referred from the dma buffer, * just need to get header part from mailboxq structure.
*/
pmb = (uint8_t *)&pmboxq->u.mb;
pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb;
memcpy(pmb_buf, pmb, sizeof(MAILBOX_t));
/* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
job = dd_data->set_job; if (job) { /* Prevent timeout handling from trying to abort job */
job->dd_data = NULL;
}
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* Copy the mailbox data to the job if it is still active */
/** * lpfc_bsg_check_cmd_access - test for a supported mailbox command * @phba: Pointer to HBA context object. * @mb: Pointer to a mailbox object. * @vport: Pointer to a vport object. * * Some commands require the port to be offline, some may not be called from * the application.
**/ staticint lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
MAILBOX_t *mb, struct lpfc_vport *vport)
{ /* return negative error values for bsg job */ switch (mb->mbxCommand) { /* Offline only */ case MBX_INIT_LINK: case MBX_DOWN_LINK: case MBX_CONFIG_LINK: case MBX_CONFIG_RING: case MBX_RESET_RING: case MBX_UNREG_LOGIN: case MBX_CLEAR_LA: case MBX_DUMP_CONTEXT: case MBX_RUN_DIAGS: case MBX_RESTART: case MBX_SET_MASK: if (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2743 Command 0x%x is illegal in on-line " "state\n",
mb->mbxCommand); return -EPERM;
} break; case MBX_WRITE_NV: case MBX_WRITE_VPARMS: case MBX_LOAD_SM: case MBX_READ_NV: case MBX_READ_CONFIG: case MBX_READ_RCONFIG: case MBX_READ_STATUS: case MBX_READ_XRI: case MBX_READ_REV: case MBX_READ_LNK_STAT: case MBX_DUMP_MEMORY: case MBX_DOWN_LOAD: case MBX_UPDATE_CFG: case MBX_KILL_BOARD: case MBX_READ_TOPOLOGY: case MBX_LOAD_AREA: case MBX_LOAD_EXP_ROM: case MBX_BEACON: case MBX_DEL_LD_ENTRY: case MBX_SET_DEBUG: case MBX_WRITE_WWN: case MBX_SLI4_CONFIG: case MBX_READ_EVENT_LOG: case MBX_READ_EVENT_LOG_STATUS: case MBX_WRITE_EVENT_LOG: case MBX_PORT_CAPABILITIES: case MBX_PORT_IOV_CONTROL: case MBX_RUN_BIU_DIAG64: break; case MBX_SET_VARIABLE:
lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "1226 mbox: set_variable 0x%x, 0x%x\n",
mb->un.varWords[0],
mb->un.varWords[1]); break; case MBX_READ_SPARM64: case MBX_REG_LOGIN: case MBX_REG_LOGIN64: case MBX_CONFIG_PORT: case MBX_RUN_BIU_DIAG: default:
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2742 Unknown Command 0x%x\n",
mb->mbxCommand); return -EPERM;
}
return 0; /* ok */
}
/** * lpfc_bsg_mbox_ext_session_reset - clean up context of multi-buffer mbox session * @phba: Pointer to HBA context object. * * This is routine clean up and reset BSG handling of multi-buffer mbox * command session.
**/ staticvoid
lpfc_bsg_mbox_ext_session_reset(struct lpfc_hba *phba)
{ if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_IDLE) return;
/* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
job = dd_data->set_job; if (job) {
bsg_reply = job->reply; /* Prevent timeout handling from trying to abort job */
job->dd_data = NULL;
}
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* * The outgoing buffer is readily referred from the dma buffer, * just need to get header part from mailboxq structure.
*/
pmb = (uint8_t *)&pmboxq->u.mb;
pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb; /* Copy the byte swapped response mailbox back to the user */
memcpy(pmb_buf, pmb, sizeof(MAILBOX_t)); /* if there is any non-embedded extended data copy that too */
dmabuf = phba->mbox_ext_buf_ctx.mbx_dmabuf;
sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt; if (!bsg_bf_get(lpfc_mbox_hdr_emb,
&sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr)) {
pmbx = (uint8_t *)dmabuf->virt; /* byte swap the extended data following the mailbox command */
lpfc_sli_pcimem_bcopy(&pmbx[sizeof(MAILBOX_t)],
&pmbx[sizeof(MAILBOX_t)],
sli_cfg_mbx->un.sli_config_emb0_subsys.mse[0].buf_len);
/* Special handling for non-embedded READ_OBJECT */
opcode = bsg_bf_get(lpfc_emb0_subcmnd_opcode,
&sli_cfg_mbx->un.sli_config_emb0_subsys); switch (opcode) { case COMN_OPCODE_READ_OBJECT: if (job) {
rc = lpfc_rd_obj_emb0_handle_job(phba, pmb_buf,
sli_cfg_mbx,
job,
bsg_reply);
bsg_reply->result = rc; goto done;
} break; default: break;
}
}
if (pmboxq->u.mb.mbxStatus || phba->mbox_ext_buf_ctx.numBuf == 1)
lpfc_bsg_mbox_ext_session_reset(phba);
/* free base driver mailbox structure memory */
mempool_free(pmboxq, phba->mbox_mem_pool);
/* if the job is still active, call job done */ if (job) {
bsg_reply = job->reply;
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
} return;
}
/** * lpfc_bsg_issue_write_mbox_ext_cmpl - cmpl handler for multi-buffer write mbox * @phba: Pointer to HBA context object. * @pmboxq: Pointer to mailbox command. * * This is completion handler function for mailbox write commands with multiple * external buffers.
**/ staticvoid
lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{ struct bsg_job *job; struct fc_bsg_reply *bsg_reply;
/* free all memory, including dma buffers */
mempool_free(pmboxq, phba->mbox_mem_pool);
lpfc_bsg_mbox_ext_session_reset(phba);
/* if the job is still active, call job done */ if (job) {
bsg_reply = job->reply;
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
}
/* Allocate hbds */ for (i = 0; i < hbd_cnt; i++) {
ext_dmabuf = lpfc_bsg_dma_page_alloc(phba); if (!ext_dmabuf) {
rc = -ENOMEM; goto job_error;
}
list_add_tail(&ext_dmabuf->list,
ext_dmabuf_list);
}
/* Fill out the physical memory addresses for the * hbds
*/
i = 0;
list_for_each_entry_safe(curr_dmabuf, next_dmabuf,
ext_dmabuf_list, list) {
emb0_subsys->hbd[i].pa_hi =
putPaddrHigh(curr_dmabuf->phys);
emb0_subsys->hbd[i].pa_lo =
putPaddrLow(curr_dmabuf->phys);
/* state change */
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
/* * Non-embedded mailbox subcommand data gets byte swapped here because * the lower level driver code only does the first 64 mailbox words.
*/ if ((!bsg_bf_get(lpfc_mbox_hdr_emb,
&sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr)) &&
(nemb_tp == nemb_mse))
lpfc_sli_pcimem_bcopy(&pmbx[sizeof(MAILBOX_t)],
&pmbx[sizeof(MAILBOX_t)],
sli_cfg_mbx->un.sli_config_emb0_subsys.
mse[0].buf_len);
/* state reset on not handled new multi-buffer mailbox command */ if (rc != SLI_CONFIG_HANDLED)
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_IDLE;
return rc;
}
/** * lpfc_bsg_mbox_ext_abort - request to abort mbox command with ext buffers * @phba: Pointer to HBA context object. * * This routine is for requesting to abort a pass-through mailbox command with * multiple external buffers due to error condition.
**/ staticvoid
lpfc_bsg_mbox_ext_abort(struct lpfc_hba *phba)
{ if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_PORT)
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS; else
lpfc_bsg_mbox_ext_session_reset(phba); return;
}
/** * lpfc_bsg_read_ebuf_get - get the next mailbox read external buffer * @phba: Pointer to HBA context object. * @job: Pointer to the job object. * * This routine extracts the next mailbox read external buffer back to * user space through BSG.
**/ staticint
lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct bsg_job *job)
{ struct fc_bsg_reply *bsg_reply = job->reply; struct lpfc_sli_config_mbox *sli_cfg_mbx; struct lpfc_dmabuf *dmabuf;
uint8_t *pbuf;
uint32_t size;
uint32_t index;
index = phba->mbox_ext_buf_ctx.seqNum;
phba->mbox_ext_buf_ctx.seqNum++;
/* set up external buffer descriptor and add to external buffer list */
lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, index,
phba->mbox_ext_buf_ctx.mbx_dmabuf,
dmabuf);
list_add_tail(&dmabuf->list, &phba->mbox_ext_buf_ctx.ext_dmabuf_list);
/** * lpfc_bsg_issue_mbox - issues a mailbox command on behalf of an app * @phba: Pointer to HBA context object. * @job: Pointer to the job object. * @vport: Pointer to a vport object. * * Allocate a tracking object, mailbox command memory, get a mailbox * from the mailbox pool, copy the caller mailbox command. * * If offline and the sli is active we need to poll for the command (port is * being reset) and complete the job, otherwise issue the mailbox command and * let our completion handler finish the command.
**/ staticint
lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct bsg_job *job, struct lpfc_vport *vport)
{ struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply;
LPFC_MBOXQ_t *pmboxq = NULL; /* internal mailbox queue */
MAILBOX_t *pmb; /* shortcut to the pmboxq mailbox */ /* a 4k buffer to hold the mb and extended data from/to the bsg */
uint8_t *pmbx = NULL; struct bsg_job_data *dd_data = NULL; /* bsg data tracking structure */ struct lpfc_dmabuf *dmabuf = NULL; struct dfc_mbox_req *mbox_req; struct READ_EVENT_LOG_VAR *rdEventLog;
uint32_t transmit_length, receive_length, mode; struct lpfc_mbx_sli4_config *sli4_config; struct lpfc_mbx_nembed_cmd *nembed_sge; struct ulp_bde64 *bde;
uint8_t *ext = NULL; int rc = 0;
uint8_t *from;
uint32_t size;
/* in case no data is transferred */
bsg_reply->reply_payload_rcv_len = 0;
/* sanity check to protect driver */ if (job->request_payload.payload_len > BSG_MBOX_SIZE) {
rc = -ERANGE; goto job_done;
}
/* * Don't allow mailbox commands to be sent when blocked or when in * the middle of discovery
*/ if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
rc = -EAGAIN; goto job_done;
}
/* If HBA encountered an error attention, allow only DUMP * or RESTART mailbox commands until the HBA is restarted.
*/ if (phba->pport->stopped &&
pmb->mbxCommand != MBX_DUMP_MEMORY &&
pmb->mbxCommand != MBX_RESTART &&
pmb->mbxCommand != MBX_WRITE_VPARMS &&
pmb->mbxCommand != MBX_WRITE_WWN)
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX, "2797 mbox: Issued mailbox cmd " "0x%x while in stopped state.\n",
pmb->mbxCommand);
/* extended mailbox commands will need an extended buffer */ if (mbox_req->inExtWLen || mbox_req->outExtWLen) {
from = pmbx;
ext = from + sizeof(MAILBOX_t);
pmboxq->ext_buf = ext;
pmboxq->in_ext_byte_len =
mbox_req->inExtWLen * sizeof(uint32_t);
pmboxq->out_ext_byte_len =
mbox_req->outExtWLen * sizeof(uint32_t);
pmboxq->mbox_offset_word = mbox_req->mbOffset;
}
/* biu diag will need a kernel buffer to transfer the data * allocate our own buffer and setup the mailbox command to * use ours
*/ if (pmb->mbxCommand == MBX_RUN_BIU_DIAG64) {
transmit_length = pmb->un.varWords[1];
receive_length = pmb->un.varWords[4]; /* transmit length cannot be greater than receive length or * mailbox extension size
*/ if ((transmit_length > receive_length) ||
(transmit_length > BSG_MBOX_SIZE - sizeof(MAILBOX_t))) {
rc = -ERANGE; goto job_done;
}
pmb->un.varBIUdiag.un.s2.xmit_bde64.addrHigh =
putPaddrHigh(dmabuf->phys + sizeof(MAILBOX_t));
pmb->un.varBIUdiag.un.s2.xmit_bde64.addrLow =
putPaddrLow(dmabuf->phys + sizeof(MAILBOX_t));
/* receive length cannot be greater than mailbox * extension size
*/ if (receive_length > BSG_MBOX_SIZE - sizeof(MAILBOX_t)) {
rc = -ERANGE; goto job_done;
}
/* mode zero uses a bde like biu diags command */ if (mode == 0) {
pmb->un.varWords[3] = putPaddrLow(dmabuf->phys
+ sizeof(MAILBOX_t));
pmb->un.varWords[4] = putPaddrHigh(dmabuf->phys
+ sizeof(MAILBOX_t));
}
} elseif (phba->sli_rev == LPFC_SLI_REV4) { /* Let type 4 (well known data) through because the data is * returned in varwords[4-8] * otherwise check the recieve length and fetch the buffer addr
*/ if ((pmb->mbxCommand == MBX_DUMP_MEMORY) &&
(pmb->un.varDmp.type != DMP_WELL_KNOWN)) { /* rebuild the command for sli4 using our own buffers * like we do for biu diags
*/
receive_length = pmb->un.varWords[2]; /* receive length cannot be greater than mailbox * extension size
*/ if (receive_length == 0) {
rc = -ERANGE; goto job_done;
}
pmb->un.varWords[3] = putPaddrLow(dmabuf->phys
+ sizeof(MAILBOX_t));
pmb->un.varWords[4] = putPaddrHigh(dmabuf->phys
+ sizeof(MAILBOX_t));
} elseif ((pmb->mbxCommand == MBX_UPDATE_CFG) &&
pmb->un.varUpdateCfg.co) {
bde = (struct ulp_bde64 *)&pmb->un.varWords[4];
/* bde size cannot be greater than mailbox ext size */ if (bde->tus.f.bdeSize >
BSG_MBOX_SIZE - sizeof(MAILBOX_t)) {
rc = -ERANGE; goto job_done;
}
bde->addrHigh = putPaddrHigh(dmabuf->phys
+ sizeof(MAILBOX_t));
bde->addrLow = putPaddrLow(dmabuf->phys
+ sizeof(MAILBOX_t));
} elseif (pmb->mbxCommand == MBX_SLI4_CONFIG) { /* Handling non-embedded SLI_CONFIG mailbox command */
sli4_config = &pmboxq->u.mqe.un.sli4_config; if (!bf_get(lpfc_mbox_hdr_emb,
&sli4_config->header.cfg_mhdr)) { /* rebuild the command for sli4 using our * own buffers like we do for biu diags
*/
nembed_sge = (struct lpfc_mbx_nembed_cmd *)
&pmb->un.varWords[0];
receive_length = nembed_sge->sge[0].length;
/* receive length cannot be greater than * mailbox extension size
*/ if ((receive_length == 0) ||
(receive_length >
BSG_MBOX_SIZE - sizeof(MAILBOX_t))) {
rc = -ERANGE; goto job_done;
}
/* job finished, copy the data */
memcpy(pmbx, pmb, sizeof(*pmb));
bsg_reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt,
pmbx, size); /* not waiting mbox already done */
rc = 0; goto job_done;
}
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) return 1; /* job started */
job_done: /* common exit for error or job completed inline */ if (pmboxq)
mempool_free(pmboxq, phba->mbox_mem_pool);
lpfc_bsg_dma_page_free(phba, dmabuf);
kfree(dd_data);
job_cont: return rc;
}
/** * lpfc_bsg_mbox_cmd - process an fc bsg LPFC_BSG_VENDOR_MBOX command * @job: MBOX fc_bsg_job for LPFC_BSG_VENDOR_MBOX.
**/ staticint
lpfc_bsg_mbox_cmd(struct bsg_job *job)
{ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job)); struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply; struct lpfc_hba *phba = vport->phba; struct dfc_mbox_req *mbox_req; int rc = 0;
/** * lpfc_check_fwlog_support: Check FW log support on the adapter * @phba: Pointer to HBA context object. * * Check if FW Logging support by the adapter
**/ int
lpfc_check_fwlog_support(struct lpfc_hba *phba)
{ struct lpfc_ras_fwlog *ras_fwlog = NULL;
/* Even though FW-logging is active re-initialize * FW-logging with new log-level. Return status * "Logging already Running" to caller.
**/
spin_lock_irq(&phba->ras_fwlog_lock); if (ras_fwlog->state != INACTIVE)
action_status = -EINPROGRESS;
spin_unlock_irq(&phba->ras_fwlog_lock);
/* Check if FW-logging is re-initialized */ if (action_status == -EINPROGRESS)
rc = action_status;
}
ras_job_error: /* make error code available to userspace */
bsg_reply->result = rc;
/* complete the job back to userspace */ if (!rc)
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
return rc;
}
/** * lpfc_bsg_get_ras_lwpd: Get log write position data * @job: fc_bsg_job to handle * * Get Offset/Wrap count of the log message written * in host memory
**/ staticint
lpfc_bsg_get_ras_lwpd(struct bsg_job *job)
{ struct Scsi_Host *shost = fc_bsg_to_shost(job); struct lpfc_vport *vport = shost_priv(shost); struct lpfc_bsg_get_ras_lwpd *ras_reply; struct lpfc_hba *phba = vport->phba; struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog; struct fc_bsg_reply *bsg_reply = job->reply;
u32 *lwpd_ptr = NULL; int rc = 0;
rc = lpfc_check_fwlog_support(phba); if (rc) goto ras_job_error;
/** * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job * @job: fc_bsg_job to handle
**/ staticint
lpfc_bsg_hst_vendor(struct bsg_job *job)
{ struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply; int command = bsg_request->rqst_data.h_vendor.vendor_cmd[0]; int rc;
switch (command) { case LPFC_BSG_VENDOR_SET_CT_EVENT:
rc = lpfc_bsg_hba_set_event(job); break; case LPFC_BSG_VENDOR_GET_CT_EVENT:
rc = lpfc_bsg_hba_get_event(job); break; case LPFC_BSG_VENDOR_SEND_MGMT_RESP:
rc = lpfc_bsg_send_mgmt_rsp(job); break; case LPFC_BSG_VENDOR_DIAG_MODE:
rc = lpfc_bsg_diag_loopback_mode(job); break; case LPFC_BSG_VENDOR_DIAG_MODE_END:
rc = lpfc_sli4_bsg_diag_mode_end(job); break; case LPFC_BSG_VENDOR_DIAG_RUN_LOOPBACK:
rc = lpfc_bsg_diag_loopback_run(job); break; case LPFC_BSG_VENDOR_LINK_DIAG_TEST:
rc = lpfc_sli4_bsg_link_diag_test(job); break; case LPFC_BSG_VENDOR_GET_MGMT_REV:
rc = lpfc_bsg_get_dfc_rev(job); break; case LPFC_BSG_VENDOR_MBOX:
rc = lpfc_bsg_mbox_cmd(job); break; case LPFC_BSG_VENDOR_FORCED_LINK_SPEED:
rc = lpfc_forced_link_speed(job); break; case LPFC_BSG_VENDOR_RAS_GET_LWPD:
rc = lpfc_bsg_get_ras_lwpd(job); break; case LPFC_BSG_VENDOR_RAS_GET_FWLOG:
rc = lpfc_bsg_get_ras_fwlog(job); break; case LPFC_BSG_VENDOR_RAS_GET_CONFIG:
rc = lpfc_bsg_get_ras_config(job); break; case LPFC_BSG_VENDOR_RAS_SET_CONFIG:
rc = lpfc_bsg_set_ras_config(job); break; case LPFC_BSG_VENDOR_GET_TRUNK_INFO:
rc = lpfc_get_trunk_info(job); break; case LPFC_BSG_VENDOR_GET_CGNBUF_INFO:
rc = lpfc_get_cgnbuf_info(job); break; default:
rc = -EINVAL;
bsg_reply->reply_payload_rcv_len = 0; /* make error code available to userspace */
bsg_reply->result = rc; break;
}
return rc;
}
/** * lpfc_bsg_request - handle a bsg request from the FC transport * @job: bsg_job to handle
**/ int
lpfc_bsg_request(struct bsg_job *job)
{ struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply;
uint32_t msgcode; int rc;
msgcode = bsg_request->msgcode; switch (msgcode) { case FC_BSG_HST_VENDOR:
rc = lpfc_bsg_hst_vendor(job); break; case FC_BSG_RPT_ELS:
rc = lpfc_bsg_rport_els(job); break; case FC_BSG_RPT_CT:
rc = lpfc_bsg_send_mgmt_cmd(job); break; default:
rc = -EINVAL;
bsg_reply->reply_payload_rcv_len = 0; /* make error code available to userspace */
bsg_reply->result = rc; break;
}
return rc;
}
/** * lpfc_bsg_timeout - handle timeout of a bsg request from the FC transport * @job: bsg_job that has timed out * * This function just aborts the job's IOCB. The aborted IOCB will return to * the waiting function which will handle passing the error back to userspace
**/ int
lpfc_bsg_timeout(struct bsg_job *job)
{ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job)); struct lpfc_hba *phba = vport->phba; struct lpfc_iocbq *cmdiocb; struct lpfc_sli_ring *pring; struct bsg_job_data *dd_data; unsignedlong flags; int rc = 0;
LIST_HEAD(completions); struct lpfc_iocbq *check_iocb, *next_iocb;
pring = lpfc_phba_elsring(phba); if (unlikely(!pring)) return -EIO;
/* if job's driver data is NULL, the command completed or is in the * the process of completing. In this case, return status to request * so the timeout is retried. This avoids double completion issues * and the request will be pulled off the timer queue when the * command's completion handler executes. Otherwise, prevent the * command's completion handler from executing the job done callback * and continue processing to abort the outstanding the command.
*/
switch (dd_data->type) { case TYPE_IOCB: /* Check to see if IOCB was issued to the port or not. If not, * remove it from the txq queue and call cancel iocbs. * Otherwise, call abort iotag
*/
cmdiocb = dd_data->context_un.iocb.cmdiocbq;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
spin_lock_irqsave(&phba->hbalock, flags); /* make sure the I/O abort window is still open */ if (!(cmdiocb->cmd_flag & LPFC_IO_CMD_OUTSTANDING)) {
spin_unlock_irqrestore(&phba->hbalock, flags); return -EAGAIN;
}
list_for_each_entry_safe(check_iocb, next_iocb, &pring->txq,
list) { if (check_iocb == cmdiocb) {
list_move_tail(&check_iocb->list, &completions); break;
}
} if (list_empty(&completions))
lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb, NULL);
spin_unlock_irqrestore(&phba->hbalock, flags); if (!list_empty(&completions)) {
lpfc_sli_cancel_iocbs(phba, &completions,
IOSTAT_LOCAL_REJECT,
IOERR_SLI_ABORTED);
} break;
case TYPE_EVT:
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); break;
case TYPE_MBOX: /* Update the ext buf ctx state if needed */
/* scsi transport fc fc_bsg_job_timeout expects a zero return code, * otherwise an error message will be displayed on the console * so always return success (zero)
*/ return rc;
}
Messung V0.5 in Prozent
¤ 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.0.138Bemerkung:
(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.