📄 isp_target.c
字号:
/* @(#)isp_target.c 1.14 *//* * Machine and OS Independent Target Mode Code for the Qlogic SCSI/FC adapters. * * Copyright (c) 1999, 2000, 2001 by Matthew Jacob * All rights reserved. * mjacob@feral.com * * 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. *//* * Bug fixes gratefully acknowledged from: * Oded Kedem <oded@kashya.com> *//* * Include header file appropriate for platform we're building on. */#ifdef __NetBSD__#include <dev/ic/isp_netbsd.h>#endif#ifdef __FreeBSD__#include <dev/isp/isp_freebsd.h>#endif#ifdef __OpenBSD__#include <dev/ic/isp_openbsd.h>#endif#ifdef __linux__#include "isp_linux.h"#endif#ifdef ISP_TARGET_MODEstatic const char atiocope[] = "ATIO returned for lun %d because it was in the middle of Bus Device Reset " "on bus %d";static const char atior[] = "ATIO returned on for lun %d on from IID %d because a Bus Reset occurred " "on bus %d";static void isp_got_msg(struct ispsoftc *, int, in_entry_t *);static void isp_got_msg_fc(struct ispsoftc *, int, in_fcentry_t *);static void isp_notify_ack(struct ispsoftc *, void *);static void isp_handle_atio(struct ispsoftc *, at_entry_t *);static void isp_handle_atio2(struct ispsoftc *, at2_entry_t *);static void isp_handle_ctio(struct ispsoftc *, ct_entry_t *);static void isp_handle_ctio2(struct ispsoftc *, ct2_entry_t *);/* * The Qlogic driver gets an interrupt to look at response queue entries. * Some of these are status completions for initiatior mode commands, but * if target mode is enabled, we get a whole wad of response queue entries * to be handled here. * * Basically the split into 3 main groups: Lun Enable/Modification responses, * SCSI Command processing, and Immediate Notification events. * * You start by writing a request queue entry to enable target mode (and * establish some resource limitations which you can modify later). * The f/w responds with a LUN ENABLE or LUN MODIFY response with * the status of this action. If the enable was successful, you can expect... * * Response queue entries with SCSI commands encapsulate show up in an ATIO * (Accept Target IO) type- sometimes with enough info to stop the command at * this level. Ultimately the driver has to feed back to the f/w's request * queue a sequence of CTIOs (continue target I/O) that describe data to * be moved and/or status to be sent) and finally finishing with sending * to the f/w's response queue an ATIO which then completes the handshake * with the f/w for that command. There's a lot of variations on this theme, * including flags you can set in the CTIO for the Qlogic 2X00 fibre channel * cards that 'auto-replenish' the f/w's ATIO count, but this is the basic * gist of it. * * The third group that can show up in the response queue are Immediate * Notification events. These include things like notifications of SCSI bus * resets, or Bus Device Reset messages or other messages received. This * a classic oddbins area. It can get a little weird because you then turn * around and acknowledge the Immediate Notify by writing an entry onto the * request queue and then the f/w turns around and gives you an acknowledgement * to *your* acknowledgement on the response queue (the idea being to let * the f/w tell you when the event is *really* over I guess). * *//* * A new response queue entry has arrived. The interrupt service code * has already swizzled it into the platform dependent from canonical form. * * Because of the way this driver is designed, unfortunately most of the * actual synchronization work has to be done in the platform specific * code- we have no synchroniation primitives in the common code. */intisp_target_notify(struct ispsoftc *isp, void *vptr, u_int16_t *optrp){ u_int16_t status, seqid; union { at_entry_t *atiop; at2_entry_t *at2iop; ct_entry_t *ctiop; ct2_entry_t *ct2iop; lun_entry_t *lunenp; in_entry_t *inotp; in_fcentry_t *inot_fcp; na_entry_t *nackp; na_fcentry_t *nack_fcp; isphdr_t *hp; void * *vp;#define atiop unp.atiop#define at2iop unp.at2iop#define ctiop unp.ctiop#define ct2iop unp.ct2iop#define lunenp unp.lunenp#define inotp unp.inotp#define inot_fcp unp.inot_fcp#define nackp unp.nackp#define nack_fcp unp.nack_fcp#define hdrp unp.hp } unp; u_int8_t local[QENTRY_LEN]; int bus, type, rval = 0; type = isp_get_response_type(isp, (isphdr_t *)vptr); unp.vp = vptr; ISP_TDQE(isp, "isp_target_notify", (int) *optrp, vptr); switch(type) { case RQSTYPE_ATIO: isp_get_atio(isp, atiop, (at_entry_t *) local); isp_handle_atio(isp, (at_entry_t *) local); break; case RQSTYPE_CTIO: isp_get_ctio(isp, ctiop, (ct_entry_t *) local); isp_handle_ctio(isp, (ct_entry_t *) local); break; case RQSTYPE_ATIO2: isp_get_atio2(isp, at2iop, (at2_entry_t *) local); isp_handle_atio2(isp, (at2_entry_t *) local); break; case RQSTYPE_CTIO2: isp_get_ctio2(isp, ct2iop, (ct2_entry_t *) local); isp_handle_ctio2(isp, (ct2_entry_t *) local); break; case RQSTYPE_ENABLE_LUN: case RQSTYPE_MODIFY_LUN: isp_get_enable_lun(isp, lunenp, (lun_entry_t *) local); (void) isp_async(isp, ISPASYNC_TARGET_ACTION, local); break; case RQSTYPE_NOTIFY: /* * Either the ISP received a SCSI message it can't * handle, or it's returning an Immed. Notify entry * we sent. We can send Immed. Notify entries to * increment the firmware's resource count for them * (we set this initially in the Enable Lun entry). */ bus = 0; if (IS_FC(isp)) { isp_get_notify_fc(isp, inot_fcp, (in_fcentry_t *)local); inot_fcp = (in_fcentry_t *) local; status = inot_fcp->in_status; seqid = inot_fcp->in_seqid; } else { isp_get_notify(isp, inotp, (in_entry_t *)local); inotp = (in_entry_t *) local; status = inotp->in_status & 0xff; seqid = inotp->in_seqid; if (IS_DUALBUS(isp)) { bus = GET_BUS_VAL(inotp->in_iid); SET_BUS_VAL(inotp->in_iid, 0); } } isp_prt(isp, ISP_LOGTDEBUG0, "Immediate Notify On Bus %d, status=0x%x seqid=0x%x", bus, status, seqid); /* * ACK it right away. */ isp_notify_ack(isp, (status == IN_RESET)? NULL : local); switch (status) { case IN_RESET: (void) isp_async(isp, ISPASYNC_BUS_RESET, &bus); break; case IN_MSG_RECEIVED: case IN_IDE_RECEIVED: if (IS_FC(isp)) { isp_got_msg_fc(isp, bus, (in_fcentry_t *)local); } else { isp_got_msg(isp, bus, (in_entry_t *)local); } break; case IN_RSRC_UNAVAIL: isp_prt(isp, ISP_LOGWARN, "Firmware out of ATIOs"); break; case IN_ABORT_TASK: isp_prt(isp, ISP_LOGWARN, "Abort Task from IID %d RX_ID 0x%x", inot_fcp->in_iid, seqid); (void) isp_async(isp, ISPASYNC_TARGET_ACTION, &bus); break; case IN_PORT_LOGOUT: isp_prt(isp, ISP_LOGWARN, "Port Logout for Initiator %d RX_ID 0x%x", inot_fcp->in_iid, seqid); break; case IN_PORT_CHANGED: isp_prt(isp, ISP_LOGWARN, "Port Changed for Initiator %d RX_ID 0x%x", inot_fcp->in_iid, seqid); break; case IN_GLOBAL_LOGO: isp_prt(isp, ISP_LOGWARN, "All ports logged out"); break; default: isp_prt(isp, ISP_LOGERR, "bad status (0x%x) in isp_target_notify", status); break; } break; case RQSTYPE_NOTIFY_ACK: /* * The ISP is acknowledging our acknowledgement of an * Immediate Notify entry for some asynchronous event. */ if (IS_FC(isp)) { isp_get_notify_ack_fc(isp, nack_fcp, (na_fcentry_t *)local); nack_fcp = (na_fcentry_t *)local; isp_prt(isp, ISP_LOGTDEBUG1, "Notify Ack status=0x%x seqid 0x%x", nack_fcp->na_status, nack_fcp->na_seqid); } else { isp_get_notify_ack(isp, nackp, (na_entry_t *)local); nackp = (na_entry_t *)local; isp_prt(isp, ISP_LOGTDEBUG1, "Notify Ack event 0x%x status=0x%x seqid 0x%x", nackp->na_event, nackp->na_status, nackp->na_seqid); } break; default: isp_prt(isp, ISP_LOGERR, "Unknown entry type 0x%x in isp_target_notify", type); rval = -1; break; }#undef atiop#undef at2iop#undef ctiop#undef ct2iop#undef lunenp#undef inotp#undef inot_fcp#undef nackp#undef nack_fcp#undef hdrp return (rval);} /* * Toggle (on/off) target mode for bus/target/lun * * The caller has checked for overlap and legality. * * Note that not all of bus, target or lun can be paid attention to. * Note also that this action will not be complete until the f/w writes * response entry. The caller is responsible for synchronizing this. */intisp_lun_cmd(struct ispsoftc *isp, int cmd, int bus, int tgt, int lun, int cmd_cnt, int inot_cnt, u_int32_t opaque){ lun_entry_t el; u_int16_t nxti, optr; void *outp; MEMZERO(&el, sizeof (el)); if (IS_DUALBUS(isp)) { el.le_rsvd = (bus & 0x1) << 7; } el.le_cmd_count = cmd_cnt; el.le_in_count = inot_cnt; if (cmd == RQSTYPE_ENABLE_LUN) { if (IS_SCSI(isp)) { el.le_flags = LUN_TQAE|LUN_DISAD; el.le_cdb6len = 12; el.le_cdb7len = 12; } } else if (cmd == -RQSTYPE_ENABLE_LUN) { cmd = RQSTYPE_ENABLE_LUN; el.le_cmd_count = 0; el.le_in_count = 0; } else if (cmd == -RQSTYPE_MODIFY_LUN) { cmd = RQSTYPE_MODIFY_LUN; el.le_ops = LUN_CCDECR | LUN_INDECR; } else { el.le_ops = LUN_CCINCR | LUN_ININCR; } el.le_header.rqs_entry_type = cmd; el.le_header.rqs_entry_count = 1; el.le_reserved = opaque; if (IS_SCSI(isp)) { el.le_tgt = tgt; el.le_lun = lun; } else if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) { el.le_lun = lun; } el.le_timeout = 2; if (isp_getrqentry(isp, &nxti, &optr, &outp)) { isp_prt(isp, ISP_LOGERR, "Request Queue Overflow in isp_lun_cmd"); return (-1); } ISP_TDQE(isp, "isp_lun_cmd", (int) optr, &el); isp_put_enable_lun(isp, &el, outp); ISP_ADD_REQUEST(isp, nxti); return (0);}intisp_target_put_entry(struct ispsoftc *isp, void *ap){ void *outp; u_int16_t nxti, optr; u_int8_t etype = ((isphdr_t *) ap)->rqs_entry_type; if (isp_getrqentry(isp, &nxti, &optr, &outp)) { isp_prt(isp, ISP_LOGWARN, "Request Queue Overflow in isp_target_put_entry"); return (-1); } switch (etype) { case RQSTYPE_ATIO: isp_put_atio(isp, (at_entry_t *) ap, (at_entry_t *) outp); break; case RQSTYPE_ATIO2: isp_put_atio2(isp, (at2_entry_t *) ap, (at2_entry_t *) outp); break; case RQSTYPE_CTIO: isp_put_ctio(isp, (ct_entry_t *) ap, (ct_entry_t *) outp); break; case RQSTYPE_CTIO2: isp_put_ctio2(isp, (ct2_entry_t *) ap, (ct2_entry_t *) outp); break; default: isp_prt(isp, ISP_LOGERR, "Unknown type 0x%x in isp_put_entry", etype); return (-1); } ISP_TDQE(isp, "isp_target_put_entry", (int) optr, ap);; ISP_ADD_REQUEST(isp, nxti); return (0);}intisp_target_put_atio(struct ispsoftc *isp, void *arg){ union { at_entry_t _atio; at2_entry_t _atio2; } atun; MEMZERO(&atun, sizeof atun); if (IS_FC(isp)) { at2_entry_t *aep = arg; atun._atio2.at_header.rqs_entry_type = RQSTYPE_ATIO2; atun._atio2.at_header.rqs_entry_count = 1; if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { atun._atio2.at_scclun = (u_int16_t) aep->at_scclun; } else { atun._atio2.at_lun = (u_int8_t) aep->at_lun;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -