/* * linux/drivers/message/fusion/mptctl.c * mpt Ioctl driver. * For use with LSI PCI chip/adapters * running LSI Fusion MPT (Message Passing Technology) firmware. * * Copyright (c) 1999-2008 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/kernel.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/delay.h> /* for mdelay */#include <linux/miscdevice.h>#include <linux/mutex.h>#include <linux/compat.h> #include <asm/io.h>#include <linux/uaccess.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> #define COPYRIGHT "Copyright (c) 1999-2008 LSI Corporation"#define MODULEAUTHOR "LSI Corporation"#include "mptbase.h"#include "mptctl.h" /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/#define my_NAME "Fusion MPT misc device (ioctl) driver"#define my_VERSION MPT_LINUX_VERSION_COMMON#define MYNAM "mptctl" MODULE_AUTHOR(MODULEAUTHOR);MODULE_DESCRIPTION(my_NAME);MODULE_LICENSE("GPL");MODULE_VERSION(my_VERSION); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static DEFINE_MUTEX(mpctl_mutex);static u8 mptctl_id = MPT_MAX_PROTOCOL_DRIVERS;static u8 mptctl_taskmgmt_id = MPT_MAX_PROTOCOL_DRIVERS; static DECLARE_WAIT_QUEUE_HEAD ( mptctl_wait ); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ struct buflist { u8 *kptr; int len;}; /* * Function prototypes. Called from OS entry point mptctl_ioctl. * arg contents specific to function. */static int mptctl_fw_download(MPT_ADAPTER *iocp, unsigned long arg);static int mptctl_getiocinfo(MPT_ADAPTER *iocp, unsigned long arg, unsigned int cmd);static int mptctl_gettargetinfo(MPT_ADAPTER *iocp, unsigned long arg);static int mptctl_readtest(MPT_ADAPTER *iocp, unsigned long arg);static int mptctl_mpt_command(MPT_ADAPTER *iocp, unsigned long arg);static int mptctl_eventquery(MPT_ADAPTER *iocp, unsigned long arg);static int mptctl_eventenable(MPT_ADAPTER *iocp, unsigned long arg);static int mptctl_eventreport(MPT_ADAPTER *iocp, unsigned long arg);static int mptctl_replace_fw(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_do_reset(MPT_ADAPTER *iocp, unsigned long arg);static int mptctl_hp_hostinfo(MPT_ADAPTER *iocp, unsigned long arg, unsigned int cmd);static int mptctl_hp_targetinfo(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_probe(struct pci_dev *);static void mptctl_remove(struct pci_dev *); #ifdef CONFIG_COMPATstatic long compat_mpctl_ioctl(struct file *f, unsigned cmd, unsigned long arg);#endif/* * Private function calls. */static int mptctl_do_mpt_command(MPT_ADAPTER *iocp, struct mpt_ioctl_command karg, void __user *mfPtr);static int mptctl_do_fw_download(MPT_ADAPTER *iocp, char __user *ufwbuf, size_t fwlen);static MptSge_t *kbuf_alloc_2_sgl(int bytes, u32 dir, int sge_offset, int *frags, struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc);static void kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTER *ioc); /* * Reset Handler cleanup function */static int mptctl_ioc_reset(MPT_ADAPTER *ioc, int reset_phase); /* * Event Handler function */static int mptctl_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);static struct fasync_struct *async_queue=NULL; /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* * Scatter gather list (SGL) sizes and limits... *///#define MAX_SCSI_FRAGS 9#define MAX_FRAGS_SPILL1 9#define MAX_FRAGS_SPILL2 15#define FRAGS_PER_BUCKET (MAX_FRAGS_SPILL2 + 1) //#define MAX_CHAIN_FRAGS 64//#define MAX_CHAIN_FRAGS (15+15+15+16)#define MAX_CHAIN_FRAGS (4 * MAX_FRAGS_SPILL2 + 1) // Define max sg LIST bytes ( == (#frags + #chains) * 8 bytes each)// Works out to: 592d bytes! (9+1)*8 + 4*(15+1)*8// ^----------------- 80 + 512#define MAX_SGL_BYTES ((MAX_FRAGS_SPILL1 + 1 + (4 * FRAGS_PER_BUCKET)) * 8) /* linux only seems to ever give 128kB MAX contiguous (GFP_USER) mem bytes */#define MAX_KMALLOC_SZ (128*1024) #define MPT_IOCTL_DEFAULT_TIMEOUT 10 /* Default timeout value (seconds) */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//** * mptctl_syscall_down - Down the MPT adapter syscall semaphore. * @ioc: Pointer to MPT adapter * @nonblock: boolean, non-zero if O_NONBLOCK is set * * All of the ioctl commands can potentially sleep, which is illegal * with a spinlock held, thus we perform mutual exclusion here. * * Returns negative errno on error, or zero for success. */static inline intmptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock){ int rc = 0; if (nonblock) { if (!mutex_trylock(&ioc->ioctl_cmds.mutex)) rc = -EAGAIN; } else { if (mutex_lock_interruptible(&ioc->ioctl_cmds.mutex)) rc = -ERESTARTSYS; } return rc;} /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* * This is the callback for any message we have posted. The message itself * will be returned to the message pool when we return from the IRQ * * This runs in irq context so be short and sweet. */static intmptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply){ char *sense_data; int req_index; int sz; if (!req) return 0; dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "completing mpi function " "(0x%02X), req=%p, reply=%p\n", ioc->name, req->u.hdr.Function, req, reply)); /* * Handling continuation of the same reply. Processing the first * reply, and eating the other replys that come later. */ if (ioc->ioctl_cmds.msg_context != req->u.hdr.MsgContext) goto out_continuation; ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD; if (!reply) goto out; ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_RF_VALID; sz = min(ioc->reply_sz, 4*reply->u.reply.MsgLength); memcpy(ioc->ioctl_cmds.reply, reply, sz); if (reply->u.reply.IOCStatus || reply->u.reply.IOCLogInfo) dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "iocstatus (0x%04X), loginfo (0x%08X)\n", ioc->name, le16_to_cpu(reply->u.reply.IOCStatus), le32_to_cpu(reply->u.reply.IOCLogInfo))); if ((req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) || (req->u.hdr.Function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { if (reply->u.sreply.SCSIStatus || reply->u.sreply.SCSIState) dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "scsi_status (0x%02x), scsi_state (0x%02x), " "tag = (0x%04x), transfer_count (0x%08x)\n", ioc->name, reply->u.sreply.SCSIStatus, reply->u.sreply.SCSIState, le16_to_cpu(reply->u.sreply.TaskTag), le32_to_cpu(reply->u.sreply.TransferCount))); if (reply->u.sreply.SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { sz = req->u.scsireq.SenseBufferLength; req_index = le16_to_cpu(req->u.frame.hwhdr.msgctxu.fld.req_idx); sense_data = ((u8 *)ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC)); memcpy(ioc->ioctl_cmds.sense, sense_data, sz); ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_SENSE_VALID; } } out: /* We are done, issue wake up */ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_PENDING) { if (req->u.hdr.Function == MPI_FUNCTION_SCSI_TASK_MGMT) { mpt_clear_taskmgmt_in_progress_flag(ioc); ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING; complete(&ioc->ioctl_cmds.done); if (ioc->bus_type == SAS) ioc->schedule_target_reset(ioc); } else { ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING; complete(&ioc->ioctl_cmds.done); } } out_continuation: if (reply && (reply->u.reply.MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) return 0; return 1;} static intmptctl_taskmgmt_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr){ if (!mf) return 0; dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed (mf=%p, mr=%p)\n", ioc->name, mf, mr)); ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD; if (!mr) goto out; ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID; memcpy(ioc->taskmgmt_cmds.reply, mr, min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength)); out: if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) { mpt_clear_taskmgmt_in_progress_flag(ioc); ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING; complete(&ioc->taskmgmt_cmds.done); if (ioc->bus_type == SAS) ioc->schedule_target_reset(ioc); return 1; } return 0;} static intmptctl_do_taskmgmt(MPT_ADAPTER *ioc, u8 tm_type, u8 bus_id, u8 target_id){ MPT_FRAME_HDR *mf; SCSITaskMgmt_t *pScsiTm; SCSITaskMgmtReply_t *pScsiTmReply; int ii; int retval; unsigned long timeout; u16 iocstatus; mutex_lock(&ioc->taskmgmt_cmds.mutex); if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) { mutex_unlock(&ioc->taskmgmt_cmds.mutex); return -EPERM; } retval = 0; mf = mpt_get_msg_frame(mptctl_taskmgmt_id, ioc); if (mf == NULL) { dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "TaskMgmt, no msg frames!!\n", ioc->name)); mpt_clear_taskmgmt_in_progress_flag(ioc); retval = -ENOMEM; goto tm_done; } dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n", ioc->name, mf)); pScsiTm = (SCSITaskMgmt_t *) mf; memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t)); pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; pScsiTm->TaskType = tm_type; if ((tm_type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (ioc->bus_type == FC)) pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION; pScsiTm->TargetID = target_id; pScsiTm->Bus = bus_id; pScsiTm->ChainOffset = 0; pScsiTm->Reserved = 0; pScsiTm->Reserved1 = 0; pScsiTm->TaskMsgContext = 0; for (ii= 0; ii < 8; ii++) pScsiTm->LUN[ii] = 0; for (ii=0; ii < 7; ii++) pScsiTm->Reserved2[ii] = 0; switch (ioc->bus_type) { case FC: timeout = 40; break; case SAS: timeout = 30; break; case SPI: default: timeout = 10; break; } dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt type=%d timeout=%ld\n", ioc->name, tm_type, timeout)); INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status) if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) && (ioc->facts.MsgVersion >= MPI_VERSION_01_05)) mpt_put_msg_frame_hi_pri(mptctl_taskmgmt_id, ioc, mf); else { retval = mpt_send_handshake_request(mptctl_taskmgmt_id, ioc, sizeof(SCSITaskMgmt_t), (u32 *)pScsiTm, CAN_SLEEP); if (retval != 0) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "TaskMgmt send_handshake FAILED!" " (ioc %p, mf %p, rc=%d) \n", ioc->name, ioc, mf, retval)); mpt_free_msg_frame(ioc, mf); mpt_clear_taskmgmt_in_progress_flag(ioc); goto tm_done; } } /* Now wait for the command to complete */ ii = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done, timeout*HZ); if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt failed\n", ioc->name)); mpt_free_msg_frame(ioc, mf); mpt_clear_taskmgmt_in_progress_flag(ioc); if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) retval = 0; else retval = -1; /* return failure */ goto tm_done; } if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) { dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt failed\n", ioc->name)); retval = -1; /* return failure */ goto tm_done; } pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply; dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt fw_channel = %d, fw_id = %d, task_type=0x%02X, " "iocstatus=0x%04X\n\tloginfo=0x%08X, response_code=0x%02X, " "term_cmnds=%d\n", ioc->name, pScsiTmReply->Bus, pScsiTmReply->TargetID, tm_type, le16_to_cpu(pScsiTmReply->IOCStatus), le32_to_cpu(pScsiTmReply->IOCLogInfo), pScsiTmReply->ResponseCode, le32_to_cpu(pScsiTmReply->TerminationCount))); iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK; if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED || iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED || iocstatus == MPI_IOCSTATUS_SUCCESS) retval = 0; else { dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt failed\n", ioc->name)); retval = -1; /* return failure */ } tm_done: mutex_unlock(&ioc->taskmgmt_cmds.mutex); CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status) return retval;} /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* mptctl_timeout_expired * * Expecting an interrupt, however timed out. * */static voidmptctl_timeout_expired(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf){ unsigned long flags; int ret_val = -1; SCSIIORequest_t *scsi_req = (SCSIIORequest_t *) mf; u8 function = mf->u.hdr.Function; dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": %s\n", ioc->name, __func__)); if (mpt_fwfault_debug) mpt_halt_firmware(ioc); spin_lock_irqsave(&ioc->taskmgmt_lock, flags); if (ioc->ioc_reset_in_progress) { spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) mpt_free_msg_frame(ioc, mf); return; } spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) if (ioc->bus_type == SAS) { if (function == MPI_FUNCTION_SCSI_IO_REQUEST) ret_val = mptctl_do_taskmgmt(ioc, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, scsi_req->Bus, scsi_req->TargetID); else if (function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) ret_val = mptctl_do_taskmgmt(ioc, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, scsi_req->Bus, 0); if (!ret_val) return; } else { if ((function == MPI_FUNCTION_SCSI_IO_REQUEST) || (function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) ret_val = mptctl_do_taskmgmt(ioc,Lines 1–500 of 2953. Load more
/* * linux/drivers/message/fusion/mptctl.c * mpt Ioctl driver. * For use with LSI PCI chip/adapters * running LSI Fusion MPT (Message Passing Technology) firmware. * * Copyright (c) 1999-2008 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/kernel.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/pci.h> #include <linux/delay.h> /* for mdelay */ #include <linux/miscdevice.h> #include <linux/mutex.h> #include <linux/compat.h> #include <asm/io.h> #include <linux/uaccess.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> #define COPYRIGHT "Copyright (c) 1999-2008 LSI Corporation" #define MODULEAUTHOR "LSI Corporation" #include "mptbase.h" #include "mptctl.h" /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #define my_NAME "Fusion MPT misc device (ioctl) driver" #define my_VERSION MPT_LINUX_VERSION_COMMON #define MYNAM "mptctl" MODULE_AUTHOR(MODULEAUTHOR); MODULE_DESCRIPTION(my_NAME); MODULE_LICENSE("GPL"); MODULE_VERSION(my_VERSION); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static DEFINE_MUTEX(mpctl_mutex); static u8 mptctl_id = MPT_MAX_PROTOCOL_DRIVERS; static u8 mptctl_taskmgmt_id = MPT_MAX_PROTOCOL_DRIVERS; static DECLARE_WAIT_QUEUE_HEAD ( mptctl_wait ); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ struct buflist { u8 *kptr; int len; }; /* * Function prototypes. Called from OS entry point mptctl_ioctl. * arg contents specific to function. */ static int mptctl_fw_download(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_getiocinfo(MPT_ADAPTER *iocp, unsigned long arg, unsigned int cmd); static int mptctl_gettargetinfo(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_readtest(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_mpt_command(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_eventquery(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_eventenable(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_eventreport(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_replace_fw(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_do_reset(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_hp_hostinfo(MPT_ADAPTER *iocp, unsigned long arg, unsigned int cmd); static int mptctl_hp_targetinfo(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_probe(struct pci_dev *); static void mptctl_remove(struct pci_dev *); #ifdef CONFIG_COMPAT static long compat_mpctl_ioctl(struct file *f, unsigned cmd, unsigned long arg); #endif /* * Private function calls. */ static int mptctl_do_mpt_command(MPT_ADAPTER *iocp, struct mpt_ioctl_command karg, void __user *mfPtr); static int mptctl_do_fw_download(MPT_ADAPTER *iocp, char __user *ufwbuf, size_t fwlen); static MptSge_t *kbuf_alloc_2_sgl(int bytes, u32 dir, int sge_offset, int *frags, struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc); static void kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTER *ioc); /* * Reset Handler cleanup function */ static int mptctl_ioc_reset(MPT_ADAPTER *ioc, int reset_phase); /* * Event Handler function */ static int mptctl_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); static struct fasync_struct *async_queue=NULL; /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * Scatter gather list (SGL) sizes and limits... */ //#define MAX_SCSI_FRAGS 9 #define MAX_FRAGS_SPILL1 9 #define MAX_FRAGS_SPILL2 15 #define FRAGS_PER_BUCKET (MAX_FRAGS_SPILL2 + 1) //#define MAX_CHAIN_FRAGS 64 //#define MAX_CHAIN_FRAGS (15+15+15+16) #define MAX_CHAIN_FRAGS (4 * MAX_FRAGS_SPILL2 + 1) // Define max sg LIST bytes ( == (#frags + #chains) * 8 bytes each) // Works out to: 592d bytes! (9+1)*8 + 4*(15+1)*8 // ^----------------- 80 + 512 #define MAX_SGL_BYTES ((MAX_FRAGS_SPILL1 + 1 + (4 * FRAGS_PER_BUCKET)) * 8) /* linux only seems to ever give 128kB MAX contiguous (GFP_USER) mem bytes */ #define MAX_KMALLOC_SZ (128*1024) #define MPT_IOCTL_DEFAULT_TIMEOUT 10 /* Default timeout value (seconds) */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * mptctl_syscall_down - Down the MPT adapter syscall semaphore. * @ioc: Pointer to MPT adapter * @nonblock: boolean, non-zero if O_NONBLOCK is set * * All of the ioctl commands can potentially sleep, which is illegal * with a spinlock held, thus we perform mutual exclusion here. * * Returns negative errno on error, or zero for success. */ static inline int mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock) { int rc = 0; if (nonblock) { if (!mutex_trylock(&ioc->ioctl_cmds.mutex)) rc = -EAGAIN; } else { if (mutex_lock_interruptible(&ioc->ioctl_cmds.mutex)) rc = -ERESTARTSYS; } return rc; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * This is the callback for any message we have posted. The message itself * will be returned to the message pool when we return from the IRQ * * This runs in irq context so be short and sweet. */ static int mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply) { char *sense_data; int req_index; int sz; if (!req) return 0; dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "completing mpi function " "(0x%02X), req=%p, reply=%p\n", ioc->name, req->u.hdr.Function, req, reply)); /* * Handling continuation of the same reply. Processing the first * reply, and eating the other replys that come later. */ if (ioc->ioctl_cmds.msg_context != req->u.hdr.MsgContext) goto out_continuation; ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD; if (!reply) goto out; ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_RF_VALID; sz = min(ioc->reply_sz, 4*reply->u.reply.MsgLength); memcpy(ioc->ioctl_cmds.reply, reply, sz); if (reply->u.reply.IOCStatus || reply->u.reply.IOCLogInfo) dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "iocstatus (0x%04X), loginfo (0x%08X)\n", ioc->name, le16_to_cpu(reply->u.reply.IOCStatus), le32_to_cpu(reply->u.reply.IOCLogInfo))); if ((req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) || (req->u.hdr.Function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { if (reply->u.sreply.SCSIStatus || reply->u.sreply.SCSIState) dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "scsi_status (0x%02x), scsi_state (0x%02x), " "tag = (0x%04x), transfer_count (0x%08x)\n", ioc->name, reply->u.sreply.SCSIStatus, reply->u.sreply.SCSIState, le16_to_cpu(reply->u.sreply.TaskTag), le32_to_cpu(reply->u.sreply.TransferCount))); if (reply->u.sreply.SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { sz = req->u.scsireq.SenseBufferLength; req_index = le16_to_cpu(req->u.frame.hwhdr.msgctxu.fld.req_idx); sense_data = ((u8 *)ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC)); memcpy(ioc->ioctl_cmds.sense, sense_data, sz); ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_SENSE_VALID; } } out: /* We are done, issue wake up */ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_PENDING) { if (req->u.hdr.Function == MPI_FUNCTION_SCSI_TASK_MGMT) { mpt_clear_taskmgmt_in_progress_flag(ioc); ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING; complete(&ioc->ioctl_cmds.done); if (ioc->bus_type == SAS) ioc->schedule_target_reset(ioc); } else { ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING; complete(&ioc->ioctl_cmds.done); } } out_continuation: if (reply && (reply->u.reply.MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) return 0; return 1; } static int mptctl_taskmgmt_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) { if (!mf) return 0; dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed (mf=%p, mr=%p)\n", ioc->name, mf, mr)); ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD; if (!mr) goto out; ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID; memcpy(ioc->taskmgmt_cmds.reply, mr, min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength)); out: if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) { mpt_clear_taskmgmt_in_progress_flag(ioc); ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING; complete(&ioc->taskmgmt_cmds.done); if (ioc->bus_type == SAS) ioc->schedule_target_reset(ioc); return 1; } return 0; } static int mptctl_do_taskmgmt(MPT_ADAPTER *ioc, u8 tm_type, u8 bus_id, u8 target_id) { MPT_FRAME_HDR *mf; SCSITaskMgmt_t *pScsiTm; SCSITaskMgmtReply_t *pScsiTmReply; int ii; int retval; unsigned long timeout; u16 iocstatus; mutex_lock(&ioc->taskmgmt_cmds.mutex); if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) { mutex_unlock(&ioc->taskmgmt_cmds.mutex); return -EPERM; } retval = 0; mf = mpt_get_msg_frame(mptctl_taskmgmt_id, ioc); if (mf == NULL) { dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "TaskMgmt, no msg frames!!\n", ioc->name)); mpt_clear_taskmgmt_in_progress_flag(ioc); retval = -ENOMEM; goto tm_done; } dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n", ioc->name, mf)); pScsiTm = (SCSITaskMgmt_t *) mf; memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t)); pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; pScsiTm->TaskType = tm_type; if ((tm_type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (ioc->bus_type == FC)) pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION; pScsiTm->TargetID = target_id; pScsiTm->Bus = bus_id; pScsiTm->ChainOffset = 0; pScsiTm->Reserved = 0; pScsiTm->Reserved1 = 0; pScsiTm->TaskMsgContext = 0; for (ii= 0; ii < 8; ii++) pScsiTm->LUN[ii] = 0; for (ii=0; ii < 7; ii++) pScsiTm->Reserved2[ii] = 0; switch (ioc->bus_type) { case FC: timeout = 40; break; case SAS: timeout = 30; break; case SPI: default: timeout = 10; break; } dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt type=%d timeout=%ld\n", ioc->name, tm_type, timeout)); INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status) if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) && (ioc->facts.MsgVersion >= MPI_VERSION_01_05)) mpt_put_msg_frame_hi_pri(mptctl_taskmgmt_id, ioc, mf); else { retval = mpt_send_handshake_request(mptctl_taskmgmt_id, ioc, sizeof(SCSITaskMgmt_t), (u32 *)pScsiTm, CAN_SLEEP); if (retval != 0) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "TaskMgmt send_handshake FAILED!" " (ioc %p, mf %p, rc=%d) \n", ioc->name, ioc, mf, retval)); mpt_free_msg_frame(ioc, mf); mpt_clear_taskmgmt_in_progress_flag(ioc); goto tm_done; } } /* Now wait for the command to complete */ ii = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done, timeout*HZ); if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt failed\n", ioc->name)); mpt_free_msg_frame(ioc, mf); mpt_clear_taskmgmt_in_progress_flag(ioc); if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) retval = 0; else retval = -1; /* return failure */ goto tm_done; } if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) { dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt failed\n", ioc->name)); retval = -1; /* return failure */ goto tm_done; } pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply; dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt fw_channel = %d, fw_id = %d, task_type=0x%02X, " "iocstatus=0x%04X\n\tloginfo=0x%08X, response_code=0x%02X, " "term_cmnds=%d\n", ioc->name, pScsiTmReply->Bus, pScsiTmReply->TargetID, tm_type, le16_to_cpu(pScsiTmReply->IOCStatus), le32_to_cpu(pScsiTmReply->IOCLogInfo), pScsiTmReply->ResponseCode, le32_to_cpu(pScsiTmReply->TerminationCount))); iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK; if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED || iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED || iocstatus == MPI_IOCSTATUS_SUCCESS) retval = 0; else { dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt failed\n", ioc->name)); retval = -1; /* return failure */ } tm_done: mutex_unlock(&ioc->taskmgmt_cmds.mutex); CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status) return retval; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* mptctl_timeout_expired * * Expecting an interrupt, however timed out. * */ static void mptctl_timeout_expired(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) { unsigned long flags; int ret_val = -1; SCSIIORequest_t *scsi_req = (SCSIIORequest_t *) mf; u8 function = mf->u.hdr.Function; dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": %s\n", ioc->name, __func__)); if (mpt_fwfault_debug) mpt_halt_firmware(ioc); spin_lock_irqsave(&ioc->taskmgmt_lock, flags); if (ioc->ioc_reset_in_progress) { spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) mpt_free_msg_frame(ioc, mf); return; } spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) if (ioc->bus_type == SAS) { if (function == MPI_FUNCTION_SCSI_IO_REQUEST) ret_val = mptctl_do_taskmgmt(ioc, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, scsi_req->Bus, scsi_req->TargetID); else if (function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) ret_val = mptctl_do_taskmgmt(ioc, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, scsi_req->Bus, 0); if (!ret_val) return; } else { if ((function == MPI_FUNCTION_SCSI_IO_REQUEST) || (function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) ret_val = mptctl_do_taskmgmt(ioc,