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

📄 mptscsih.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  linux/drivers/message/fusion/mptscsih.c *      For use with LSI PCI chip/adapter(s) *      running LSI Fusion MPT (Message Passing Technology) firmware. * *  Copyright (c) 1999-2007 LSI Corporation *  (mailto:DL-MPTFusionLinux@lsi.com) * *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//*    This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; version 2 of the License.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.    NO WARRANTY    THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR    CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT    LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is    solely responsible for determining the appropriateness of using and    distributing the Program and assumes all risks associated with its    exercise of rights under this Agreement, including but not limited to    the risks and costs of program errors, damage to or loss of data,    programs or equipment, and unavailability or interruption of operations.    DISCLAIMER OF LIABILITY    NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL    DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED    HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES    You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA*//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/kdev_t.h>#include <linux/blkdev.h>#include <linux/delay.h>	/* for mdelay */#include <linux/interrupt.h>	/* needed for in_interrupt() proto */#include <linux/reboot.h>	/* notifier code */#include <linux/workqueue.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_dbg.h>#include "mptbase.h"#include "mptscsih.h"#include "lsi/mpi_log_sas.h"/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/#define my_NAME		"Fusion MPT SCSI Host driver"#define my_VERSION	MPT_LINUX_VERSION_COMMON#define MYNAM		"mptscsih"MODULE_AUTHOR(MODULEAUTHOR);MODULE_DESCRIPTION(my_NAME);MODULE_LICENSE("GPL");MODULE_VERSION(my_VERSION);/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* *  Other private/forward protos... */static struct scsi_cmnd * mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);static struct scsi_cmnd * mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i);static void	mptscsih_set_scsi_lookup(MPT_ADAPTER *ioc, int i, struct scsi_cmnd *scmd);static int	SCPNT_TO_LOOKUP_IDX(MPT_ADAPTER *ioc, struct scsi_cmnd *scmd);int		mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);static void	mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);int		mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);static int	mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,				 SCSIIORequest_t *pReq, int req_idx);static void	mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx);static void	mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);static int	mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);static int	mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout );static int	mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout);int		mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);int		mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);int		mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);static int	mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);static void	mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice);void 		mptscsih_remove(struct pci_dev *);void 		mptscsih_shutdown(struct pci_dev *);#ifdef CONFIG_PMint 		mptscsih_suspend(struct pci_dev *pdev, pm_message_t state);int 		mptscsih_resume(struct pci_dev *pdev);#endif#define SNS_LEN(scp)	sizeof((scp)->sense_buffer)/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//** *	mptscsih_add_sge - Place a simple SGE at address pAddr. *	@pAddr: virtual address for SGE *	@flagslength: SGE flags and data transfer length *	@dma_addr: Physical address * *	This routine places a MPT request frame back on the MPT adapter's *	FreeQ. */static inline voidmptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr){	if (sizeof(dma_addr_t) == sizeof(u64)) {		SGESimple64_t *pSge = (SGESimple64_t *) pAddr;		u32 tmp = dma_addr & 0xFFFFFFFF;		pSge->FlagsLength = cpu_to_le32(flagslength);		pSge->Address.Low = cpu_to_le32(tmp);		tmp = (u32) ((u64)dma_addr >> 32);		pSge->Address.High = cpu_to_le32(tmp);	} else {		SGESimple32_t *pSge = (SGESimple32_t *) pAddr;		pSge->FlagsLength = cpu_to_le32(flagslength);		pSge->Address = cpu_to_le32(dma_addr);	}} /* mptscsih_add_sge() *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//** *	mptscsih_add_chain - Place a chain SGE at address pAddr. *	@pAddr: virtual address for SGE *	@next: nextChainOffset value (u32's) *	@length: length of next SGL segment *	@dma_addr: Physical address * *	This routine places a MPT request frame back on the MPT adapter's *	FreeQ. */static inline voidmptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr){	if (sizeof(dma_addr_t) == sizeof(u64)) {		SGEChain64_t *pChain = (SGEChain64_t *) pAddr;		u32 tmp = dma_addr & 0xFFFFFFFF;		pChain->Length = cpu_to_le16(length);		pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();		pChain->NextChainOffset = next;		pChain->Address.Low = cpu_to_le32(tmp);		tmp = (u32) ((u64)dma_addr >> 32);		pChain->Address.High = cpu_to_le32(tmp);	} else {		SGEChain32_t *pChain = (SGEChain32_t *) pAddr;		pChain->Length = cpu_to_le16(length);		pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();		pChain->NextChainOffset = next;		pChain->Address = cpu_to_le32(dma_addr);	}} /* mptscsih_add_chain() *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* *	mptscsih_getFreeChainBuffer - Function to get a free chain *	from the MPT_SCSI_HOST FreeChainQ. *	@ioc: Pointer to MPT_ADAPTER structure *	@req_idx: Index of the SCSI IO request frame. (output) * *	return SUCCESS or FAILED */static inline intmptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex){	MPT_FRAME_HDR *chainBuf;	unsigned long flags;	int rc;	int chain_idx;	dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "getFreeChainBuffer called\n",	    ioc->name));	spin_lock_irqsave(&ioc->FreeQlock, flags);	if (!list_empty(&ioc->FreeChainQ)) {		int offset;		chainBuf = list_entry(ioc->FreeChainQ.next, MPT_FRAME_HDR,				u.frame.linkage.list);		list_del(&chainBuf->u.frame.linkage.list);		offset = (u8 *)chainBuf - (u8 *)ioc->ChainBuffer;		chain_idx = offset / ioc->req_sz;		rc = SUCCESS;		dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT		    "getFreeChainBuffer chainBuf=%p ChainBuffer=%p offset=%d chain_idx=%d\n",		    ioc->name, chainBuf, ioc->ChainBuffer, offset, chain_idx));	} else {		rc = FAILED;		chain_idx = MPT_HOST_NO_CHAIN;		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "getFreeChainBuffer failed\n",		    ioc->name));	}	spin_unlock_irqrestore(&ioc->FreeQlock, flags);	*retIndex = chain_idx;	return rc;} /* mptscsih_getFreeChainBuffer() *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* *	mptscsih_AddSGE - Add a SGE (plus chain buffers) to the *	SCSIIORequest_t Message Frame. *	@ioc: Pointer to MPT_ADAPTER structure *	@SCpnt: Pointer to scsi_cmnd structure *	@pReq: Pointer to SCSIIORequest_t structure * *	Returns ... */static intmptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,		SCSIIORequest_t *pReq, int req_idx){	char 	*psge;	char	*chainSge;	struct scatterlist *sg;	int	 frm_sz;	int	 sges_left, sg_done;	int	 chain_idx = MPT_HOST_NO_CHAIN;	int	 sgeOffset;	int	 numSgeSlots, numSgeThisFrame;	u32	 sgflags, sgdir, thisxfer = 0;	int	 chain_dma_off = 0;	int	 newIndex;	int	 ii;	dma_addr_t v2;	u32	RequestNB;	sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK;	if (sgdir == MPI_SCSIIO_CONTROL_WRITE)  {		sgdir = MPT_TRANSFER_HOST_TO_IOC;	} else {		sgdir = MPT_TRANSFER_IOC_TO_HOST;	}	psge = (char *) &pReq->SGL;	frm_sz = ioc->req_sz;	/* Map the data portion, if any.	 * sges_left  = 0 if no data transfer.	 */	sges_left = scsi_dma_map(SCpnt);	if (sges_left < 0)		return FAILED;	/* Handle the SG case.	 */	sg = scsi_sglist(SCpnt);	sg_done  = 0;	sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);	chainSge = NULL;	/* Prior to entering this loop - the following must be set	 * current MF:  sgeOffset (bytes)	 *              chainSge (Null if original MF is not a chain buffer)	 *              sg_done (num SGE done for this MF)	 */nextSGEset:	numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );	numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;	sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;	/* Get first (num - 1) SG elements	 * Skip any SG entries with a length of 0	 * NOTE: at finish, sg and psge pointed to NEXT data/location positions	 */	for (ii=0; ii < (numSgeThisFrame-1); ii++) {		thisxfer = sg_dma_len(sg);		if (thisxfer == 0) {			sg = sg_next(sg); /* Get next SG element from the OS */			sg_done++;			continue;		}		v2 = sg_dma_address(sg);		mptscsih_add_sge(psge, sgflags | thisxfer, v2);		sg = sg_next(sg);	/* Get next SG element from the OS */		psge += (sizeof(u32) + sizeof(dma_addr_t));		sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));		sg_done++;	}	if (numSgeThisFrame == sges_left) {		/* Add last element, end of buffer and end of list flags.		 */		sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |				MPT_SGE_FLAGS_END_OF_BUFFER |				MPT_SGE_FLAGS_END_OF_LIST;		/* Add last SGE and set termination flags.		 * Note: Last SGE may have a length of 0 - which should be ok.		 */		thisxfer = sg_dma_len(sg);		v2 = sg_dma_address(sg);		mptscsih_add_sge(psge, sgflags | thisxfer, v2);		/*		sg = sg_next(sg);		psge += (sizeof(u32) + sizeof(dma_addr_t));		*/		sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));		sg_done++;		if (chainSge) {			/* The current buffer is a chain buffer,			 * but there is not another one.			 * Update the chain element			 * Offset and Length fields.			 */			mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);		} else {			/* The current buffer is the original MF			 * and there is no Chain buffer.			 */			pReq->ChainOffset = 0;			RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor)  + 1) & 0x03;			dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT			    "Single Buffer RequestNB=%x, sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));			ioc->RequestNB[req_idx] = RequestNB;		}	} else {		/* At least one chain buffer is needed.		 * Complete the first MF		 *  - last SGE element, set the LastElement bit		 *  - set ChainOffset (words) for orig MF		 *             (OR finish previous MF chain buffer)		 *  - update MFStructPtr ChainIndex		 *  - Populate chain element		 * Also		 * Loop until done.		 */		dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SG: Chain Required! sg done %d\n",				ioc->name, sg_done));		/* Set LAST_ELEMENT flag for last non-chain element		 * in the buffer. Since psge points at the NEXT		 * SGE element, go back one SGE element, update the flags		 * and reset the pointer. (Note: sgflags & thisxfer are already		 * set properly).		 */		if (sg_done) {			u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));			sgflags = le32_to_cpu(*ptmp);			sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;			*ptmp = cpu_to_le32(sgflags);		}		if (chainSge) {			/* The current buffer is a chain buffer.			 * chainSge points to the previous Chain Element.			 * Update its chain element Offset and Length (must			 * include chain element size) fields.			 * Old chain element is now complete.			 */			u8 nextChain = (u8) (sgeOffset >> 2);			sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));			mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);		} else {			/* The original MF buffer requires a chain buffer -			 * set the offset.			 * Last element in this MF is a chain element.			 */			pReq->ChainOffset = (u8) (sgeOffset >> 2);			RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor)  + 1) & 0x03;			dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Chain Buffer Needed, RequestNB=%x sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));			ioc->RequestNB[req_idx] = RequestNB;		}		sges_left -= sg_done;		/* NOTE: psge points to the beginning of the chain element		 * in current buffer. Get a chain buffer.		 */		if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) {			dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT			    "getFreeChainBuffer FAILED SCSI cmd=%02x (%p)\n", 			    ioc->name, pReq->CDB[0], SCpnt));			return FAILED;		}		/* Update the tracking arrays.		 * If chainSge == NULL, update ReqToChain, else ChainToChain		 */		if (chainSge) {			ioc->ChainToChain[chain_idx] = newIndex;		} else {			ioc->ReqToChain[req_idx] = newIndex;		}		chain_idx = newIndex;		chain_dma_off = ioc->req_sz * chain_idx;		/* Populate the chainSGE for the current buffer.		 * - Set chain buffer pointer to psge and fill		 *   out the Address and Flags fields.		 */		chainSge = (char *) psge;		dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "  Current buff @ %p (index 0x%x)",		    ioc->name, psge, req_idx));		/* Start the SGE for the next buffer		 */		psge = (char *) (ioc->ChainBuffer + chain_dma_off);		sgeOffset = 0;		sg_done = 0;		dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "  Chain buff @ %p (index 0x%x)\n",		    ioc->name, psge, chain_idx));		/* Start the SGE for the next buffer		 */		goto nextSGEset;	}	return SUCCESS;} /* mptscsih_AddSGE() */static voidmptscsih_issue_sep_command(MPT_ADAPTER *ioc, VirtTarget *vtarget,    U32 SlotStatus){	MPT_FRAME_HDR *mf;	SEPRequest_t 	 *SEPMsg;

⌨️ 快捷键说明

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