📄 mptscsih.c
字号:
/* * linux/drivers/message/fusion/mptscsih.c * For use with LSI Logic PCI chip/adapter(s) * running LSI Logic Fusion MPT (Message Passing Technology) firmware. * * Copyright (c) 1999-2005 LSI Logic Corporation * (mailto:mpt_linux_developer@lsil.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_compat.h" /* linux-2.6 tweaks */#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/sched.h>#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"/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/#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");/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/typedef struct _BIG_SENSE_BUF { u8 data[MPT_SENSE_BUFFER_ALLOC];} BIG_SENSE_BUF;#define MPT_SCANDV_GOOD (0x00000000) /* must be 0 */#define MPT_SCANDV_DID_RESET (0x00000001)#define MPT_SCANDV_SENSE (0x00000002)#define MPT_SCANDV_SOME_ERROR (0x00000004)#define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008)#define MPT_SCANDV_ISSUE_SENSE (0x00000010)#define MPT_SCANDV_FALLBACK (0x00000020)#define MPT_SCANDV_MAX_RETRIES (10)#define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */#define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */#define MPT_ICFLAG_EBOS 0x04 /* ReadBuffer Echo buffer has EBOS */#define MPT_ICFLAG_PHYS_DISK 0x08 /* Any SCSI IO but do Phys Disk Format */#define MPT_ICFLAG_TAGGED_CMD 0x10 /* Do tagged IO */#define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occurred with this command */#define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */typedef struct _internal_cmd { char *data; /* data pointer */ dma_addr_t data_dma; /* data dma address */ int size; /* transfer size */ u8 cmd; /* SCSI Op Code */ u8 bus; /* bus number */ u8 id; /* SCSI ID (virtual) */ u8 lun; u8 flags; /* Bit Field - See above */ u8 physDiskNum; /* Phys disk number, -1 else */ u8 rsvd2; u8 rsvd;} INTERNAL_CMD;typedef struct _negoparms { u8 width; u8 offset; u8 factor; u8 flags;} NEGOPARMS;typedef struct _dv_parameters { NEGOPARMS max; NEGOPARMS now; u8 cmd; u8 id; u16 pad1;} DVPARAMETERS;/* * Other private/forward protos... */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 u32 SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc);static int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout);static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 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);static void mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen);static void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56);static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq);static void mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags);static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id);static int mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target, int flags);static int mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus);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 int mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum);static struct work_struct mptscsih_persistTask;#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATIONstatic int mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io);static void mptscsih_domainValidation(void *hd);static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id);static void mptscsih_qas_check(MPT_SCSI_HOST *hd, int id);static int mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target);static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage);static void mptscsih_fillbuf(char *buffer, int size, int index, int width);static void mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id);#endifvoid 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)#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION/* * Domain Validation task structure */static DEFINE_SPINLOCK(dvtaskQ_lock);static int dvtaskQ_active = 0;static int dvtaskQ_release = 0;static struct work_struct dvTaskQ_task;#endif/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//** * 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((MYIOC_s_INFO_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((MYIOC_s_ERR_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((MYIOC_s_INFO_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. */ if ( (sges_left = SCpnt->use_sg) ) { sges_left = pci_map_sg(ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer, SCpnt->use_sg, SCpnt->sc_data_direction); if (sges_left == 0) return FAILED; } else if (SCpnt->request_bufflen) { SCpnt->SCp.dma_handle = pci_map_single(ioc->pcidev, SCpnt->request_buffer, SCpnt->request_bufflen, SCpnt->sc_data_direction); dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n", ioc->name, SCpnt, SCpnt->request_bufflen)); mptscsih_add_sge((char *) &pReq->SGL, 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen, SCpnt->SCp.dma_handle); return SUCCESS; } /* Handle the SG case. */ sg = (struct scatterlist *) SCpnt->request_buffer; 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 ++; /* Get next SG element from the OS */ sg_done++; continue; } v2 = sg_dma_address(sg); mptscsih_add_sge(psge, sgflags | thisxfer, v2); 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++; 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((MYIOC_s_INFO_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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -