diff options
author | James Smart <jsmart2021@gmail.com> | 2018-09-10 20:30:50 +0300 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2018-09-12 03:37:33 +0300 |
commit | d2cc9bcd7fa30b6c2270c044ff6dc9e839bf779e (patch) | |
tree | 70c980415dc98ad098354c750d45ecbf89a4b0ec /drivers/scsi/lpfc/lpfc_sli.c | |
parent | 18027a8ccca5b9189f49be0d8b84dbc717084a26 (diff) | |
download | linux-d2cc9bcd7fa30b6c2270c044ff6dc9e839bf779e.tar.xz |
scsi: lpfc: add support to retrieve firmware logs
This patch adds the ability to read firmware logs from the adapter. The driver
registers a buffer with the adapter that is then written to by the adapter.
The adapter posts CQEs to indicate content updates in the buffer. While the
adapter is writing to the buffer in a circular fashion, an application will
poll the driver to read the next amount of log data from the buffer.
Driver log buffer size is configurable via the ras_fwlog_buffsize sysfs
attribute. Verbosity to be used by firmware when logging to host memory is
controlled through the ras_fwlog_level attribute. The ras_fwlog_func
attribute enables or disables loggy by firmware.
Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: James Smart <james.smart@broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_sli.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 99fddd056675..c378230d03ad 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -6149,6 +6149,271 @@ lpfc_set_features(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox, } /** + * lpfc_sli4_ras_dma_free - Free memory allocated for FW logging. + * @phba: Pointer to HBA context object. + * + * This function is called to free memory allocated for RAS FW logging + * support in the driver. + **/ +void +lpfc_sli4_ras_dma_free(struct lpfc_hba *phba) +{ + struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog; + struct lpfc_dmabuf *dmabuf, *next; + + if (!list_empty(&ras_fwlog->fwlog_buff_list)) { + list_for_each_entry_safe(dmabuf, next, + &ras_fwlog->fwlog_buff_list, + list) { + list_del(&dmabuf->list); + dma_free_coherent(&phba->pcidev->dev, + LPFC_RAS_MAX_ENTRY_SIZE, + dmabuf->virt, dmabuf->phys); + kfree(dmabuf); + } + } + + if (ras_fwlog->lwpd.virt) { + dma_free_coherent(&phba->pcidev->dev, + sizeof(uint32_t) * 2, + ras_fwlog->lwpd.virt, + ras_fwlog->lwpd.phys); + ras_fwlog->lwpd.virt = NULL; + } + + ras_fwlog->ras_active = false; +} + +/** + * lpfc_sli4_ras_dma_alloc: Allocate memory for FW support + * @phba: Pointer to HBA context object. + * @fwlog_buff_count: Count of buffers to be created. + * + * This routine DMA memory for Log Write Position Data[LPWD] and buffer + * to update FW log is posted to the adapter. + * Buffer count is calculated based on module param ras_fwlog_buffsize + * Size of each buffer posted to FW is 64K. + **/ + +static int +lpfc_sli4_ras_dma_alloc(struct lpfc_hba *phba, + uint32_t fwlog_buff_count) +{ + struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog; + struct lpfc_dmabuf *dmabuf; + int rc = 0, i = 0; + + /* Initialize List */ + INIT_LIST_HEAD(&ras_fwlog->fwlog_buff_list); + + /* Allocate memory for the LWPD */ + ras_fwlog->lwpd.virt = dma_alloc_coherent(&phba->pcidev->dev, + sizeof(uint32_t) * 2, + &ras_fwlog->lwpd.phys, + GFP_KERNEL); + if (!ras_fwlog->lwpd.virt) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "6185 LWPD Memory Alloc Failed\n"); + + return -ENOMEM; + } + + ras_fwlog->fw_buffcount = fwlog_buff_count; + for (i = 0; i < ras_fwlog->fw_buffcount; i++) { + dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), + GFP_KERNEL); + if (!dmabuf) { + rc = -ENOMEM; + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "6186 Memory Alloc failed FW logging"); + goto free_mem; + } + + dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev, + LPFC_RAS_MAX_ENTRY_SIZE, + &dmabuf->phys, + GFP_KERNEL); + if (!dmabuf->virt) { + kfree(dmabuf); + rc = -ENOMEM; + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "6187 DMA Alloc Failed FW logging"); + goto free_mem; + } + memset(dmabuf->virt, 0, LPFC_RAS_MAX_ENTRY_SIZE); + dmabuf->buffer_tag = i; + list_add_tail(&dmabuf->list, &ras_fwlog->fwlog_buff_list); + } + +free_mem: + if (rc) + lpfc_sli4_ras_dma_free(phba); + + return rc; +} + +/** + * lpfc_sli4_ras_mbox_cmpl: Completion handler for RAS MBX command + * @phba: pointer to lpfc hba data structure. + * @pmboxq: pointer to the driver internal queue element for mailbox command. + * + * Completion handler for driver's RAS MBX command to the device. + **/ +static void +lpfc_sli4_ras_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) +{ + MAILBOX_t *mb; + union lpfc_sli4_cfg_shdr *shdr; + uint32_t shdr_status, shdr_add_status; + struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog; + + mb = &pmb->u.mb; + + shdr = (union lpfc_sli4_cfg_shdr *) + &pmb->u.mqe.un.ras_fwlog.header.cfg_shdr; + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); + + if (mb->mbxStatus != MBX_SUCCESS || shdr_status) { + lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX, + "6188 FW LOG mailbox " + "completed with status x%x add_status x%x," + " mbx status x%x\n", + shdr_status, shdr_add_status, mb->mbxStatus); + goto disable_ras; + } + + ras_fwlog->ras_active = true; + mempool_free(pmb, phba->mbox_mem_pool); + + return; + +disable_ras: + /* Free RAS DMA memory */ + lpfc_sli4_ras_dma_free(phba); + mempool_free(pmb, phba->mbox_mem_pool); +} + +/** + * lpfc_sli4_ras_fwlog_init: Initialize memory and post RAS MBX command + * @phba: pointer to lpfc hba data structure. + * @fwlog_level: Logging verbosity level. + * @fwlog_enable: Enable/Disable logging. + * + * Initialize memory and post mailbox command to enable FW logging in host + * memory. + **/ +int +lpfc_sli4_ras_fwlog_init(struct lpfc_hba *phba, + uint32_t fwlog_level, + uint32_t fwlog_enable) +{ + struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog; + struct lpfc_mbx_set_ras_fwlog *mbx_fwlog = NULL; + struct lpfc_dmabuf *dmabuf; + LPFC_MBOXQ_t *mbox; + uint32_t len = 0, fwlog_buffsize, fwlog_entry_count; + int rc = 0; + + fwlog_buffsize = (LPFC_RAS_MIN_BUFF_POST_SIZE * + phba->cfg_ras_fwlog_buffsize); + fwlog_entry_count = (fwlog_buffsize/LPFC_RAS_MAX_ENTRY_SIZE); + + /* + * If re-enabling FW logging support use earlier allocated + * DMA buffers while posting MBX command. + **/ + if (!ras_fwlog->lwpd.virt) { + rc = lpfc_sli4_ras_dma_alloc(phba, fwlog_entry_count); + if (rc) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "6189 RAS FW Log Support Not Enabled"); + return rc; + } + } + + /* Setup Mailbox command */ + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "6190 RAS MBX Alloc Failed"); + rc = -ENOMEM; + goto mem_free; + } + + ras_fwlog->fw_loglevel = fwlog_level; + len = (sizeof(struct lpfc_mbx_set_ras_fwlog) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + + lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_LOWLEVEL, + LPFC_MBOX_OPCODE_SET_DIAG_LOG_OPTION, + len, LPFC_SLI4_MBX_EMBED); + + mbx_fwlog = (struct lpfc_mbx_set_ras_fwlog *)&mbox->u.mqe.un.ras_fwlog; + bf_set(lpfc_fwlog_enable, &mbx_fwlog->u.request, + fwlog_enable); + bf_set(lpfc_fwlog_loglvl, &mbx_fwlog->u.request, + ras_fwlog->fw_loglevel); + bf_set(lpfc_fwlog_buffcnt, &mbx_fwlog->u.request, + ras_fwlog->fw_buffcount); + bf_set(lpfc_fwlog_buffsz, &mbx_fwlog->u.request, + LPFC_RAS_MAX_ENTRY_SIZE/SLI4_PAGE_SIZE); + + /* Update DMA buffer address */ + list_for_each_entry(dmabuf, &ras_fwlog->fwlog_buff_list, list) { + memset(dmabuf->virt, 0, LPFC_RAS_MAX_ENTRY_SIZE); + + mbx_fwlog->u.request.buff_fwlog[dmabuf->buffer_tag].addr_lo = + putPaddrLow(dmabuf->phys); + + mbx_fwlog->u.request.buff_fwlog[dmabuf->buffer_tag].addr_hi = + putPaddrHigh(dmabuf->phys); + } + + /* Update LPWD address */ + mbx_fwlog->u.request.lwpd.addr_lo = putPaddrLow(ras_fwlog->lwpd.phys); + mbx_fwlog->u.request.lwpd.addr_hi = putPaddrHigh(ras_fwlog->lwpd.phys); + + mbox->vport = phba->pport; + mbox->mbox_cmpl = lpfc_sli4_ras_mbox_cmpl; + + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); + + if (rc == MBX_NOT_FINISHED) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "6191 RAS Mailbox failed. " + "status %d mbxStatus : x%x", rc, + bf_get(lpfc_mqe_status, &mbox->u.mqe)); + mempool_free(mbox, phba->mbox_mem_pool); + rc = -EIO; + goto mem_free; + } else + rc = 0; +mem_free: + if (rc) + lpfc_sli4_ras_dma_free(phba); + + return rc; +} + +/** + * lpfc_sli4_ras_setup - Check if RAS supported on the adapter + * @phba: Pointer to HBA context object. + * + * Check if RAS is supported on the adapter and initialize it. + **/ +void +lpfc_sli4_ras_setup(struct lpfc_hba *phba) +{ + /* Check RAS FW Log needs to be enabled or not */ + if (lpfc_check_fwlog_support(phba)) + return; + + lpfc_sli4_ras_fwlog_init(phba, phba->cfg_ras_fwlog_level, + LPFC_RAS_ENABLE_LOGGING); +} + +/** * lpfc_sli4_alloc_resource_identifiers - Allocate all SLI4 resource extents. * @phba: Pointer to HBA context object. * |