/******************************************************************* * 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;
--> --------------------
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.