📄 isp_linux.c
字号:
/* @(#)isp_linux.c 1.48 *//* * Qlogic ISP Host Adapter Common Bus Linux routies *--------------------------------------- * * Copyright (c) 1998, 1999, 2000, 2001 by Matthew Jacob * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * the GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Matthew Jacob * Feral Software * PMB #825 * 5214-F Diamond Hts Blvd * San Francisco, CA, 94131 * mjacob@feral.com * *-------- * Bug fixes from Janice McLaughlin (janus@somemore.com) * gratefully acknowledged. */#define ISP_MODULE 1#include "isp_linux.h"#include "linux/smp_lock.h"#ifdef ISP_PRIVATE_ASYNC#define isp_async isp_async_level1#endifextern void scsi_add_timer(Scsi_Cmnd *, int, void ((*)(Scsi_Cmnd *)));extern int scsi_delete_timer(Scsi_Cmnd *);static int isp_task_thread(void *);struct ispsoftc *isplist = NULL;int isp_debug = 0;int isp_throttle = 0;int isp_cmd_per_lun = 0;int isp_unit_seed = 0;int isp_disable = 0;int isp_nofwreload = 0;int isp_nonvram = 0;int isp_maxluns = 8;int isp_fcduplex = 0;int isp_nport_only = 0;int isp_loop_only = 0;int isp_deadloop_time = 30; /* how long to wait before assume loop dead */int isp_xtime = 0;static char *isp_roles;static char *isp_wwpns;static char *isp_wwnns;#ifdef LINUX_ISP_TARGET_MODE#ifndef ISP_PARENT_TARGET#define ISP_PARENT_TARGET scsi_target_handler#endifextern void ISP_PARENT_TARGET (qact_e, void *);static void isp_attach_target(struct ispsoftc *);static void isp_detach_target(struct ispsoftc *);static void isp_taction(qact_e, void *);static INLINE int nolunsenabled(struct ispsoftc *, int);static void isp_target_start_ctio(struct ispsoftc *, tmd_cmd_t *);static int isp_handle_platform_atio(struct ispsoftc *, at_entry_t *);static int isp_handle_platform_atio2(struct ispsoftc *, at2_entry_t *);static int isp_handle_platform_ctio(struct ispsoftc *, void *);static void isp_target_putback_atio(struct ispsoftc *, tmd_cmd_t *);static void isp_complete_ctio(struct ispsoftc *, tmd_cmd_t *);static int isp_en_dis_lun(struct ispsoftc *, int, int, int, int);#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,27)struct proc_dir_entry proc_scsi_qlc = { PROC_SCSI_QLOGICISP, 3, "isp", S_IFDIR | S_IRUGO | S_IXUGO, 2};#endifstatic const char *class3_roles[4] = { "None", "Target", "Initiator", "Target/Initiator"};extern int isplinux_pci_detect(Scsi_Host_Template *);extern void isplinux_pci_release(struct Scsi_Host *);intisplinux_proc_info(char *buf, char **st, off_t off, int len, int host, int io){#define PBF (&buf[size]) int size, pos, begin, i, lim; struct ispsoftc *isp; isp = isplist; while (isp) { if (isp->isp_host->host_no == host) { break; } isp = isp->isp_next; } if (isp == NULL) { return (-ENODEV); } if (io) { buf[len] = 0; io = -ENOSYS; if (strncmp(buf, "debug=", 6) == 0) { unsigned long debug; char *p = &buf[6], *q; debug = simple_strtoul(p, &q, 16); if (q == &buf[6]) { isp_prt(isp, ISP_LOGERR, "Garbled Debug Line '%s'", buf); return (-EINVAL); } isp_prt(isp, ISP_LOGINFO, "setting debug level to 0x%lx", debug); ISP_LOCKU_SOFTC(isp); isp->isp_dblev = debug; ISP_UNLKU_SOFTC(isp); io = len; } else if (strncmp(buf, "rescan", 6) == 0) { if (IS_FC(isp)) { SEND_THREAD_EVENT(isp, ISP_THREAD_FC_RESCAN, 1); io = len; } } else if (strncmp(buf, "lip", 3) == 0) { if (IS_FC(isp)) { ISP_LOCKU_SOFTC(isp); (void) isp_control(isp, ISPCTL_SEND_LIP, 0); ISP_UNLKU_SOFTC(isp); io = len; } } else if (strncmp(buf, "busreset=", 9) == 0) { char *p = &buf[6], *q; int bus = (int) simple_strtoul(p, &q, 16); if (q == &buf[6]) { isp_prt(isp, ISP_LOGERR, "Garbled Bus Reset Line '%s'", buf); return (-EINVAL); } ISP_LOCKU_SOFTC(isp); (void) isp_control(isp, ISPCTL_RESET_BUS, &bus); ISP_UNLKU_SOFTC(isp); io = len; } else if (strncmp(buf, "devreset=", 9) == 0) { char *p = &buf[6], *q; int dev = (int) simple_strtoul(p, &q, 16); if (q == &buf[6]) { isp_prt(isp, ISP_LOGERR, "Garbled Dev Reset Line '%s'", buf); return (-EINVAL); } /* always bus 0 */ ISP_LOCKU_SOFTC(isp); (void) isp_control(isp, ISPCTL_RESET_DEV, &dev); ISP_UNLKU_SOFTC(isp); io = len; }#ifdef LINUX_ISP_TARGET_MODE /* * Note that this cannot enable or disable luns on other than bus 0. */ else if (strncmp(buf, "enable_lun=", 11) == 0) { unsigned long lun; char *p = &buf[11], *q; lun = simple_strtoul(p, &q, 10); if (q == &buf[11]) { isp_prt(isp, ISP_LOGERR, "attempted enable of invalid lun (%s)", buf); return (-EINVAL); } io = isp_en_dis_lun(isp, 1, 0, -1, (int) lun); if (io >= 0) io = len; } else if (strncmp(buf, "disable_lun=", 12) == 0) { unsigned long lun; char *p = &buf[12], *q; lun = simple_strtoul(p, &q, 10); if (q == &buf[12]) { isp_prt(isp, ISP_LOGERR, "attempted disable of invalid lun (%s)", buf); return (-EINVAL); } io = isp_en_dis_lun(isp, 0, 0, -1, (int) lun); if (io >= 0) io = len; }#endif#ifdef ISP_FW_CRASH_DUMP else if (strncmp(buf, "fwcrash", 7) == 0) { if (IS_FC(isp)) { ISP_LOCKU_SOFTC(isp); SEND_THREAD_EVENT(isp, ISP_THREAD_FW_CRASH_DUMP, 0); ISP_UNLKU_SOFTC(isp); io = len; } }#endif return (io); } ISP_LOCKU_SOFTC(isp); begin = size = 0; size += sprintf(PBF, isplinux_info(isp->isp_host));#ifdef HBA_VERSION size += sprintf(PBF, "\n HBA Version %s, built %s, %s", HBA_VERSION, __DATE__, __TIME__);#endif size += sprintf(PBF, "\n DEVID %x role %d\n", isp->isp_osinfo.device_id, isp->isp_role); size += sprintf(PBF, " Interrupt Stats:\n" " total=0x%08x%08x bogus=0x%08x%08x\n" " MboxC=0x%08x%08x async=0x%08x%08x\n" " CRslt=0x%08x%08x CPost=0x%08x%08x\n" " RspnsCHiWater=0x%04x FastPostC_Hiwater=0x%04x\n", (u_int32_t) (isp->isp_intcnt >> 32), (u_int32_t) (isp->isp_intcnt & 0xffffffff), (u_int32_t) (isp->isp_intbogus >> 32), (u_int32_t) (isp->isp_intbogus & 0xffffffff), (u_int32_t) (isp->isp_intmboxc >> 32), (u_int32_t) (isp->isp_intmboxc & 0xffffffff), (u_int32_t) (isp->isp_intoasync >> 32), (u_int32_t) (isp->isp_intoasync & 0xffffffff), (u_int32_t) (isp->isp_rsltccmplt >> 32), (u_int32_t) (isp->isp_rsltccmplt & 0xffffffff), (u_int32_t) (isp->isp_fphccmplt >> 32), (u_int32_t) (isp->isp_fphccmplt & 0xffffffff), isp->isp_rscchiwater, isp->isp_fpcchiwater); size += sprintf(PBF, " Request In %d Request Out %d Result %d Nactv %d" " HiWater %u QAVAIL %d WtQHi %d\n", isp->isp_reqidx, isp->isp_reqodx, isp->isp_residx, isp->isp_nactive, isp->isp_osinfo.hiwater, ISP_QAVAIL(isp), isp->isp_osinfo.wqhiwater); for (lim = i = 0; i < isp->isp_maxcmds; i++) { if (isp->isp_xflist[i]) { size += sprintf(PBF, " %d:%p", i, isp->isp_xflist[i]); if (lim++ > 5) { size += sprintf(PBF, "..."); break; } } } size += sprintf(PBF, "\n"); if (isp->isp_osinfo.wqnext) { Scsi_Cmnd *f = isp->isp_osinfo.wqnext; size += sprintf(PBF, "WaitQ(%d)", isp->isp_osinfo.wqcnt); lim = 0; while (f) { size += sprintf(PBF, "->%p", f); f = (Scsi_Cmnd *) f->host_scribble; if (lim++ > 5) { size += sprintf(PBF, "..."); break; } } size += sprintf(PBF, "\n"); } if (isp->isp_osinfo.dqnext) { Scsi_Cmnd *f = isp->isp_osinfo.dqnext; size += sprintf(PBF, "DoneQ"); lim = 0; while (f) { size += sprintf(PBF, "->%p", f); f = (Scsi_Cmnd *) f->host_scribble; if (lim++ > 5) { size += sprintf(PBF, "..."); break; } } size += sprintf(PBF, "\n"); } if (IS_FC(isp)) { fcparam *fcp = isp->isp_param; size += sprintf(PBF, "Loop ID: %d AL_PA 0x%x Port ID 0x%x FW State %x Loop State %x\n", fcp->isp_loopid, fcp->isp_alpa, fcp->isp_portid, fcp->isp_fwstate, fcp->isp_loopstate); size += sprintf(PBF, "Port WWN 0x%08x%08x Node WWN 0x%08x%08x\n", (unsigned int) (ISP_PORTWWN(isp) >> 32), (unsigned int) (ISP_PORTWWN(isp) & 0xffffffff), (unsigned int) (ISP_NODEWWN(isp) >> 32), (unsigned int) (ISP_NODEWWN(isp) & 0xffffffff)); for (i = 0; i < MAX_FC_TARG; i++) { if (fcp->portdb[i].valid == 0 && i < FL_PORT_ID) continue; if (fcp->portdb[i].port_wwn == 0) continue; size += sprintf(PBF, "TGT % 3d Loop ID % 3d Port id 0x%04x, role %s" "\n Port WWN 0x%08x%08x Node WWN 0x%08x%08x\n\n", i, fcp->portdb[i].loopid, fcp->portdb[i].portid, class3_roles[fcp->portdb[i].roles], (unsigned int) (fcp->portdb[i].port_wwn >> 32), (unsigned int) (fcp->portdb[i].port_wwn & 0xffffffff), (unsigned int) (fcp->portdb[i].node_wwn >> 32), (unsigned int) (fcp->portdb[i].node_wwn & 0xffffffff)); } } else { sdparam *sdp = (sdparam *)isp->isp_param; size += sprintf(PBF, "Initiator ID: %d\n", sdp->isp_initiator_id); size += sprintf(PBF, "Target Flag Period Offset\n"); for (i = 0; i < MAX_TARGETS; i++) { size += sprintf(PBF, "%6d: 0x%04x 0x%04x 0x%x\n", i, sdp->isp_devparam[i].actv_flags, sdp->isp_devparam[i].actv_offset, sdp->isp_devparam[i].actv_period); } if (IS_DUALBUS(isp)) { sdp++; size += sprintf(PBF, "\nInitiator ID: %d, Channel B\n", sdp->isp_initiator_id); size += sprintf(PBF, "Target CurFlag DevFlag Period Offset B-Channel\n"); for (i = 0; i < MAX_TARGETS; i++) { size += sprintf(PBF, "%6d: 0x%04x 0x%04x 0x%x\n", i, sdp->isp_devparam[i].actv_flags, sdp->isp_devparam[i].actv_offset, sdp->isp_devparam[i].actv_period); } } } ISP_UNLKU_SOFTC(isp); pos = size; if (pos < off) { size = 0; begin = pos; } *st = buf + (off - begin); size -= (off - begin); if (size > len) size = len; return (size);}intisplinux_detect(Scsi_Host_Template *tmpt){ int rval;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) tmpt->proc_name = "isp";#else tmpt->proc_dir = &proc_scsi_qlc;#endif ISP_DRIVER_ENTRY_LOCK(isp); rval = isplinux_pci_detect(tmpt); ISP_DRIVER_EXIT_LOCK(isp); return (rval);}#ifdef MODULE/* io_request_lock *not* held here */intisplinux_release(struct Scsi_Host *host){ struct ispsoftc *isp = (struct ispsoftc *) host->hostdata; if (isp->isp_osinfo.task_thread) { SEND_THREAD_EVENT(isp, ISP_THREAD_EXIT, 1); } ISP_LOCKU_SOFTC(isp); isp->dogactive = 0; del_timer(&isp->isp_osinfo.timer); DISABLE_INTS(isp); if (isp->isp_bustype == ISP_BT_PCI) { isplinux_pci_release(host); } ISP_UNLKU_SOFTC(isp);#ifdef ISP_FW_CRASH_DUMP if (FCPARAM(isp)->isp_dump_data) { isp_prt(isp, ISP_LOGCONFIG, "freeing crash dump area"); vfree(FCPARAM(isp)->isp_dump_data); FCPARAM(isp)->isp_dump_data = 0; }#endif#ifdef ISP_TARGET_MODE isp_detach_target(isp);#endif return (1);}#endifconst char *isplinux_info(struct Scsi_Host *host){ struct ispsoftc *isp = (struct ispsoftc *) host->hostdata; if (IS_FC(isp)) { static char *foo = "Driver for a Qlogic ISP 2X00 Host Adapter"; if (isp->isp_type == ISP_HA_FC_2100) foo[25] = '1'; else if (isp->isp_type == ISP_HA_FC_2200) foo[25] = '2'; else if (isp->isp_type == ISP_HA_FC_2300) foo[25] = '3'; else if (isp->isp_type == ISP_HA_FC_2312) { foo[25] = '3'; foo[26] = '1'; foo[27] = '2'; } return (foo); } else if (IS_1240(isp)) { return ("Driver for a Qlogic ISP 1240 Host Adapter"); } else if (IS_1080(isp)) { return ("Driver for a Qlogic ISP 1080 Host Adapter"); } else if (IS_1280(isp)) { return ("Driver for a Qlogic ISP 1280 Host Adapter"); } else if (IS_12160(isp)) { return ("Driver for a Qlogic ISP 12160 Host Adapter"); } else { return ("Driver for a Qlogic ISP 1020/1040 Host Adapter"); }}static INLINE voidisplinux_append_to_waitq(struct ispsoftc *isp, Scsi_Cmnd *Cmnd){ /* * If we're a fibre channel card and we consider the loop to be * down, we just finish the command here and now. */ if (IS_FC(isp) && isp->isp_deadloop) { XS_INITERR(Cmnd); XS_SETERR(Cmnd, DID_NO_CONNECT); /* * Add back a timer else scsi_done drops this on the floor. */ scsi_add_timer(Cmnd, Cmnd->timeout_per_command, Cmnd->done); isp_prt(isp, ISP_LOGDEBUG0, "giving up on target %d", Cmnd->target); ISP_UNLK_SOFTC(isp); ISP_LOCK_SCSI_DONE(isp); (*Cmnd->scsi_done)(Cmnd); ISP_UNLK_SCSI_DONE(isp); ISP_LOCK_SOFTC(isp); return; } isp->isp_osinfo.wqcnt++; if (isp->isp_osinfo.wqhiwater < isp->isp_osinfo.wqcnt) isp->isp_osinfo.wqhiwater = isp->isp_osinfo.wqcnt; if (isp->isp_osinfo.wqnext == NULL) { isp->isp_osinfo.wqtail = isp->isp_osinfo.wqnext = Cmnd; } else { isp->isp_osinfo.wqtail->host_scribble = (unsigned char *) Cmnd; isp->isp_osinfo.wqtail = Cmnd; } Cmnd->host_scribble = NULL; /* * Stop the clock for this command. */ (void) scsi_delete_timer(Cmnd);}static INLINE voidisplinux_insert_head_waitq(struct ispsoftc *isp, Scsi_Cmnd *Cmnd){ isp->isp_osinfo.wqcnt++; if (isp->isp_osinfo.wqnext == NULL) { isp->isp_osinfo.wqtail = isp->isp_osinfo.wqnext = Cmnd; Cmnd->host_scribble = NULL; } else { Cmnd->host_scribble = (unsigned char *) isp->isp_osinfo.wqnext; isp->isp_osinfo.wqnext = Cmnd; }}static INLINE Scsi_Cmnd *isp_remove_from_waitq(Scsi_Cmnd *Cmnd){ struct ispsoftc *isp; Scsi_Cmnd *f; if (Cmnd == NULL) return (Cmnd); isp = (struct ispsoftc *) Cmnd->host->hostdata; if ((f = isp->isp_osinfo.wqnext) == Cmnd) { isp->isp_osinfo.wqnext = (Scsi_Cmnd *) Cmnd->host_scribble; } else { Scsi_Cmnd *b = f; while (f) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -