📄 lpfc_sli.c
字号:
/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2004-2005 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.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/blkdev.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_transport_fc.h>#include "lpfc_hw.h"#include "lpfc_sli.h"#include "lpfc_disc.h"#include "lpfc_scsi.h"#include "lpfc.h"#include "lpfc_crtn.h"#include "lpfc_logmsg.h"#include "lpfc_compat.h"/* * Define macro to log: Mailbox command x%x cannot issue Data * This allows multiple uses of lpfc_msgBlk0311 * w/o perturbing log msg utility. */#define LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) \ lpfc_printf_log(phba, \ KERN_INFO, \ LOG_MBOX | LOG_SLI, \ "%d:0311 Mailbox command x%x cannot issue " \ "Data: x%x x%x x%x\n", \ phba->brd_no, \ mb->mbxCommand, \ phba->hba_state, \ psli->sli_flag, \ flag);/* There are only four IOCB completion types. */typedef enum _lpfc_iocb_type { LPFC_UNKNOWN_IOCB, LPFC_UNSOL_IOCB, LPFC_SOL_IOCB, LPFC_ABORT_IOCB} lpfc_iocb_type;struct lpfc_iocbq *lpfc_sli_get_iocbq(struct lpfc_hba * phba){ struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list; struct lpfc_iocbq * iocbq = NULL; list_remove_head(lpfc_iocb_list, iocbq, struct lpfc_iocbq, list); return iocbq;}voidlpfc_sli_release_iocbq(struct lpfc_hba * phba, struct lpfc_iocbq * iocbq){ size_t start_clean = (size_t)(&((struct lpfc_iocbq *)NULL)->iocb); /* * Clean all volatile data fields, preserve iotag and node struct. */ memset((char*)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean); list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);}/* * Translate the iocb command to an iocb command type used to decide the final * disposition of each completed IOCB. */static lpfc_iocb_typelpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd){ lpfc_iocb_type type = LPFC_UNKNOWN_IOCB; if (iocb_cmnd > CMD_MAX_IOCB_CMD) return 0; switch (iocb_cmnd) { case CMD_XMIT_SEQUENCE_CR: case CMD_XMIT_SEQUENCE_CX: case CMD_XMIT_BCAST_CN: case CMD_XMIT_BCAST_CX: case CMD_ELS_REQUEST_CR: case CMD_ELS_REQUEST_CX: case CMD_CREATE_XRI_CR: case CMD_CREATE_XRI_CX: case CMD_GET_RPI_CN: case CMD_XMIT_ELS_RSP_CX: case CMD_GET_RPI_CR: case CMD_FCP_IWRITE_CR: case CMD_FCP_IWRITE_CX: case CMD_FCP_IREAD_CR: case CMD_FCP_IREAD_CX: case CMD_FCP_ICMND_CR: case CMD_FCP_ICMND_CX: case CMD_ADAPTER_MSG: case CMD_ADAPTER_DUMP: case CMD_XMIT_SEQUENCE64_CR: case CMD_XMIT_SEQUENCE64_CX: case CMD_XMIT_BCAST64_CN: case CMD_XMIT_BCAST64_CX: case CMD_ELS_REQUEST64_CR: case CMD_ELS_REQUEST64_CX: case CMD_FCP_IWRITE64_CR: case CMD_FCP_IWRITE64_CX: case CMD_FCP_IREAD64_CR: case CMD_FCP_IREAD64_CX: case CMD_FCP_ICMND64_CR: case CMD_FCP_ICMND64_CX: case CMD_GEN_REQUEST64_CR: case CMD_GEN_REQUEST64_CX: case CMD_XMIT_ELS_RSP64_CX: type = LPFC_SOL_IOCB; break; case CMD_ABORT_XRI_CN: case CMD_ABORT_XRI_CX: case CMD_CLOSE_XRI_CN: case CMD_CLOSE_XRI_CX: case CMD_XRI_ABORTED_CX: case CMD_ABORT_MXRI64_CN: type = LPFC_ABORT_IOCB; break; case CMD_RCV_SEQUENCE_CX: case CMD_RCV_ELS_REQ_CX: case CMD_RCV_SEQUENCE64_CX: case CMD_RCV_ELS_REQ64_CX: type = LPFC_UNSOL_IOCB; break; default: type = LPFC_UNKNOWN_IOCB; break; } return type;}static intlpfc_sli_ring_map(struct lpfc_hba * phba, LPFC_MBOXQ_t *pmb){ struct lpfc_sli *psli = &phba->sli; MAILBOX_t *pmbox = &pmb->mb; int i, rc; for (i = 0; i < psli->num_rings; i++) { phba->hba_state = LPFC_INIT_MBX_CMDS; lpfc_config_ring(phba, i, pmb); rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); if (rc != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "%d:0446 Adapter failed to init, " "mbxCmd x%x CFG_RING, mbxStatus x%x, " "ring %d\n", phba->brd_no, pmbox->mbxCommand, pmbox->mbxStatus, i); phba->hba_state = LPFC_HBA_ERROR; return -ENXIO; } } return 0;}static intlpfc_sli_ringtxcmpl_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocb){ uint16_t iotag; list_add_tail(&piocb->list, &pring->txcmplq); pring->txcmplq_cnt++; if (unlikely(pring->ringno == LPFC_ELS_RING)) mod_timer(&phba->els_tmofunc, jiffies + HZ * (phba->fc_ratov << 1)); if (pring->fast_lookup) { /* Setup fast lookup based on iotag for completion */ iotag = piocb->iocb.ulpIoTag; if (iotag && (iotag < pring->fast_iotag)) *(pring->fast_lookup + iotag) = piocb; else { /* Cmd ring <ringno> put: iotag <iotag> greater then configured max <fast_iotag> wd0 <icmd> */ lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "%d:0316 Cmd ring %d put: iotag x%x " "greater then configured max x%x " "wd0 x%x\n", phba->brd_no, pring->ringno, iotag, pring->fast_iotag, *(((uint32_t *)(&piocb->iocb)) + 7)); } } return (0);}static struct lpfc_iocbq *lpfc_sli_ringtx_get(struct lpfc_hba * phba, struct lpfc_sli_ring * pring){ struct list_head *dlp; struct lpfc_iocbq *cmd_iocb; dlp = &pring->txq; cmd_iocb = NULL; list_remove_head((&pring->txq), cmd_iocb, struct lpfc_iocbq, list); if (cmd_iocb) { /* If the first ptr is not equal to the list header, * deque the IOCBQ_t and return it. */ pring->txq_cnt--; } return (cmd_iocb);}static IOCB_t *lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring){ struct lpfc_pgp *pgp = &phba->slim2p->mbx.us.s2.port[pring->ringno]; uint32_t max_cmd_idx = pring->numCiocb; IOCB_t *iocb = NULL; if ((pring->next_cmdidx == pring->cmdidx) && (++pring->next_cmdidx >= max_cmd_idx)) pring->next_cmdidx = 0; if (unlikely(pring->local_getidx == pring->next_cmdidx)) { pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); if (unlikely(pring->local_getidx >= max_cmd_idx)) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "%d:0315 Ring %d issue: portCmdGet %d " "is bigger then cmd ring %d\n", phba->brd_no, pring->ringno, pring->local_getidx, max_cmd_idx); phba->hba_state = LPFC_HBA_ERROR; /* * All error attention handlers are posted to * worker thread */ phba->work_ha |= HA_ERATT; phba->work_hs = HS_FFER3; if (phba->work_wait) wake_up(phba->work_wait); return NULL; } if (pring->local_getidx == pring->next_cmdidx) return NULL; } iocb = IOCB_ENTRY(pring->cmdringaddr, pring->cmdidx); return iocb;}uint16_tlpfc_sli_next_iotag(struct lpfc_hba * phba, struct lpfc_iocbq * iocbq){ struct lpfc_iocbq ** new_arr; struct lpfc_iocbq ** old_arr; size_t new_len; struct lpfc_sli *psli = &phba->sli; uint16_t iotag; spin_lock_irq(phba->host->host_lock); iotag = psli->last_iotag; if(++iotag < psli->iocbq_lookup_len) { psli->last_iotag = iotag; psli->iocbq_lookup[iotag] = iocbq; spin_unlock_irq(phba->host->host_lock); iocbq->iotag = iotag; return iotag; } else if (psli->iocbq_lookup_len < (0xffff - LPFC_IOCBQ_LOOKUP_INCREMENT)) { new_len = psli->iocbq_lookup_len + LPFC_IOCBQ_LOOKUP_INCREMENT; spin_unlock_irq(phba->host->host_lock); new_arr = kmalloc(new_len * sizeof (struct lpfc_iocbq *), GFP_KERNEL); if (new_arr) { memset((char *)new_arr, 0, new_len * sizeof (struct lpfc_iocbq *)); spin_lock_irq(phba->host->host_lock); old_arr = psli->iocbq_lookup; if (new_len <= psli->iocbq_lookup_len) { /* highly unprobable case */ kfree(new_arr); iotag = psli->last_iotag; if(++iotag < psli->iocbq_lookup_len) { psli->last_iotag = iotag; psli->iocbq_lookup[iotag] = iocbq; spin_unlock_irq(phba->host->host_lock); iocbq->iotag = iotag; return iotag; } spin_unlock_irq(phba->host->host_lock); return 0; } if (psli->iocbq_lookup) memcpy(new_arr, old_arr, ((psli->last_iotag + 1) * sizeof (struct lpfc_iocbq *))); psli->iocbq_lookup = new_arr; psli->iocbq_lookup_len = new_len; psli->last_iotag = iotag; psli->iocbq_lookup[iotag] = iocbq; spin_unlock_irq(phba->host->host_lock); iocbq->iotag = iotag; kfree(old_arr); return iotag; } } lpfc_printf_log(phba, KERN_ERR,LOG_SLI, "%d:0318 Failed to allocate IOTAG.last IOTAG is %d\n", phba->brd_no, psli->last_iotag); return 0;}static voidlpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, IOCB_t *iocb, struct lpfc_iocbq *nextiocb){ /* * Set up an iotag */ nextiocb->iocb.ulpIoTag = (nextiocb->iocb_cmpl) ? nextiocb->iotag : 0; /* * Issue iocb command to adapter */ lpfc_sli_pcimem_bcopy(&nextiocb->iocb, iocb, sizeof (IOCB_t)); wmb(); pring->stats.iocb_cmd++; /* * If there is no completion routine to call, we can release the * IOCB buffer back right now. For IOCBs, like QUE_RING_BUF, * that have no rsp ring completion, iocb_cmpl MUST be NULL. */ if (nextiocb->iocb_cmpl) lpfc_sli_ringtxcmpl_put(phba, pring, nextiocb); else lpfc_sli_release_iocbq(phba, nextiocb); /* * Let the HBA know what IOCB slot will be the next one the * driver will put a command into. */ pring->cmdidx = pring->next_cmdidx; writel(pring->cmdidx, phba->MBslimaddr + (SLIMOFF + (pring->ringno * 2)) * 4);}static voidlpfc_sli_update_full_ring(struct lpfc_hba * phba, struct lpfc_sli_ring *pring){ int ringno = pring->ringno; pring->flag |= LPFC_CALL_RING_AVAILABLE; wmb(); /* * Set ring 'ringno' to SET R0CE_REQ in Chip Att register. * The HBA will tell us when an IOCB entry is available. */ writel((CA_R0ATT|CA_R0CE_REQ) << (ringno*4), phba->CAregaddr); readl(phba->CAregaddr); /* flush */ pring->stats.iocb_cmd_full++;}static voidlpfc_sli_update_ring(struct lpfc_hba * phba, struct lpfc_sli_ring *pring){ int ringno = pring->ringno; /* * Tell the HBA that there is work to do in this ring. */ wmb(); writel(CA_R0ATT << (ringno * 4), phba->CAregaddr); readl(phba->CAregaddr); /* flush */}static voidlpfc_sli_resume_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring){ IOCB_t *iocb; struct lpfc_iocbq *nextiocb; /* * Check to see if: * (a) there is anything on the txq to send * (b) link is up * (c) link attention events can be processed (fcp ring only) * (d) IOCB processing is not blocked by the outstanding mbox command. */ if (pring->txq_cnt && (phba->hba_state > LPFC_LINK_DOWN) && (pring->ringno != phba->sli.fcp_ring || phba->sli.sli_flag & LPFC_PROCESS_LA) && !(pring->flag & LPFC_STOP_IOCB_MBX)) { while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) && (nextiocb = lpfc_sli_ringtx_get(phba, pring))) lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb); if (iocb) lpfc_sli_update_ring(phba, pring); else lpfc_sli_update_full_ring(phba, pring); } return;}/* lpfc_sli_turn_on_ring is only called by lpfc_sli_handle_mb_event below */static voidlpfc_sli_turn_on_ring(struct lpfc_hba * phba, int ringno){ struct lpfc_pgp *pgp = &phba->slim2p->mbx.us.s2.port[ringno]; /* If the ring is active, flag it */ if (phba->sli.ring[ringno].cmdringaddr) { if (phba->sli.ring[ringno].flag & LPFC_STOP_IOCB_MBX) { phba->sli.ring[ringno].flag &= ~LPFC_STOP_IOCB_MBX; /* * Force update of the local copy of cmdGetInx */ phba->sli.ring[ringno].local_getidx = le32_to_cpu(pgp->cmdGetInx); spin_lock_irq(phba->host->host_lock); lpfc_sli_resume_iocb(phba, &phba->sli.ring[ringno]); spin_unlock_irq(phba->host->host_lock); } }}static intlpfc_sli_chk_mbx_command(uint8_t mbxCommand){ uint8_t ret; switch (mbxCommand) { case MBX_LOAD_SM: case MBX_READ_NV: case MBX_WRITE_NV: case MBX_RUN_BIU_DIAG: case MBX_INIT_LINK: case MBX_DOWN_LINK: case MBX_CONFIG_LINK: case MBX_CONFIG_RING: case MBX_RESET_RING: case MBX_READ_CONFIG: case MBX_READ_RCONFIG: case MBX_READ_SPARM: case MBX_READ_STATUS: case MBX_READ_RPI: case MBX_READ_XRI: case MBX_READ_REV: case MBX_READ_LNK_STAT: case MBX_REG_LOGIN: case MBX_UNREG_LOGIN:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -