lpfc_sli.c
来自「linux 内核源代码」· C语言 代码 · 共 2,495 行 · 第 1/5 页
C
2,495 行
/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2004-2007 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"#include "lpfc_debugfs.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, pmbox, 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", \ pmbox->vport ? pmbox->vport->vpi : 0, \ pmbox->mb.mbxCommand, \ phba->pport->port_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; /* SLI-2/SLI-3 provide different sized iocbs. Given a pointer * to the start of the ring, and the slot number of the * desired iocb entry, calc a pointer to that entry. */static inline IOCB_t *lpfc_cmd_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring){ return (IOCB_t *) (((char *) pring->cmdringaddr) + pring->cmdidx * phba->iocb_cmd_size);}static inline IOCB_t *lpfc_resp_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring){ return (IOCB_t *) (((char *) pring->rspringaddr) + pring->rspidx * phba->iocb_rsp_size);}static 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;}struct lpfc_iocbq *lpfc_sli_get_iocbq(struct lpfc_hba *phba){ struct lpfc_iocbq * iocbq = NULL; unsigned long iflags; spin_lock_irqsave(&phba->hbalock, iflags); iocbq = __lpfc_sli_get_iocbq(phba); spin_unlock_irqrestore(&phba->hbalock, iflags); return iocbq;}void__lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq){ size_t start_clean = offsetof(struct lpfc_iocbq, 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);}voidlpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq){ unsigned long iflags; /* * Clean all volatile data fields, preserve iotag and node struct. */ spin_lock_irqsave(&phba->hbalock, iflags); __lpfc_sli_release_iocbq(phba, iocbq); spin_unlock_irqrestore(&phba->hbalock, iflags);}/* * 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_FCP_TSEND_CX: case CMD_FCP_TRSP_CX: case CMD_FCP_TRECEIVE_CX: case CMD_FCP_AUTO_TRSP_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_FCP_TSEND64_CX: case CMD_FCP_TRSP64_CX: case CMD_FCP_TRECEIVE64_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: case CMD_IOCB_RCV_SEQ64_CX: case CMD_IOCB_RCV_ELS64_CX: case CMD_IOCB_RCV_CONT64_CX: type = LPFC_UNSOL_IOCB; break; default: type = LPFC_UNKNOWN_IOCB; break; } return type;}static intlpfc_sli_ring_map(struct lpfc_hba *phba){ struct lpfc_sli *psli = &phba->sli; LPFC_MBOXQ_t *pmb; MAILBOX_t *pmbox; int i, rc, ret = 0; pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmb) return -ENOMEM; pmbox = &pmb->mb; phba->link_state = LPFC_INIT_MBX_CMDS; for (i = 0; i < psli->num_rings; i++) { 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, "0446 Adapter failed to init (%d), " "mbxCmd x%x CFG_RING, mbxStatus x%x, " "ring %d\n", rc, pmbox->mbxCommand, pmbox->mbxStatus, i); phba->link_state = LPFC_HBA_ERROR; ret = -ENXIO; break; } } mempool_free(pmb, phba->mbox_mem_pool); return ret;}static intlpfc_sli_ringtxcmpl_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct lpfc_iocbq *piocb){ list_add_tail(&piocb->list, &pring->txcmplq); pring->txcmplq_cnt++; if ((unlikely(pring->ringno == LPFC_ELS_RING)) && (piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) && (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN)) { if (!piocb->vport) BUG(); else mod_timer(&piocb->vport->els_tmofunc, jiffies + HZ * (phba->fc_ratov << 1)); } return 0;}static struct lpfc_iocbq *lpfc_sli_ringtx_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring){ struct lpfc_iocbq *cmd_iocb; list_remove_head((&pring->txq), cmd_iocb, struct lpfc_iocbq, list); if (cmd_iocb != NULL) 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->sli_rev == 3) ? &phba->slim2p->mbx.us.s3_pgp.port[pring->ringno] : &phba->slim2p->mbx.us.s2.port[pring->ringno]; uint32_t max_cmd_idx = pring->numCiocb; 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, "0315 Ring %d issue: portCmdGet %d " "is bigger then cmd ring %d\n", pring->ringno, pring->local_getidx, max_cmd_idx); phba->link_state = LPFC_HBA_ERROR; /* * All error attention handlers are posted to * worker thread */ phba->work_ha |= HA_ERATT; phba->work_hs = HS_FFER3; /* hbalock should already be held */ if (phba->work_wait) lpfc_worker_wake_up(phba); return NULL; } if (pring->local_getidx == pring->next_cmdidx) return NULL; } return lpfc_cmd_iocb(phba, pring);}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->hbalock); iotag = psli->last_iotag; if(++iotag < psli->iocbq_lookup_len) { psli->last_iotag = iotag; psli->iocbq_lookup[iotag] = iocbq; spin_unlock_irq(&phba->hbalock); 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->hbalock); new_arr = kzalloc(new_len * sizeof (struct lpfc_iocbq *), GFP_KERNEL); if (new_arr) { spin_lock_irq(&phba->hbalock); 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->hbalock); iocbq->iotag = iotag; return iotag; } spin_unlock_irq(&phba->hbalock); 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->hbalock); iocbq->iotag = iotag; kfree(old_arr); return iotag; } } else spin_unlock_irq(&phba->hbalock); lpfc_printf_log(phba, KERN_ERR,LOG_SLI, "0318 Failed to allocate IOTAG.last IOTAG is %d\n", 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; if (pring->ringno == LPFC_ELS_RING) { lpfc_debugfs_slow_ring_trc(phba, "IOCB cmd ring: wd4:x%08x wd6:x%08x wd7:x%08x", *(((uint32_t *) &nextiocb->iocb) + 4), *(((uint32_t *) &nextiocb->iocb) + 6), *(((uint32_t *) &nextiocb->iocb) + 7)); } /* * Issue iocb command to adapter */ lpfc_sli_pcimem_bcopy(&nextiocb->iocb, iocb, phba->iocb_cmd_size); 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->host_gp[pring->ringno].cmdPutInx);}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 && lpfc_is_link_up(phba) && (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->sli_rev == 3) ? &phba->slim2p->mbx.us.s3_pgp.port[ringno] : &phba->slim2p->mbx.us.s2.port[ringno]; unsigned long iflags;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?