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 + -
显示快捷键?