lpfc_sli.c

来自「底层驱动开发」· C语言 代码 · 共 2,503 行 · 第 1/5 页

C
2,503
字号
/******************************************************************* * 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;/* * 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;}static uint32_tlpfc_sli_next_iotag(struct lpfc_hba * phba, struct lpfc_sli_ring * pring){	uint32_t search_start;	if (pring->fast_lookup == NULL) {		pring->iotag_ctr++;		if (pring->iotag_ctr >= pring->iotag_max)			pring->iotag_ctr = 1;		return pring->iotag_ctr;	}	search_start = pring->iotag_ctr;	do {		pring->iotag_ctr++;		if (pring->iotag_ctr >= pring->fast_iotag)			pring->iotag_ctr = 1;		if (*(pring->fast_lookup + pring->iotag_ctr) == NULL)			return pring->iotag_ctr;	} while (pring->iotag_ctr != search_start);	/*	 * Outstanding I/O count for ring <ringno> is at max <fast_iotag>	 */	lpfc_printf_log(phba,		KERN_ERR,		LOG_SLI,		"%d:0318 Outstanding I/O count for ring %d is at max x%x\n",		phba->brd_no,		pring->ringno,		pring->fast_iotag);	return (0);}static voidlpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,		IOCB_t *iocb, struct lpfc_iocbq *nextiocb){	/*	 * Allocate and set up an iotag	 */	nextiocb->iocb.ulpIoTag =		lpfc_sli_next_iotag(phba, &phba->sli.ring[phba->sli.fcp_ring]);	/*	 * 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 {		list_add_tail(&nextiocb->list, &phba->lpfc_iocb_list);	}	/*	 * Let the HBA know what IOCB slot will be the next one the	 * driver will put a command into.	 */	pring->cmdidx = pring->next_cmdidx;	writeb(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:	case MBX_READ_LA:	case MBX_CLEAR_LA:	case MBX_DUMP_MEMORY:	case MBX_DUMP_CONTEXT:	case MBX_RUN_DIAGS:	case MBX_RESTART:	case MBX_UPDATE_CFG:	case MBX_DOWN_LOAD:	case MBX_DEL_LD_ENTRY:	case MBX_RUN_PROGRAM:	case MBX_SET_MASK:	case MBX_SET_SLIM:	case MBX_UNREG_D_ID:	case MBX_CONFIG_FARP:	case MBX_LOAD_AREA:	case MBX_RUN_BIU_DIAG64:	case MBX_CONFIG_PORT:	case MBX_READ_SPARM64:	case MBX_READ_RPI64:	case MBX_REG_LOGIN64:	case MBX_READ_LA64:	case MBX_FLASH_WR_ULA:	case MBX_SET_DEBUG:	case MBX_LOAD_EXP_ROM:		ret = mbxCommand;		break;	default:		ret = MBX_SHUTDOWN;		break;	}	return (ret);}static voidlpfc_sli_wake_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq){	wait_queue_head_t *pdone_q;	/*	 * If pdone_q is empty, the driver thread gave up waiting and	 * continued running.	 */	pdone_q = (wait_queue_head_t *) pmboxq->context1;	if (pdone_q)		wake_up_interruptible(pdone_q);	return;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?