⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lpfc_sli.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/******************************************************************* * 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 + -