📄 mptscsih.c
字号:
/* * linux/drivers/message/fusion/mptscsih.c * High performance SCSI / Fibre Channel SCSI Host device driver. * For use with PCI chip/adapter(s): * LSIFC9xx/LSI409xx Fibre Channel * running LSI Logic Fusion MPT (Message Passing Technology) firmware. * * Credits: * This driver would not exist if not for Alan Cox's development * of the linux i2o driver. * * A special thanks to Pamela Delaney (LSI Logic) for tons of work * and countless enhancements while adding support for the 1030 * chip family. Pam has been instrumental in the development of * of the 2.xx.xx series fusion drivers, and her contributions are * far too numerous to hope to list in one place. * * A huge debt of gratitude is owed to David S. Miller (DaveM) * for fixing much of the stupid and broken stuff in the early * driver while porting to sparc64 platform. THANK YOU! * * (see mptbase.c) * * Copyright (c) 1999-2004 LSI Logic Corporation * Original author: Steven J. Ralston * (mailto:sjralston1@netscape.net) * (mailto:mpt_linux_developer@lsil.com) * * $Id: mptscsih.c,v 1.1.2.4 2003/05/07 14:08:34 pdelaney Exp $ *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* 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/version.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/kdev_t.h>#include <linux/blkdev.h>#include <linux/blk.h> /* for io_request_lock (spinlock) decl */#include <linux/delay.h> /* for mdelay */#include <linux/interrupt.h> /* needed for in_interrupt() proto */#include <linux/reboot.h> /* notifier code */#include "../../scsi/scsi.h"#include "../../scsi/hosts.h"#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45)#include "../../scsi/sd.h"#endif#include "mptbase.h"#include "mptscsih.h"#include "isense.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");/* Set string for command line args from insmod */#ifdef MODULEchar *mptscsih = 0;MODULE_PARM(mptscsih, "s");#endif/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/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_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_PHYS_DISK 0x04 /* Any SCSI IO but do Phys Disk Format */#define MPT_ICFLAG_TAGGED_CMD 0x08 /* Do tagged IO */#define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occured 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... */static int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);static void mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);static int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);static int mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt, SCSIIORequest_t *pReq, int req_idx);static void mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx);static int mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init);static void copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);#ifndef MPT_SCSI_USE_NEW_EHstatic void search_taskQ_for_cmd(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd);#elsestatic int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);#endifstatic u32 SCPNT_TO_LOOKUP_IDX(Scsi_Cmnd *sc);static MPT_FRAME_HDR *mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx);static void post_pendingQ_commands(MPT_SCSI_HOST *hd);static int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag);static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag);static int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);static 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);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);static int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);static void mptscsih_timer_expired(unsigned long data);static void mptscsih_taskmgmt_timeout(unsigned long data);static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);static int mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum);#ifndef MPTSCSIH_DISABLE_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);#endifstatic int mptscsih_setup(char *str);static int mptscsih_halt(struct notifier_block *nb, ulong event, void *buf);/* * Reboot Notification */static struct notifier_block mptscsih_notifier = { mptscsih_halt, NULL, 0};/* * Private data... */static int mpt_scsi_hosts = 0;static int ScsiDoneCtx = -1;static int ScsiTaskCtx = -1;static int ScsiScanDvCtx = -1; /* Used only for bus scan and dv */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,28)static struct proc_dir_entry proc_mpt_scsihost ={ .low_ino = PROC_SCSI_MPT, .namelen = 8, .name = "mptscsih", .mode = S_IFDIR | S_IRUGO | S_IXUGO, .nlink = 2,};#endif#define SNS_LEN(scp) sizeof((scp)->sense_buffer)#ifndef MPT_SCSI_USE_NEW_EH/* * Stuff to handle single-threading SCSI TaskMgmt * (abort/reset) requests... */static spinlock_t mytaskQ_lock = SPIN_LOCK_UNLOCKED;static int mytaskQ_bh_active = 0;static struct mpt_work_struct mptscsih_ptaskfoo;static atomic_t mpt_taskQdepth;#endif#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION/* * Domain Validation task structure */static spinlock_t dvtaskQ_lock = SPIN_LOCK_UNLOCKED;static int dvtaskQ_active = 0;static int dvtaskQ_release = 0;static struct mpt_work_struct mptscsih_dvTask;#endif/* * Wait Queue setup */static DECLARE_WAIT_QUEUE_HEAD (scandv_waitq);static int scandv_wait_done = 1;/* Driver default setup */static struct mptscsih_driver_setup driver_setup = MPTSCSIH_DRIVER_SETUP;#ifdef MPTSCSIH_DBG_TIMEOUTstatic Scsi_Cmnd *foo_to[8];#endif/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* * Private inline routines... *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* 19991030 -sralston * Return absolute SCSI data direction: * 1 = _DATA_OUT * 0 = _DIR_NONE * -1 = _DATA_IN * * Changed: 3-20-2002 pdelaney to use the default data * direction and the defines set up in the * 2.4 kernel series * 1 = _DATA_OUT changed to SCSI_DATA_WRITE (1) * 0 = _DIR_NONE changed to SCSI_DATA_NONE (3) * -1 = _DATA_IN changed to SCSI_DATA_READ (2) * If the direction is unknown, fall through to original code. * * Mid-layer bug fix(): sg interface generates the wrong data * direction in some cases. Set the direction the hard way for * the most common commands. */static inline intmptscsih_io_direction(Scsi_Cmnd *cmd){ switch (cmd->cmnd[0]) { case WRITE_6: case WRITE_10: return SCSI_DATA_WRITE; break; case READ_6: case READ_10: return SCSI_DATA_READ; break; }#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) if (cmd->sc_data_direction != SCSI_DATA_UNKNOWN) return cmd->sc_data_direction;#endif switch (cmd->cmnd[0]) { /* _DATA_OUT commands */ case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: case WRITE_VERIFY: case WRITE_VERIFY_12: case COMPARE: case COPY: case COPY_VERIFY: case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: case REASSIGN_BLOCKS: case PERSISTENT_RESERVE_OUT: case 0xea: case 0xa3: return SCSI_DATA_WRITE; /* No data transfer commands */ case SEEK_6: case SEEK_10: case RESERVE: case RELEASE: case TEST_UNIT_READY: case START_STOP: case ALLOW_MEDIUM_REMOVAL: return SCSI_DATA_NONE; /* Conditional data transfer commands */ case FORMAT_UNIT: if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */ return SCSI_DATA_WRITE; else return SCSI_DATA_NONE; case VERIFY: if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */ return SCSI_DATA_WRITE; else return SCSI_DATA_NONE; case RESERVE_10: if (cmd->cmnd[1] & 0x03) /* RESERVE:{LongID|Extent} (data out phase)? */ return SCSI_DATA_WRITE; else return SCSI_DATA_NONE; /* Must be data _IN! */ default: return SCSI_DATA_READ; }} /* mptscsih_io_direction() *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//** * 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_getFreeChainBuffes - Function to get a free chain * from the MPT_SCSI_HOST FreeChainQ. * @hd: Pointer to the MPT_SCSI_HOST instance * @req_idx: Index of the SCSI IO request frame. (output) * * return SUCCESS or FAILED */static inline intmptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex){ MPT_FRAME_HDR *chainBuf; unsigned long flags; int rc; int chain_idx; spin_lock_irqsave(&hd->ioc->FreeQlock, flags); if (!Q_IS_EMPTY(&hd->FreeChainQ)) { int offset; chainBuf = hd->FreeChainQ.head; Q_DEL_ITEM(&chainBuf->u.frame.linkage); offset = (u8 *)chainBuf - (u8 *)hd->ChainBuffer; chain_idx = offset / hd->ioc->req_sz; rc = SUCCESS; } else { rc = FAILED; chain_idx = MPT_HOST_NO_CHAIN; } spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); *retIndex = chain_idx; dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n", hd->ioc->name, *retIndex, chainBuf)); return rc;} /* mptscsih_getFreeChainBuffer() *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -