Files
scst/qla_isp/linux/isp_linux.c
Stanislaw Gruszka bdd6415a91 Synchronize with Feral CVS repository:
I don't know why I thought this would work- forcing a re-login to the fabric
controller just barfs.

Make down count reflect jiffies elapsed rather than depend on calls to
ISP_FC_RESCAN.

Shorten deadloop time default to 10 seconds.

At least enable MSI (if not MSI-X) for 24XX cards.


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@282 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2008-02-12 11:05:04 +00:00

4929 lines
165 KiB
C

/* $Id: isp_linux.c,v 1.224 2008/01/25 22:22:47 mjacob Exp $ */
/*
* Copyright (c) 1997-2007 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.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY 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 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.
*
*
* Alternatively, this software may be distributed under the terms of the
* the GNU Public License ("GPL") with platforms where the prevalant license
* is the GNU Public License:
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of The Version 2 GNU General Public License as published
* by the Free Software Foundation.
*
* 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.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* Matthew Jacob
* Feral Software
* 421 Laurel Avenue
* Menlo Park, CA 94025
* USA
*
* gplbsd at feral com
*/
/*
* Qlogic ISP Host Adapter Common Bus Linux routies
*
* Bug fixes from Janice McLaughlin (janus@somemore.com)
* gratefully acknowledged.
*
*/
#define ISP_MODULE 1
#include "isp_linux.h"
#include "linux/smp_lock.h"
static int isp_task_thread(void *);
ispsoftc_t *isplist[MAX_ISP] = { NULL };
ispsoftc_t *api_isp = NULL;
int api_channel = 0;
int isp_debug = 0;
int isp_throttle = 0;
int isp_cmd_per_lun = 0;
int isp_maxsectors = 1024;
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 = 10; /* how long to wait before assume loop dead */
int isp_fc_id = 111;
int isp_spi_id = 7;
int isp_own_id = 0;
int isp_default_frame_size;
int isp_default_exec_throttle;
int isp_vports = 0;
static char *isp_roles;
static char *isp_wwpns;
static char *isp_wwnns;
#ifdef ISP_TARGET_MODE
#ifndef ISP_PARENT_TARGET
#define ISP_PARENT_TARGET scsi_target_handler
#endif
#define CALL_PARENT_TMD(hba, tmd, action) \
tmd->cd_action = action; \
tmd->cd_next = hba->isp_osinfo.pending_t; \
hba->isp_osinfo.pending_t = tmd
#define CALL_PARENT_NOTIFY(hba, ins) \
ins->notify.nt_lreserved = hba->isp_osinfo.pending_n; \
hba->isp_osinfo.pending_n = ins
#define CALL_PARENT_XFR(hba, xact) \
xact->td_lprivate = hba->isp_osinfo.pending_x; \
hba->isp_osinfo.pending_x = xact
extern void ISP_PARENT_TARGET (qact_e, void *);
static inline tmd_cmd_t *isp_find_tmd(ispsoftc_t *, uint64_t);
static void isp_add_wwn_entry(ispsoftc_t *, int, uint64_t, uint16_t, uint32_t);
static void isp_del_wwn_entry(ispsoftc_t *, int, uint64_t, uint16_t, uint32_t);
static inline int isp_find_pdb_by_loopid(ispsoftc_t *, int, uint32_t, fcportdb_t **);
static inline int isp_find_pdb_by_sid(ispsoftc_t *, int, uint32_t, fcportdb_t **);
static void isp_taction(qact_e, void *);
static void isp_target_start_ctio(ispsoftc_t *, tmd_xact_t *);
static void isp_handle_platform_atio(ispsoftc_t *, at_entry_t *);
static void isp_handle_platform_atio2(ispsoftc_t *, at2_entry_t *);
static void isp_handle_platform_atio7(ispsoftc_t *, at7_entry_t *);
static int isp_terminate_cmd(ispsoftc_t *, tmd_cmd_t *);
static void isp_handle_platform_ctio(ispsoftc_t *, void *);
static int isp_target_putback_atio(ispsoftc_t *, tmd_cmd_t *);
static void isp_complete_ctio(ispsoftc_t *, tmd_xact_t *);
static void isp_tgt_tq(ispsoftc_t *);
#endif
const char *
isplinux_info(struct Scsi_Host *host)
{
ispsoftc_t *isp = ISP_HOST2ISP(host);
if (IS_FC(isp)) {
static char *foo = "Driver for a Qlogic ISP 2X00 Host Adapter";
foo[26] = '0';
foo[27] = '0';
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';
} else if (isp->isp_type == ISP_HA_FC_2322) {
foo[25] = '3';
foo[26] = '2';
foo[27] = '2';
} else if (isp->isp_type == ISP_HA_FC_2400) {
foo[25] = '4';
foo[26] = '2';
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_10160(isp)) {
return ("Driver for a Qlogic ISP 10160 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 void
isplinux_append_to_waitq(ispsoftc_t *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_DATA(isp, XS_CHANNEL(Cmnd))->deadloop) || isp->isp_dead) {
XS_INITERR(Cmnd);
XS_SETERR(Cmnd, DID_NO_CONNECT);
/*
* Add back a timer else scsi_done drops this on the floor.
*/
if (Cmnd->eh_timeout.function) {
mod_timer(&Cmnd->eh_timeout, jiffies + Cmnd->timeout_per_command);
}
isp_prt(isp, ISP_LOGDEBUG0, "giving up on target %d", XS_TGT(Cmnd));
ISP_DROP_LK_SOFTC(isp);
ISP_LOCK_SCSI_DONE(isp);
(*Cmnd->scsi_done)(Cmnd);
ISP_UNLK_SCSI_DONE(isp);
ISP_IGET_LK_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.
*/
if (Cmnd->eh_timeout.function) {
del_timer(&Cmnd->eh_timeout);
}
}
static inline void
isplinux_insert_head_waitq(ispsoftc_t *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)
{
ispsoftc_t *isp;
Scsi_Cmnd *f;
if (Cmnd == NULL) {
return (Cmnd);
}
isp = XS_ISP(Cmnd);
if ((f = isp->isp_osinfo.wqnext) == Cmnd) {
isp->isp_osinfo.wqnext = (Scsi_Cmnd *) Cmnd->host_scribble;
} else {
Scsi_Cmnd *b = f;
while (f) {
f = (Scsi_Cmnd *) b->host_scribble;
if (f == Cmnd) {
b->host_scribble = f->host_scribble;
if (isp->isp_osinfo.wqtail == Cmnd) {
isp->isp_osinfo.wqtail = b;
}
break;
}
b = f;
}
}
if (f) {
f->host_scribble = NULL;
isp->isp_osinfo.wqcnt -= 1;
}
return (f);
}
static inline void
isplinux_runwaitq(ispsoftc_t *isp)
{
Scsi_Cmnd *f;
int chan, result;
for (chan = 0; chan < isp->isp_nchan; chan++) {
if (ISP_DATA(isp, chan)->blocked || ISP_DATA(isp, chan)->qfdelay) {
continue;
}
f = isp->isp_osinfo.wqnext;
while (f != NULL) {
Scsi_Cmnd *nxt = (Scsi_Cmnd *) f->host_scribble;
if (XS_CHANNEL(f) != chan) {
f = nxt;
continue;
}
f = isp_remove_from_waitq(f);
result = isp_start(f);
/*
* Restart the timer for this command if it is queued or completing.
*/
if (result == CMD_QUEUED || result == CMD_COMPLETE) {
if (f->eh_timeout.function) {
mod_timer(&f->eh_timeout, jiffies + f->timeout_per_command);
}
if (result == CMD_QUEUED) {
if (isp->isp_osinfo.hiwater < isp->isp_nactive) {
isp->isp_osinfo.hiwater = isp->isp_nactive;
}
f = isp->isp_osinfo.wqnext;
continue;
}
}
/*
* If we cannot start a command on a fibre channel card, it means
* that loop state isn't ready for us to do so. Activate the FC
* thread to rediscover loop and fabric residency- but not if
* we consider the loop to be dead. If the loop is considered dead,
* we wait until a PDB Changed after a Loop UP activates the FC
* thread.
*/
if (IS_FC(isp)) {
if (result == CMD_RQLATER && ISP_DATA(isp, XS_CHANNEL(f))->deadloop == 0) {
ISP_THREAD_EVENT(isp, ISP_THREAD_FC_RESCAN, FCPARAM(isp, chan), 0, __FUNCTION__, __LINE__);
}
}
/*
* Put the command back on the wait queue. Don't change any
* timer parameters for it because they were established
* when we originally put the command on the waitq in the first
* place.
*/
if (result == CMD_EAGAIN || result == CMD_RQLATER) {
isplinux_insert_head_waitq(isp, f);
break;
}
if (result == CMD_COMPLETE) {
isp_done(f);
} else {
isp_prt(isp, ISP_LOGERR, "isplinux_runwaitq: result %d", result);
}
f = isp->isp_osinfo.wqnext;
}
}
}
static inline void
isplinux_flushwaitq(ispsoftc_t *isp)
{
Scsi_Cmnd *Cmnd, *Ncmnd;
if ((Cmnd = isp->isp_osinfo.wqnext) == NULL) {
return;
}
isp->isp_osinfo.wqnext = isp->isp_osinfo.wqtail = NULL;
isp->isp_osinfo.wqcnt = 0;
ISP_DROP_LK_SOFTC(isp);
do {
Ncmnd = (Scsi_Cmnd *) Cmnd->host_scribble;
Cmnd->host_scribble = NULL;
XS_INITERR(Cmnd);
XS_SETERR(Cmnd, DID_NO_CONNECT);
/*
* Add back a timer else scsi_done drops this on the floor.
*/
if (Cmnd->eh_timeout.function) {
mod_timer(&Cmnd->eh_timeout, jiffies + Cmnd->timeout_per_command);
}
ISP_LOCK_SCSI_DONE(isp);
(*Cmnd->scsi_done)(Cmnd);
ISP_UNLK_SCSI_DONE(isp);
} while ((Cmnd = Ncmnd) != NULL);
ISP_IGET_LK_SOFTC(isp);
}
static inline Scsi_Cmnd *
isplinux_remove_from_doneq(Scsi_Cmnd *Cmnd)
{
Scsi_Cmnd *f;
ispsoftc_t *isp;
if (Cmnd == NULL) {
return (NULL);
}
isp = XS_ISP(Cmnd);
if (isp->isp_osinfo.dqnext == NULL) {
return (NULL);
}
if ((f = isp->isp_osinfo.dqnext) == Cmnd) {
isp->isp_osinfo.dqnext = (Scsi_Cmnd *) Cmnd->host_scribble;
} else {
Scsi_Cmnd *b = f;
while (f) {
f = (Scsi_Cmnd *) b->host_scribble;
if (f == Cmnd) {
b->host_scribble = f->host_scribble;
if (isp->isp_osinfo.dqtail == Cmnd) {
isp->isp_osinfo.dqtail = b;
}
break;
}
b = f;
}
}
if (f) {
f->host_scribble = NULL;
}
return (f);
}
int
isplinux_queuecommand(Scsi_Cmnd *Cmnd, void (*donecmd)(Scsi_Cmnd *))
{
struct Scsi_Host *host = XS_HOST(Cmnd);
ispsoftc_t *isp = ISP_HOST2ISP(host);
int result, chan;
unsigned long flags;
chan = XS_CHANNEL(Cmnd);
Cmnd->scsi_done = donecmd;
Cmnd->sense_buffer[0] = 0;
ISP_DRIVER_ENTRY_LOCK(isp);
ISP_LOCK_SOFTC(isp);
/*
* See if we're currently blocked. If we are, just queue up the command to be run later.
*/
if (ISP_DATA(isp, chan)->blocked || ISP_DATA(isp, chan)->qfdelay) {
isp_prt(isp, ISP_LOGDEBUG0, "appending cmd to waitq due to %d/%d", ISP_DATA(isp, chan)->blocked, ISP_DATA(isp, chan)->qfdelay);
isplinux_append_to_waitq(isp, Cmnd);
ISP_UNLK_SOFTC(isp);
ISP_DRIVER_EXIT_LOCK(isp);
return (0);
}
/*
* If we get past the above, and we're not at RUNSTATE, we're broken or out of service.
*/
if (isp->isp_state != ISP_RUNSTATE) {
isp_prt(isp, ISP_LOGDEBUG0, "DID_NOCONNECT because isp not at RUNSTATE");
ISP_UNLK_SOFTC(isp);
ISP_DRIVER_EXIT_LOCK(isp);
XS_INITERR(Cmnd);
XS_SETERR(Cmnd, DID_NO_CONNECT);
ISP_LOCK_SCSI_DONE(isp);
(*Cmnd->scsi_done)(Cmnd);
ISP_UNLK_SCSI_DONE(isp);
return (0);
}
/*
* Next see if we have any stored up commands to run. If so, run them.
* If we get back from this with commands still ready to run, put the
* current command at the tail of waiting commands to be run later.
*/
isplinux_runwaitq(isp);
if (isp->isp_osinfo.wqnext) {
isp_prt(isp, ISP_LOGDEBUG0, "appending cmd to waitq");
isplinux_append_to_waitq(isp, Cmnd);
ISP_UNLK_SOFTC(isp);
ISP_DRIVER_EXIT_LOCK(isp);
return (0);
}
/*
* Finally, try and run this command.
*/
result = isp_start(Cmnd);
if (result == CMD_QUEUED) {
if (isp->isp_osinfo.hiwater < isp->isp_nactive) {
isp->isp_osinfo.hiwater = isp->isp_nactive;
}
result = 0;
} else if (result == CMD_EAGAIN) {
/*
* We ran out of request queue space (or could not
* get DMA resources). Tell the upper layer to try
* later.
*/
result = 1;
} else if (result == CMD_RQLATER) {
/*
* Temporarily hold off on this one.
* Typically this means for fibre channel
* that the loop is down or we're processing
* some other change (e.g., fabric membership
* change)
*/
isplinux_append_to_waitq(isp, Cmnd);
if (IS_FC(isp) && ISP_DATA(isp, XS_CHANNEL(Cmnd))->deadloop == 0) {
ISP_THREAD_EVENT(isp, ISP_THREAD_FC_RESCAN, FCPARAM(isp, XS_CHANNEL(Cmnd)), 0, __FUNCTION__, __LINE__);
}
result = 0;
} else if (result == CMD_COMPLETE) {
result = -1;
} else {
isp_prt(isp, ISP_LOGERR, "unknown return code %d from isp_start", result);
XS_INITERR(Cmnd);
XS_SETERR(Cmnd, DID_ERROR);
ISP_LOCK_SCSI_DONE(isp);
(*Cmnd->scsi_done)(Cmnd);
ISP_UNLK_SCSI_DONE(isp);
return (0);
}
ISP_UNLK_SOFTC(isp);
ISP_DRIVER_EXIT_LOCK(isp);
if (result == -1) {
Cmnd->result &= ~0xff;
Cmnd->result |= Cmnd->SCp.Status;
Cmnd->host_scribble = NULL;
ISP_LOCK_SCSI_DONE(isp);
(*Cmnd->scsi_done)(Cmnd);
ISP_UNLK_SCSI_DONE(isp);
result = 0;
}
return (result);
}
static inline void
isplinux_scsi_probe_done(Scsi_Cmnd *Cmnd)
{
ispsoftc_t *isp = XS_ISP(Cmnd);
isp_data *idp;
/*
* If we haven't seen this target yet, check the command result. If
* it was an inquiry and it succeeded okay, then we can update our
* notions about this target's capabilities.
*
* If the command did *not* succeed, we also update our notions about
* this target's capabilities (pessimistically) - it's probably not there.
* All of this so we can know when we're done so we stop wasting cycles
* seeing whether we can enable sync mode or not.
*/
idp = ISP_DATA(isp, XS_CHANNEL(Cmnd));
if ((idp->tgts_tested & (1 << XS_TGT(Cmnd))) == 0) {
sdparam *sdp;
caddr_t iqd;
sdp = SDPARAM(isp, XS_CHANNEL(Cmnd));
if (Cmnd->cmnd[0] == 0x12 && host_byte(Cmnd->result) == DID_OK) {
if (Cmnd->use_sg == 0) {
iqd = (caddr_t) Cmnd->request_buffer;
} else {
struct scatterlist *sg;
sg = (struct scatterlist *) Cmnd->request_buffer;
iqd = page_address(sg->page) + sg->offset;
}
sdp->isp_devparam[XS_TGT(Cmnd)].goal_flags &= ~(DPARM_TQING|DPARM_SYNC|DPARM_WIDE);
if (iqd[7] & 0x2) {
sdp->isp_devparam[XS_TGT(Cmnd)].goal_flags |= DPARM_TQING;
}
if (iqd[7] & 0x10) {
sdp->isp_devparam[XS_TGT(Cmnd)].goal_flags |= DPARM_SYNC;
}
if (iqd[7] & 0x20) {
sdp->isp_devparam[XS_TGT(Cmnd)].goal_flags |= DPARM_WIDE;
}
sdp->isp_devparam[XS_TGT(Cmnd)].dev_update = 1;
idp->tgts_tested |= (1 << XS_TGT(Cmnd));
} else if (host_byte(Cmnd->result) != DID_OK) {
idp->tgts_tested |= (1 << XS_TGT(Cmnd));
}
if ((idp->tgts_tested & ~sdp->isp_initiator_id) == (0xffff & ~sdp->isp_initiator_id)) {
idp->tgts_tested = 0xffff;
sdp->update = 1;
}
}
}
void
isp_done(Scsi_Cmnd *Cmnd)
{
ispsoftc_t *isp = XS_ISP(Cmnd);
if (IS_SCSI(isp) && unlikely(ISP_DATA(isp, XS_CHANNEL(Cmnd))->tgts_tested != 0xffff)) {
isplinux_scsi_probe_done(Cmnd);
}
Cmnd->result &= ~0xff;
Cmnd->result |= Cmnd->SCp.Status;
if (Cmnd->SCp.Status != GOOD) {
isp_prt(isp, ISP_LOGDEBUG0, "%d.%d.%d: cmd 0x%x finishes with status 0x%x", XS_CHANNEL(Cmnd), XS_TGT(Cmnd), XS_LUN(Cmnd), XS_CDBP(Cmnd)[0] & 0xff, Cmnd->SCp.Status);
if (Cmnd->SCp.Status == SCSI_QFULL) {
ISP_DATA(isp, XS_CHANNEL(Cmnd))->qfdelay = 2 * ISP_WATCH_TPS;
/*
* Too many hangups in the midlayer
*/
isplinux_append_to_waitq(isp, Cmnd);
return;
}
}
Cmnd->resid = XS_RESID(Cmnd);
if (Cmnd->underflow > (Cmnd->request_bufflen - Cmnd->resid)) {
XS_SETERR(Cmnd, DID_ERROR);
}
/*
* Queue command on completion queue.
*/
if (isp->isp_osinfo.dqnext == NULL) {
isp->isp_osinfo.dqnext = Cmnd;
} else {
isp->isp_osinfo.dqtail->host_scribble = (unsigned char *) Cmnd;
}
isp->isp_osinfo.dqtail = Cmnd;
Cmnd->host_scribble = NULL;
}
/*
* Error handling routines
*/
int
isplinux_abort(Scsi_Cmnd *Cmnd)
{
ispsoftc_t *isp;
uint32_t handle;
unsigned long flags;
if (Cmnd == NULL || XS_HOST(Cmnd) == NULL) {
return (FAILED);
}
isp = XS_ISP(Cmnd);
ISP_DRIVER_CTL_ENTRY_LOCK(isp);
ISP_LOCKU_SOFTC(isp);
handle = isp_find_handle(isp, Cmnd);
if (handle == 0) {
int wqfnd = 0;
Scsi_Cmnd *NewCmnd = isp_remove_from_waitq(Cmnd);
if (NewCmnd == NULL) {
NewCmnd = isplinux_remove_from_doneq(Cmnd);
wqfnd++;
}
ISP_UNLKU_SOFTC(isp);
isp_prt(isp, ISP_LOGINFO, "isplinux_abort: found %d:%p for non-running cmd for %d.%d.%d",
wqfnd, NewCmnd, XS_CHANNEL(Cmnd), XS_TGT(Cmnd), XS_LUN(Cmnd));
if (NewCmnd == NULL) {
ISP_DRIVER_CTL_EXIT_LOCK(isp);
return (FAILED);
}
} else {
ISP_DATA(isp, XS_CHANNEL(Cmnd))->qfdelay = ISP_WATCH_TPS;
if (isp_control(isp, ISPCTL_ABORT_CMD, Cmnd)) {
ISP_UNLKU_SOFTC(isp);
ISP_DRIVER_CTL_EXIT_LOCK(isp);
return (FAILED);
}
if (isp->isp_nactive > 0) {
isp->isp_nactive--;
}
isp_destroy_handle(isp, handle);
ISP_UNLKU_SOFTC(isp);
ISP_DRIVER_CTL_EXIT_LOCK(isp);
isp_prt(isp, ISP_LOGINFO, "isplinux_abort: aborted running cmd (handle 0x%x) for %d.%d.%d",
handle, XS_CHANNEL(Cmnd), XS_TGT(Cmnd), XS_LUN(Cmnd));
}
Cmnd->result = DID_ABORT << 16;
ISP_LOCK_SCSI_DONE(isp);
(*Cmnd->scsi_done)(Cmnd);
ISP_UNLK_SCSI_DONE(isp);
return (SUCCESS);
}
int
isplinux_bdr(Scsi_Cmnd *Cmnd)
{
ispsoftc_t *isp;
int r;
unsigned long flags;
if (Cmnd == NULL || XS_HOST(Cmnd) == NULL) {
return (FAILED);
}
isp = XS_ISP(Cmnd);
ISP_DRIVER_CTL_ENTRY_LOCK(isp);
ISP_LOCKU_SOFTC(isp);
r = isp_control(isp, ISPCTL_RESET_DEV, XS_CHANNEL(Cmnd), XS_TGT(Cmnd));
ISP_UNLKU_SOFTC(isp);
ISP_DRIVER_CTL_EXIT_LOCK(isp);
isp_prt(isp, ISP_LOGINFO, "Bus Device Reset %successfully sent to %d.%d.%d",
r == 0? "s" : "uns", XS_CHANNEL(Cmnd), XS_TGT(Cmnd), XS_LUN(Cmnd));
return ((r == 0)? SUCCESS : FAILED);
}
int
isplinux_sreset(Scsi_Cmnd *Cmnd)
{
ispsoftc_t *isp;
int r;
fcparam *fcp;
unsigned long flags;
if (Cmnd == NULL || XS_HOST(Cmnd) == NULL) {
return (FAILED);
}
isp = XS_ISP(Cmnd);
fcp = FCPARAM(isp, XS_CHANNEL(Cmnd));
ISP_DRIVER_CTL_ENTRY_LOCK(isp);
ISP_LOCKU_SOFTC(isp);
ISP_DATA(isp, XS_CHANNEL(Cmnd))->qfdelay = ISP_WATCH_TPS;
if (IS_FC(isp) && fcp->isp_fwstate == FW_READY && fcp->isp_loopstate == LOOP_READY && fcp->isp_topo == TOPO_F_PORT) {
ISP_UNLKU_SOFTC(isp);
ISP_DRIVER_CTL_EXIT_LOCK(isp);
isp_prt(isp, ISP_LOGINFO, "SCSI Bus Reset request ignored");
return (SUCCESS);
}
r = isp_control(isp, ISPCTL_RESET_DEV, XS_CHANNEL(Cmnd), XS_TGT(Cmnd));
ISP_UNLKU_SOFTC(isp);
ISP_DRIVER_CTL_EXIT_LOCK(isp);
isp_prt(isp, ISP_LOGINFO, "Chan %d SCSI Bus Reset %successful", XS_CHANNEL(Cmnd), r == 0? "s" : "uns");
return ((r == 0)? SUCCESS : FAILED);
}
/*
* We call completion on any commands owned here-
* except the one we were called with.
*/
int
isplinux_hreset(Scsi_Cmnd *Cmnd)
{
Scsi_Cmnd *tmp, *dq, *wq, *xqf, *xql;
ispsoftc_t *isp;
uint32_t handle;
unsigned long flags;
if (Cmnd == NULL || XS_HOST(Cmnd) == NULL) {
return (FAILED);
}
isp = XS_ISP(Cmnd);
isp_prt(isp, ISP_LOGINFO, "Resetting Host Adapter");
ISP_DRIVER_CTL_ENTRY_LOCK(isp);
ISP_LOCKU_SOFTC(isp);
/*
* Save pending, running, and completed commands.
*/
xql = xqf = NULL;
for (handle = 1; handle <= isp->isp_maxcmds; handle++) {
tmp = isp_find_xs(isp, handle);
if (tmp == NULL) {
continue;
}
isp_destroy_handle(isp, handle);
tmp->host_scribble = NULL;
if (xqf) {
xql->host_scribble = (unsigned char *) tmp;
} else {
xqf = xql = tmp;
}
xql = tmp;
}
dq = isp->isp_osinfo.dqnext;
isp->isp_osinfo.dqnext = NULL;
wq = isp->isp_osinfo.wqnext;
isp->isp_osinfo.wqnext = NULL;
isp->isp_nactive = 0;
(void) isplinux_reinit(isp);
ISP_UNLKU_SOFTC(isp);
ISP_DRIVER_CTL_EXIT_LOCK(isp);
/*
* Call completion on the detritus, skipping the one we were called with.
*/
while ((tmp = xqf) != NULL) {
xqf = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
if (tmp == Cmnd) {
continue;
}
tmp->result = DID_RESET << 16;
if (tmp->scsi_done) {
ISP_LOCK_SCSI_DONE(isp);
(*tmp->scsi_done)(tmp);
ISP_UNLK_SCSI_DONE(isp);
}
}
while ((tmp = wq) != NULL) {
wq = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
if (tmp == Cmnd) {
continue;
}
tmp->result = DID_RESET << 16;
if (tmp->scsi_done) {
ISP_LOCK_SCSI_DONE(isp);
(*tmp->scsi_done)(tmp);
ISP_UNLK_SCSI_DONE(isp);
}
}
while ((tmp = dq) != NULL) {
dq = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
if (tmp == Cmnd) {
continue;
}
tmp->result = DID_RESET << 16;
if (tmp->scsi_done) {
ISP_LOCK_SCSI_DONE(isp);
(*tmp->scsi_done)(tmp);
ISP_UNLK_SCSI_DONE(isp);
}
}
Cmnd->result = DID_RESET << 16;
return (SUCCESS);
}
#ifdef ISP_TARGET_MODE
int
isp_init_target(ispsoftc_t *isp)
{
int i;
void *pool, *npool;
unsigned long flags;
static const uint8_t inqdsd[DEFAULT_INQSIZE] = {
0x7f, 0x00, 0x03, 0x02, 0x1c, 0x00, 0x00, 0x00,
'L', 'I', 'N', 'U', 'X', 'I', 'S', 'P',
' ', 'T', 'A', 'R', 'G', 'E', 'T', ' ',
'D', 'E', 'V', 'I', 'C', 'E', ' ', ' '
};
pool = isp_kzalloc(NTGT_CMDS * TMD_SIZE, GFP_KERNEL);
if (pool == NULL) {
isp_prt(isp, ISP_LOGERR, "cannot allocate TMD structures");
return (-ENOMEM);
}
npool = isp_kzalloc(N_NOTIFIES * sizeof (isp_notify_t), GFP_KERNEL);
if (npool == NULL) {
isp_prt(isp, ISP_LOGERR, "cannot allocate TMD NOTIFY structures");
isp_kfree(pool, NTGT_CMDS * TMD_SIZE);
return (-ENOMEM);
}
sema_init(&isp->isp_osinfo.tgt_inisem, 1);
ISP_LOCK_SOFTC(isp);
isp->isp_osinfo.pool = pool;
for (i = 0; i < NTGT_CMDS-1; i++) {
isp->isp_osinfo.pool[i].cd_next = &isp->isp_osinfo.pool[i+1];
}
isp->isp_osinfo.npool = npool;
for (i = 0; i < N_NOTIFIES-1; i++) {
isp->isp_osinfo.npool[i].notify.nt_lreserved = &isp->isp_osinfo.npool[i+1];
}
MEMZERO(isp->isp_osinfo.auxbmap, sizeof (isp->isp_osinfo.auxbmap));
MEMCPY(isp->isp_osinfo.inqdata, inqdsd, DEFAULT_INQSIZE);
isp->isp_osinfo.pending_t = NULL;
isp->isp_osinfo.tfreelist = isp->isp_osinfo.pool;
isp->isp_osinfo.bfreelist = &isp->isp_osinfo.pool[NTGT_CMDS-1];
isp->isp_osinfo.nfreelist = isp->isp_osinfo.npool;
ISP_UNLK_SOFTC(isp);
return (0);
}
void
isp_attach_target(ispsoftc_t *isp)
{
hba_register_t hba;
hba.r_identity = isp;
snprintf(hba.r_name, sizeof (hba.r_name), ISP_NAME);
hba.r_inst = isp->isp_unit;
hba.r_version = QR_VERSION;
hba.r_action = isp_taction;
hba.r_locator = isp->isp_osinfo.device_id;
hba.r_nchannels = isp->isp_nchan;
if (IS_FC(isp)) {
hba.r_type = R_FC;
} else{
hba.r_type = R_SPI;
}
hba.r_private = NULL;
ISP_PARENT_TARGET(QOUT_HBA_REG, &hba);
}
void
isp_deinit_target(ispsoftc_t *isp)
{
void *pool, *npool;
unsigned long flags;
ISP_LOCK_SOFTC(isp);
pool = isp->isp_osinfo.pool;
isp->isp_osinfo.pool = NULL;
npool = isp->isp_osinfo.npool;
isp->isp_osinfo.npool = NULL;
ISP_UNLK_SOFTC(isp);
if (pool) {
isp_kfree(pool, NTGT_CMDS * TMD_SIZE);
}
if (npool) {
isp_kfree(npool, N_NOTIFIES * sizeof (isp_notify_t));
}
}
void
isp_detach_target(ispsoftc_t *isp)
{
hba_register_t hba;
DECLARE_MUTEX_LOCKED(rsem);
hba.r_identity = isp;
snprintf(hba.r_name, sizeof (hba.r_name), ISP_NAME);
hba.r_inst = isp->isp_unit;
hba.r_version = QR_VERSION;
hba.r_action = isp_taction;
hba.r_nchannels = isp->isp_nchan;
if (IS_FC(isp)) {
hba.r_type = R_FC;
} else{
hba.r_type = R_SPI;
}
hba.r_private = &rsem;
ISP_PARENT_TARGET(QOUT_HBA_UNREG, &hba);
down(&rsem);
}
static void
isp_tgt_tq(ispsoftc_t *isp)
{
isp_notify_t *ins;
tmd_cmd_t *tmd;
tmd_xact_t *xact;
unsigned long flags;
ISP_LOCK_SOFTC(isp);
ins = isp->isp_osinfo.pending_n;
if (ins) {
isp->isp_osinfo.pending_n = NULL;
}
tmd = isp->isp_osinfo.pending_t;
if (tmd) {
isp->isp_osinfo.pending_t = NULL;
}
xact = isp->isp_osinfo.pending_x;
if (xact) {
isp->isp_osinfo.pending_x = NULL;
}
ISP_UNLK_SOFTC(isp);
while (ins != NULL) {
isp_notify_t *next = ins->notify.nt_lreserved;
ins->notify.nt_lreserved = NULL;
isp_prt(isp, ISP_LOGTDEBUG2, "isp_tgt_tq -> notify 0x%x", ins->notify.nt_ncode);
ISP_PARENT_TARGET(QOUT_NOTIFY, ins);
ins = next;
}
while (tmd != NULL) {
tmd_cmd_t *next = tmd->cd_next;
tmd->cd_next = NULL;
isp_prt(isp, ISP_LOGTDEBUG2, "isp_tgt_tq[%llx] -> code 0x%x", tmd->cd_tagval, tmd->cd_action);
ISP_PARENT_TARGET(tmd->cd_action, tmd);
tmd = next;
}
while (xact != NULL) {
tmd_xact_t *next = xact->td_lprivate;
xact->td_lprivate = NULL;
ISP_PARENT_TARGET(QOUT_TMD_DONE, xact);
xact = next;
}
}
static inline tmd_cmd_t *
isp_find_tmd(ispsoftc_t *isp, uint64_t tagval)
{
int i;
tmd_cmd_t *tmd = isp->isp_osinfo.pool;
if (tmd == NULL || tagval == TAG_ANY) {
return (NULL);
}
for (i = 0; i < NTGT_CMDS; i++) {
if ((tmd->cd_lflags & CDFL_BUSY) && tmd->cd_tagval == tagval) {
return (tmd);
}
tmd++;
}
return (NULL);
}
static void
isp_add_wwn_entry(ispsoftc_t *isp, int chan, uint64_t ini, uint16_t nphdl, uint32_t s_id)
{
fcparam *fcp;
fcportdb_t *lp;
int i;
/*
* Make sure the addition of a new target mode entry doesn't duplicate entries
* with the same N-Port handles, the same portids or the same Port WWN.
*/
fcp = FCPARAM(isp, chan);
for (i = 0; i < MAX_FC_TARG; i++) {
lp = &fcp->portdb[i];
if (lp->target_mode == 0) {
continue;
}
if (nphdl != NIL_HANDLE && lp->handle == nphdl) {
break;
}
if (s_id != PORT_NONE && lp->portid == s_id) {
break;
}
if (VALID_INI(ini) && lp->port_wwn == ini) {
break;
}
}
if (i < MAX_FC_TARG) {
if (lp->portid != s_id || (VALID_INI(lp->port_wwn) && lp->port_wwn != ini)) {
i = ISP_LOGWARN;
} else {
i = ISP_LOGTINFO;
}
if (lp->portid == s_id && VALID_INI(lp->port_wwn) && (lp->port_wwn == ini || ini == INI_NONE) && lp->handle == nphdl) {
isp_prt(isp, i, "%s: Chan %d IID 0x%016llx N-Port Handle 0x%02x Port ID 0x%06x reentered", __FUNCTION__, chan,
(unsigned long long) lp->port_wwn, lp->handle, lp->portid);
} else {
isp_prt(isp, i, "%s: Chan %d IID 0x%016llx N-Port Handle 0x%02x Port ID 0x%06x overwrites IID 0x%016llx N-Port Handle 0x%02x Port Id 0x%06x",
__FUNCTION__, chan, (unsigned long long) ini, nphdl, s_id, (unsigned long long) lp->port_wwn, lp->handle, lp->portid);
}
lp->portid = s_id;
if (VALID_INI(ini)) {
lp->port_wwn = ini;
}
lp->handle = nphdl;
return;
}
while (--i >= 0) {
if (i >= FL_ID && i <= SNS_ID) {
continue;
}
if (fcp->portdb[i].target_mode == 1) {
continue;
}
if (fcp->portdb[i].state == FC_PORTDB_STATE_NIL) {
break;
}
}
if (i < 0) {
isp_prt(isp, ISP_LOGWARN, "%s: Chan %d IID 0x%016llx N-Port Handle 0x%02x Port ID 0x%06x- no room in port database",
__FUNCTION__, chan, ini, nphdl, s_id);
return;
}
lp = &fcp->portdb[i];
MEMZERO(lp, sizeof (fcportdb_t));
lp->target_mode = 1;
lp->handle = nphdl;
lp->portid = s_id;
lp->port_wwn = ini;
isp_prt(isp, ISP_LOGTINFO, "%s: Chan %d IID 0x%016llx N-Port Handle 0x%02x Port ID 0x%06x added",
__FUNCTION__, chan, (unsigned long long) ini, nphdl, s_id);
}
static void
isp_del_wwn_entry(ispsoftc_t *isp, int chan, uint64_t ini, uint16_t nphdl, uint32_t s_id)
{
fcparam *fcp;
fcportdb_t *lp;
int i;
fcp = FCPARAM(isp, chan);
for (i = 0; i < MAX_FC_TARG; i++) {
lp = &fcp->portdb[i];
if (lp->target_mode == 0) {
continue;
}
if (ini == INI_ANY && nphdl == NIL_HANDLE && s_id == PORT_ANY) {
isp_prt(isp, ISP_LOGTINFO, "%s: Chan %d IID 0x%016llx N-Port Handle 0x%x Port ID 0x%06x cleared due to wildcard call",
__FUNCTION__, chan, (unsigned long long) lp->port_wwn, lp->handle, lp->portid);
MEMZERO(&fcp->portdb[i], sizeof (fcportdb_t));
continue;
}
if ((s_id == PORT_ANY || lp->portid == s_id) && lp->handle == nphdl) {
break;
}
}
if (ini == INI_ANY && nphdl == NIL_HANDLE && s_id == PORT_ANY) {
return;
}
if (i == MAX_FC_TARG) {
isp_prt(isp, ISP_LOGTINFO, "%s: Chan %d IID 0x%016llx N-Port Handle 0x%x Port ID 0x%06x cannot be found to be cleared",
__FUNCTION__, chan, (unsigned long long) lp->port_wwn, nphdl, lp->portid);
} else {
isp_prt(isp, ISP_LOGTINFO, "%s: Chan %d IID 0x%016llx N-Port Handle 0x%x Port ID 0x%06x cleared",
__FUNCTION__, chan, (unsigned long long) lp->port_wwn, nphdl, lp->portid);
MEMZERO(&fcp->portdb[i], sizeof (fcportdb_t));
}
}
static inline int
isp_find_pdb_by_loopid(ispsoftc_t *isp, int chan, uint32_t loopid, fcportdb_t **lptr)
{
fcparam *fcp;
int i;
fcp = FCPARAM(isp, chan);
for (i = MAX_FC_TARG-1; i >= 0; i--) {
fcportdb_t *lp = &fcp->portdb[i];
if (lp->target_mode == 0) {
continue;
}
if (lp->handle == loopid) {
*lptr = lp;
return (1);
}
}
return (0);
}
static inline int
isp_find_pdb_by_sid(ispsoftc_t *isp, int chan, uint32_t sid, fcportdb_t **lptr)
{
fcparam *fcp;
int i;
if (chan >= isp->isp_nchan) {
return (0);
}
fcp = FCPARAM(isp, chan);
for (i = MAX_FC_TARG-1; i >= 0; i--) {
fcportdb_t *lp = &fcp->portdb[i];
if (lp->target_mode == 0) {
continue;
}
if (lp->portid == sid) {
*lptr = lp;
return (1);
}
}
return (0);
}
static void
isp_tgt_dump_pdb(ispsoftc_t *isp, int chan)
{
fcparam *fcp;
int i;
if (chan >= isp->isp_nchan) {
return;
}
fcp = FCPARAM(isp, chan);
for (i = MAX_FC_TARG-1; i >= 0; i--) {
fcportdb_t *lp = &fcp->portdb[i];
if (lp->target_mode == 0) {
continue;
}
isp_prt(isp, ISP_LOGTINFO, "PDB[%d]: Chan %d 0x%016llx Port-ID 0x%06x N-Port Handle 0x%04x", i, chan, (unsigned long long) lp->port_wwn, lp->portid, lp->handle);
}
}
static void
isp_taction(qact_e action, void *arg)
{
tmd_cmd_t *tmd;
hba_register_t *hp;
enadis_t *ep;
ispsoftc_t *isp = NULL;
unsigned long flags;
switch (action) {
case QIN_HBA_REG:
hp = (hba_register_t *) arg;
isp = hp->r_identity;
if (isp == NULL) {
printk(KERN_ERR "null isp @ %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
break;
}
isp_prt(isp, ISP_LOGINFO, "completed target registration");
ISP_LOCK_SOFTC(isp);
isp->isp_osinfo.hcb = 1;
ISP_UNLK_SOFTC(isp);
break;
case QIN_GETINFO:
{
info_t *ip = arg;
isp = ip->i_identity;
if (ip->i_type == I_FC) {
fcparam *fcp = FCPARAM(isp, ip->i_channel);
ip->i_id.fc.wwnn_nvram = fcp->isp_wwnn_nvram;
ip->i_id.fc.wwpn_nvram = fcp->isp_wwpn_nvram;
ip->i_id.fc.wwnn = fcp->isp_wwnn;
ip->i_id.fc.wwpn = fcp->isp_wwpn;
ip->i_error = 0;
} else if (ip->i_type == I_SPI) {
sdparam *sdp = SDPARAM(isp, ip->i_channel);
ip->i_id.spi.iid = sdp->isp_initiator_id;
ip->i_error = 0;
} else {
ip->i_error = -EINVAL;
}
break;
}
case QIN_SETINFO:
{
info_t *ip = arg;
isp = ip->i_identity;
if (ip->i_type == I_FC) {
FCPARAM(isp, ip->i_channel)->isp_wwnn = ip->i_id.fc.wwnn;
FCPARAM(isp, ip->i_channel)->isp_wwpn = ip->i_id.fc.wwpn;
ip->i_error = 0;
} else {
ip->i_error = -EINVAL;
}
break;
}
case QIN_GETDLIST:
{
fc_dlist_t *ua = arg;
int rv, nph, nphe, lim, chan;
uint64_t wwpn;
isp = ua->d_identity;
if (IS_SCSI(isp)) {
ua->d_error = -EINVAL;
break;
}
lim = ua->d_count;
chan = ua->d_channel;
ua->d_count = 0;
if (ISP_CAP_2KLOGIN(isp)) {
nphe = NPH_MAX_2K;
} else {
nphe = NPH_MAX;
}
for (rv = 0, nph = 1; ua->d_count < lim && nph != nphe; nph++) {
ISP_LOCKU_SOFTC(isp);
rv = isp_control(isp, ISPCTL_GET_NAMES, chan, nph, NULL, &wwpn);
ISP_UNLKU_SOFTC(isp);
if (rv == 0 && wwpn != (uint64_t) 0) {
ua->d_wwpns[ua->d_count++] = wwpn;
}
}
ua->d_error = 0;
break;
}
case QIN_ENABLE:
ep = arg;
isp = ep->en_hba;
if (isp == NULL) {
printk(KERN_ERR "null isp @ %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
break;
}
ep->en_error = isp_enable_lun(isp, ep->en_chan, ep->en_lun);
ISP_PARENT_TARGET(QOUT_ENABLE, ep);
break;
case QIN_DISABLE:
ep = arg;
isp = ep->en_hba;
if (isp == NULL) {
printk(KERN_ERR "null isp @ %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
break;
}
ep->en_error = isp_disable_lun(isp, ep->en_chan, ep->en_lun);
ISP_PARENT_TARGET(QOUT_DISABLE, ep);
ISP_LOCK_SOFTC(isp);
if (ep->en_error == 0) {
(void) isp_target_async(isp, 0, ASYNC_LOOP_DOWN);
}
ISP_UNLK_SOFTC(isp);
break;
case QIN_TMD_CONT:
{
tmd_xact_t *xact = arg;
tmd = xact->td_cmd;
isp = tmd->cd_hba;
isp_target_start_ctio(isp, arg);
break;
}
case QIN_TMD_FIN:
tmd = (tmd_cmd_t *) arg;
isp = tmd->cd_hba;
if (isp == NULL) {
printk(KERN_ERR "null isp @ %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
break;
}
ISP_LOCK_SOFTC(isp);
isp_prt(isp, ISP_LOGTDEBUG1, "freeing tmd %p [%llx]", tmd, tmd->cd_tagval);
if (tmd->cd_lflags & CDFL_RESRC_FILL) {
if (isp_target_putback_atio(isp, tmd)) {
ISP_THREAD_EVENT(isp, ISP_THREAD_FC_PUTBACK, tmd, 0, __FUNCTION__, __LINE__);
ISP_UNLK_SOFTC(isp);
break;
}
}
if (tmd->cd_lflags & CDFL_NEED_CLNUP) {
tmd->cd_lflags &= ~CDFL_NEED_CLNUP;
isp_prt(isp, ISP_LOGTINFO, "Terminating [%llx] on FIN", (unsigned long long) tmd->cd_tagval);
(void) isp_terminate_cmd(isp, tmd);
}
tmd->cd_next = NULL;
if (isp->isp_osinfo.tfreelist) {
isp->isp_osinfo.bfreelist->cd_next = tmd;
} else {
isp->isp_osinfo.tfreelist = tmd;
}
isp->isp_osinfo.bfreelist = tmd; /* remember to move the list tail pointer */
isp_prt(isp, ISP_LOGTDEBUG1, "DONE freeing tmd %p [%llx]", tmd, tmd->cd_tagval);
ISP_UNLK_SOFTC(isp);
break;
case QIN_NOTIFY_ACK:
{
isp_notify_t *ins = (isp_notify_t *) arg;
isp = ins->notify.nt_hba;
if (isp == NULL) {
printk(KERN_ERR "null isp @ %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
break;
}
ISP_LOCK_SOFTC(isp);
switch (ins->notify.nt_ncode) {
case NT_HBA_RESET:
case NT_LINK_UP:
case NT_LINK_DOWN:
isp_prt(isp, ISP_LOGINFO, "s/w notify code %x acked", ins->notify.nt_ncode);
break;
default:
if (isp->isp_state != ISP_RUNSTATE) {
isp_prt(isp, ISP_LOGINFO, "[%llx] Notify Code 0x%x (qevalid=%d) acked- h/w not ready (dropping)",
ins->notify.nt_tagval, ins->notify.nt_ncode, ins->qevalid);
} else if (IS_24XX(isp) && ins->qevalid && ((isphdr_t *)ins->qentry)->rqs_entry_type == RQSTYPE_ATIO) {
ct7_entry_t local, *cto = &local;
at7_entry_t *aep = (at7_entry_t *)ins->qentry;
fcportdb_t *lp;
uint32_t sid;
uint16_t nphdl;
sid = (aep->at_hdr.s_id[0] << 16) | (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2];
if (isp_find_pdb_by_sid(isp, ins->notify.nt_channel, sid, &lp)) {
nphdl = lp->handle;
} else {
nphdl = NIL_HANDLE;
}
MEMZERO(&local, sizeof (local));
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7;
cto->ct_header.rqs_entry_count = 1;
cto->ct_nphdl = nphdl;
cto->ct_rxid = aep->at_rxid;
cto->ct_vpindex = ins->notify.nt_channel;
cto->ct_iid_lo = sid;
cto->ct_iid_hi = sid >> 16;
cto->ct_oxid = aep->at_hdr.ox_id;
cto->ct_flags = CT7_SENDSTATUS|CT7_NOACK|CT7_NO_DATA|CT7_FLAG_MODE1;
cto->ct_flags |= (aep->at_ta_len >> 12) << CT7_TASK_ATTR_SHIFT;
(void) isp_target_put_entry(isp, &local);
} else if (IS_24XX(isp) && ins->qevalid && ((isphdr_t *)ins->qentry)->rqs_entry_type == RQSTYPE_ABTS_RSP) {
abts_t *abt = (abts_t *)ins->qentry;
abts_rsp_t *rsp = (abts_rsp_t *)ins->qentry;
uint16_t rx_id, ox_id;
isp_prt(isp, ISP_LOGTINFO, "ABTS for 0x%x being BA_ACC'd", rsp->abts_rsp_rxid_abts);
rx_id = abt->abts_rx_id;
ox_id = abt->abts_ox_id;
rsp->abts_rsp_handle = rsp->abts_rsp_rxid_abts;
rsp->abts_rsp_r_ctl = BA_ACC;
MEMZERO(&rsp->abts_rsp_payload.ba_acc, sizeof (rsp->abts_rsp_payload.ba_acc));
rsp->abts_rsp_payload.ba_acc.aborted_rx_id = rx_id;
rsp->abts_rsp_payload.ba_acc.aborted_ox_id = ox_id;
rsp->abts_rsp_payload.ba_acc.high_seq_cnt = 0xffff;
isp_notify_ack(isp, ins->qentry);
} else if (ins->notify.nt_need_ack) {
isp_prt(isp, ISP_LOGDEBUG0, "[%llx] Notify Code 0x%x (qevalid=%d) being acked", ins->notify.nt_tagval, ins->notify.nt_ncode, ins->qevalid);
if (ins->qevalid) {
isp_notify_ack(isp, ins->qentry);
} else {
isp_notify_ack(isp, NULL);
}
}
break;
}
ins->notify.nt_lreserved = isp->isp_osinfo.nfreelist;
isp->isp_osinfo.nfreelist = ins;
ISP_UNLK_SOFTC(isp);
break;
}
case QIN_HBA_UNREG:
hp = (hba_register_t *) arg;
isp = hp->r_identity;
if (isp == NULL) {
printk(KERN_ERR "null isp @ %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
break;
}
isp_prt(isp, ISP_LOGINFO, "completed target unregistration");
ISP_LOCK_SOFTC(isp);
isp->isp_osinfo.hcb = 0;
if (hp->r_private) {
struct semaphore *rsemap = hp->r_private;
up(rsemap);
}
ISP_UNLK_SOFTC(isp);
/* force ourselves to not run any queues now */
isp = NULL;
break;
default:
printk(KERN_ERR "isp_taction: unknown action %x, arg %p\n", action, arg);
break;
}
if (isp) {
isp_tgt_tq(isp);
}
}
static int
lunenabled(ispsoftc_t *isp, uint16_t bus, uint16_t lun)
{
tgt_enalun_t *axl = isp->isp_osinfo.luns;
while (axl) {
if (axl->bus == bus && axl->lun == lun) {
return (1);
}
axl = axl->next;
}
return (0);
}
static int
nolunsenabled(ispsoftc_t *isp, uint16_t bus)
{
tgt_enalun_t *axl = isp->isp_osinfo.luns;
while (axl) {
if (axl->bus == bus) {
return (0);
}
axl = axl->next;
}
return (1);
}
static inline void
addlun(ispsoftc_t *isp, tgt_enalun_t *axl, uint16_t bus, uint16_t lun)
{
axl->lun = lun;
axl->bus = bus;
axl->next = isp->isp_osinfo.luns;
isp->isp_osinfo.luns = axl;
}
static inline tgt_enalun_t *
remlun(ispsoftc_t *isp, uint16_t bus, uint16_t lun)
{
tgt_enalun_t *axl, *axy = NULL;
axl = isp->isp_osinfo.luns;
if (axl == NULL) {
return (axy);
}
if (axl->lun == lun && axl->bus == bus) {
isp->isp_osinfo.luns = axl->next;
axy = axl;
} else {
while (axl->next) {
if (axl->next->lun == lun && axl->next->bus == bus) {
axy = axl->next;
axl->next = axy->next;
break;
}
axl = axl->next;
}
}
return (axy);
}
static void
isp_target_start_ctio(ispsoftc_t *isp, tmd_xact_t *xact)
{
void *qe;
uint32_t handle;
uint32_t nxti, optr;
uint8_t local[QENTRY_LEN];
unsigned long flags;
int32_t resid;
tmd_cmd_t *tmd = xact->td_cmd;
xact->td_lflags &= ~TDFL_ERROR;
xact->td_error = 0;
/*
* Check for commands that are already dead
*/
if (tmd->cd_lflags & CDFL_ABORTED) {
isp_prt(isp, ISP_LOGINFO, "[%llx] already ABORTED- not sending a CTIO", tmd->cd_tagval);
dump_stack();
xact->td_error = -ENXIO;
goto out;
}
/*
* If the transfer length is zero, we have to be sending status.
* If we're sending data, we have to have one and only one data
* direction set.
*/
if (xact->td_xfrlen == 0) {
if ((xact->td_hflags & TDFH_STSVALID) == 0) {
isp_prt(isp, ISP_LOGERR, "CTIO, no data, and no status is wrong");
dump_stack();
xact->td_error = -EINVAL;
goto out;
}
} else {
if ((xact->td_hflags & TDFH_DATA_MASK) == 0) {
isp_prt(isp, ISP_LOGERR, "data CTIO with no direction is wrong");
dump_stack();
xact->td_error = -EINVAL;
goto out;
}
if ((xact->td_hflags & TDFH_DATA_MASK) == TDFH_DATA_MASK) {
isp_prt(isp, ISP_LOGERR, "data CTIO with both directions is wrong (for now)");
dump_stack();
xact->td_error = -EINVAL;
goto out;
}
}
if ((xact->td_hflags & TDFH_STSVALID) && tmd->cd_scsi_status == SCSI_CHECK && (xact->td_hflags & TDFH_SNSVALID) && tmd->cd_sense[0] == 0) {
isp_prt(isp, ISP_LOGWARN, "[%llx] cdb0 0x%02x CHECK CONDITION but bogus sense 0x%x/0x%x/0x%x", tmd->cd_tagval, tmd->cd_cdb[0], tmd->cd_sense[0], tmd->cd_sense[12], tmd->cd_sense[13]);
}
MEMZERO(local, QENTRY_LEN);
/*
* Use this lock to protect tmd fields
*/
ISP_LOCK_SOFTC(isp);
/*
* Pre-increment cd_moved so we know how many bytes are actually in transit. If we actually fail to move
* the bytes, we'll subtract things out when we collect status.
*/
tmd->cd_moved += xact->td_xfrlen;
/*
* Set the residual to be equal to the total length less the amount previously moved plus this transfer size
*/
resid = tmd->cd_totlen - tmd->cd_moved;
/*
* We're either moving data or completing a command here (or both).
*/
if (IS_24XX(isp)) {
ct7_entry_t *cto = (ct7_entry_t *) local;
int tattr;
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7;
cto->ct_header.rqs_entry_count = 1;
cto->ct_nphdl = tmd->cd_nphdl;
cto->ct_rxid = AT2_GET_TAG(tmd->cd_tagval);
cto->ct_iid_lo = tmd->cd_portid;
cto->ct_iid_hi = tmd->cd_portid >> 16;
cto->ct_oxid = tmd->cd_oxid;
cto->ct_vpindex = tmd->cd_channel;
cto->ct_scsi_status = tmd->cd_scsi_status;
cto->ct_timeout = ISP_CT_TIMEOUT;
switch (tmd->cd_tagtype) {
case CD_SIMPLE_TAG:
tattr = FCP_CMND_TASK_ATTR_SIMPLE;
break;
case CD_HEAD_TAG:
tattr = FCP_CMND_TASK_ATTR_HEAD;
break;
case CD_ORDERED_TAG:
tattr = FCP_CMND_TASK_ATTR_ORDERED;
break;
case CD_ACA_TAG:
tattr = FCP_CMND_TASK_ATTR_ACA;
break;
default:
tattr = FCP_CMND_TASK_ATTR_UNTAGGED;
break;
}
cto->ct_flags = tattr << CT7_TASK_ATTR_SHIFT;
if (xact->td_xfrlen == 0) {
cto->ct_flags |= CT7_FLAG_MODE1 | CT7_NO_DATA | CT7_SENDSTATUS;
if ((xact->td_hflags & TDFH_SNSVALID) != 0) {
cto->rsp.m1.ct_resplen = cto->ct_senselen = min(TMD_SENSELEN, MAXRESPLEN_24XX);
MEMCPY(cto->rsp.m1.ct_resp, tmd->cd_sense, cto->ct_senselen);
cto->ct_scsi_status |= (FCP_SNSLEN_VALID << 8);
}
} else {
cto->ct_flags |= CT7_FLAG_MODE0;
if (xact->td_hflags & TDFH_DATA_IN) {
cto->ct_flags |= CT7_DATA_IN;
} else {
cto->ct_flags |= CT7_DATA_OUT;
}
if (xact->td_hflags & TDFH_STSVALID) {
cto->ct_flags |= CT7_SENDSTATUS;
}
}
if ((cto->ct_flags & CT7_SENDSTATUS) && resid) {
cto->ct_resid = resid;
if (resid < 0) {
cto->ct_scsi_status |= (FCP_RESID_OVERFLOW << 8);
} else {
cto->ct_scsi_status |= (FCP_RESID_UNDERFLOW << 8);
}
}
isp_prt(isp, ISP_LOGTDEBUG0, "CTIO7[%llx] scsi sts %x flags %x resid %d offset %u", tmd->cd_tagval, tmd->cd_scsi_status, cto->ct_flags, resid, xact->td_offset);
} else if (IS_FC(isp)) {
ct2_entry_t *cto = (ct2_entry_t *) local;
uint16_t *ssptr = NULL;
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
cto->ct_header.rqs_entry_count = 1;
if (ISP_CAP_2KLOGIN(isp)) {
((ct2e_entry_t *)cto)->ct_iid = tmd->cd_nphdl;
} else {
cto->ct_iid = tmd->cd_nphdl;
}
if (ISP_CAP_SCCFW(isp)) {
cto->ct_lun = L0LUN_TO_FLATLUN(tmd->cd_lun);
}
cto->ct_rxid = AT2_GET_TAG(tmd->cd_tagval);
if (cto->ct_rxid == 0) {
isp_prt(isp, ISP_LOGERR, "a tagval of zero is not acceptable");
xact->td_error = -EINVAL;
ISP_UNLK_SOFTC(isp);
goto out;
}
cto->ct_timeout = ISP_CT_TIMEOUT;
#if 0
/*
* I've had problems with this at varying times- dunno why
*/
cto->ct_flags = CT2_FASTPOST;
#endif
if (xact->td_xfrlen == 0) {
cto->ct_flags |= CT2_FLAG_MODE1 | CT2_NO_DATA | CT2_SENDSTATUS;
ssptr = &cto->rsp.m1.ct_scsi_status;
*ssptr = tmd->cd_scsi_status;
if ((xact->td_hflags & TDFH_SNSVALID) != 0) {
cto->rsp.m1.ct_senselen = min(TMD_SENSELEN, MAXRESPLEN);
MEMCPY(cto->rsp.m1.ct_resp, tmd->cd_sense, cto->rsp.m1.ct_senselen);
cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID;
}
} else {
cto->ct_flags |= CT2_FLAG_MODE0;
if (xact->td_hflags & TDFH_DATA_IN) {
cto->ct_flags |= CT2_DATA_IN;
} else {
cto->ct_flags |= CT2_DATA_OUT;
}
if (xact->td_hflags & TDFH_STSVALID) {
ssptr = &cto->rsp.m0.ct_scsi_status;
cto->ct_flags |= CT2_SENDSTATUS;
cto->rsp.m0.ct_scsi_status = tmd->cd_scsi_status;
/*
* It will be up to the low level mapping routine
* to check for sense data.
*/
}
}
if (ssptr && resid) {
cto->ct_resid = resid;
if (resid < 0) {
*ssptr |= CT2_DATA_OVER;
} else {
*ssptr |= CT2_DATA_UNDER;
}
}
if (cto->ct_flags & CT2_SENDSTATUS) {
cto->ct_flags |= CT2_CCINCR;
}
isp_prt(isp, ISP_LOGTDEBUG0, "CTIO2[%llx] scsi sts %x flags %x resid %d", tmd->cd_tagval, tmd->cd_scsi_status, cto->ct_flags, resid);
} else {
ct_entry_t *cto = (ct_entry_t *) local;
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO;
cto->ct_header.rqs_entry_count = 1;
cto->ct_iid = tmd->cd_iid;
cto->ct_tgt = tmd->cd_tgt;
cto->ct_lun = L0LUN_TO_FLATLUN(tmd->cd_lun);
cto->ct_fwhandle = AT_GET_HANDLE(tmd->cd_tagval);
cto->ct_timeout = ISP_CT_TIMEOUT;
if (AT_HAS_TAG(tmd->cd_tagval)) {
cto->ct_tag_val = AT_GET_TAG(tmd->cd_tagval);
cto->ct_flags |= CT_TQAE;
}
if (tmd->cd_flags & CDF_NODISC) {
cto->ct_flags |= CT_NODISC;
}
if (xact->td_xfrlen == 0) {
cto->ct_flags |= CT_NO_DATA | CT_SENDSTATUS;
cto->ct_scsi_status = tmd->cd_scsi_status;
} else {
if (xact->td_hflags & TDFH_STSVALID) {
cto->ct_flags |= CT_SENDSTATUS;
}
if (xact->td_hflags & TDFH_DATA_IN) {
cto->ct_flags |= CT_DATA_IN;
} else {
cto->ct_flags |= CT_DATA_OUT;
}
/*
* We assume we'll transfer what we say we'll transfer.
* Otherwise, the command is dead.
*/
if (xact->td_hflags & TDFH_STSVALID) {
cto->ct_resid = resid;
}
}
if (cto->ct_flags & CT_SENDSTATUS) {
cto->ct_flags |= CT_CCINCR;
}
isp_prt(isp, ISP_LOGTDEBUG0, "CTIO[%llx] scsi sts %x resid %d cd_lflags %x", tmd->cd_tagval, tmd->cd_scsi_status, resid, xact->td_hflags);
}
if (isp_getrqentry(isp, &nxti, &optr, &qe)) {
isp_prt(isp, ISP_LOGWARN, "%s: request queue overflow", __FUNCTION__);
xact->td_error = -ENOMEM;
ISP_UNLK_SOFTC(isp);
goto out;
}
if (isp_save_xs_tgt(isp, xact, &handle)) {
isp_prt(isp, ISP_LOGERR, "isp_target_start_ctio: No XFLIST pointers");
xact->td_error = -ENOMEM;
ISP_UNLK_SOFTC(isp);
goto out;
}
if (IS_24XX(isp)) {
((ct7_entry_t *) local)->ct_syshandle = handle;
} else if (IS_FC(isp)) {
((ct2_entry_t *) local)->ct_syshandle = handle;
} else {
((ct_entry_t *) local)->ct_syshandle = handle;
}
/*
* Call the dma setup routines for this entry (and any subsequent
* CTIOs) if there's data to move, and then tell the f/w it's got
* new things to play with. As with isp_start's usage of DMA setup,
* any swizzling is done in the machine dependent layer. Because
* of this, we put the request onto the queue area first in native
* format.
*/
switch (ISP_DMASETUP(isp, (XS_T *)xact, (ispreq_t *) local, &nxti, optr)) {
case CMD_QUEUED:
ISP_ADD_REQUEST(isp, nxti);
ISP_UNLK_SOFTC(isp);
return;
case CMD_EAGAIN:
xact->td_error = -ENOMEM;
isp_destroy_tgt_handle(isp, handle);
break;
case CMD_COMPLETE:
isp_destroy_tgt_handle(isp, handle);
break;
default:
xact->td_error = -EFAULT; /* probably dma mapping failure */
isp_destroy_tgt_handle(isp, handle);
break;
}
ISP_UNLK_SOFTC(isp);
out:
if (xact->td_error) {
xact->td_lflags |= TDFL_ERROR;
}
if ((tmd->cd_lflags & CDFL_LCL) == 0) {
CALL_PARENT_XFR(isp, xact);
}
}
static void
isp_lcl_respond(ispsoftc_t *isp, void *aep, tmd_cmd_t *tmd)
{
uint8_t *cdbp;
if (IS_24XX(isp)) {
cdbp = ((at7_entry_t *)aep)->at_cmnd.cdb_dl.sf.fcp_cmnd_cdb;
} else if (IS_FC(isp)) {
cdbp = ((at2_entry_t *)aep)->at_cdb;
} else {
cdbp = ((at_entry_t *)aep)->at_cdb;
}
if (cdbp[0] == INQUIRY && L0LUN_TO_FLATLUN(tmd->cd_lun) == 0) {
if (cdbp[1] == 0 && cdbp[2] == 0 && cdbp[3] == 0 && cdbp[5] == 0) {
tmd_xact_t *xact;
struct scatterlist *dp;
int amt, i;
for (i = 0; i < N_TGT_AUX; i++) {
if (ISP_BTST(isp->isp_osinfo.auxbmap, i)) {
break;
}
}
if (i == N_TGT_AUX) {
if (IS_24XX(isp)) {
isp_endcmd(isp, aep, tmd->cd_nphdl, tmd->cd_channel, SCSI_BUSY, 0);
} else {
isp_endcmd(isp, aep, SCSI_BUSY, 0);
}
return;
}
ISP_BSET(isp->isp_osinfo.auxbmap, i);
xact = &isp->isp_osinfo.auxinfo[i].xact;
dp = &isp->isp_osinfo.auxinfo[i].sg;
MEMZERO(dp, sizeof (*dp));
dp->page = virt_to_page(isp->isp_osinfo.inqdata);
dp->offset = offset_in_page(isp->isp_osinfo.inqdata);
dp->length = DEFAULT_INQSIZE;
xact->td_data = dp;
xact->td_xfrlen = min(DEFAULT_INQSIZE, tmd->cd_totlen);
if ((amt = cdbp[4]) == 0) {
amt = 256;
}
if (xact->td_xfrlen > amt) {
xact->td_xfrlen = amt;
}
xact->td_hflags |= TDFH_DATA_IN|TDFH_STSVALID;
xact->td_cmd = tmd;
xact->td_offset = 0;
xact->td_error = 0;
xact->td_lflags = 0;
tmd->cd_scsi_status = 0;
tmd->cd_lflags |= CDFL_LCL;
ISP_DROP_LK_SOFTC(isp);
isp_target_start_ctio(isp, xact);
ISP_IGET_LK_SOFTC(isp);
return;
}
/*
* Illegal field in CDB
* 0x24 << 24 | 0x5 << 12 | ECMD_SVALID | SCSI_CHECK
*/
if (IS_24XX(isp)) {
isp_endcmd(isp, aep, tmd->cd_nphdl, tmd->cd_channel, 0x24005102, 0);
} else {
isp_endcmd(isp, aep, 0x24005102, 0);
}
} else if (L0LUN_TO_FLATLUN(tmd->cd_lun) == 0) {
/*
* Not Ready, Cause Not Reportable
*
* 0x4 << 24 | 0x2 << 12 | ECMD_SVALID | SCSI_CHECK
*/
if (IS_24XX(isp)) {
isp_endcmd(isp, aep, tmd->cd_nphdl, tmd->cd_channel, 0x04002102, 0);
} else {
isp_endcmd(isp, aep, 0x04002102, 0);
}
} else {
/*
* Logical Unit Not Supported:
* 0x25 << 24 | 0x5 << 12 | ECMD_SVALID | SCSI_CHECK
*/
if (IS_24XX(isp)) {
isp_endcmd(isp, aep, tmd->cd_nphdl, tmd->cd_channel, 0x25005102, 0);
} else {
isp_endcmd(isp, aep, 0x25005102, 0);
}
}
MEMZERO(tmd, TMD_SIZE);
if (isp->isp_osinfo.tfreelist) {
isp->isp_osinfo.bfreelist->cd_next = tmd;
} else {
isp->isp_osinfo.tfreelist = tmd;
}
isp->isp_osinfo.bfreelist = tmd;
}
static void
isp_handle_platform_atio(ispsoftc_t *isp, at_entry_t *aep)
{
tmd_cmd_t *tmd;
int status;
isp->isp_osinfo.cmds_started++;
/*
* The firmware status (except for the QLTM_SVALID bit)
* indicates why this ATIO was sent to us.
*
* If QLTM_SVALID is set, the firware has recommended Sense Data.
*
* If the DISCONNECTS DISABLED bit is set in the flags field,
* we're still connected on the SCSI bus.
*/
status = aep->at_status;
if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) {
/*
* Bus Phase Sequence error. We should have sense data
* suggested by the f/w. I'm not sure quite yet what
* to do about this.
*/
isp_prt(isp, ISP_LOGERR, "PHASE ERROR in atio");
isp_endcmd(isp, aep, SCSI_BUSY, 0);
return;
}
if ((status & ~QLTM_SVALID) != AT_CDB) {
isp_prt(isp, ISP_LOGERR, "bad atio (0x%x) leaked to platform", status);
isp_endcmd(isp, aep, SCSI_BUSY, 0);
return;
}
if ((tmd = isp->isp_osinfo.tfreelist) == NULL) {
if (isp->isp_osinfo.out_of_tmds == 0) {
isp_prt(isp, ISP_LOGWARN, "out of TMDs");
isp->isp_osinfo.out_of_tmds = jiffies;
}
isp_endcmd(isp, aep, SCSI_BUSY, 0);
if (jiffies - isp->isp_osinfo.out_of_tmds > 30 * HZ) {
isp_prt(isp, ISP_LOGERR, "out of TMDs too long: disabling port");
ISP_THREAD_EVENT(isp, ISP_THREAD_REINIT, NULL, 0, __FUNCTION__, __LINE__);
}
return;
}
isp->isp_osinfo.out_of_tmds = 0;
if ((isp->isp_osinfo.tfreelist = tmd->cd_next) == NULL) {
isp->isp_osinfo.bfreelist = NULL;
}
MEMZERO(tmd, TMD_SIZE);
/*
* Set the local flags to BUSY. Also set the flags
* to force a resource replenish just in case we never
* get around to sending a CTIO.
*/
tmd->cd_lflags = CDFL_BUSY|CDFL_RESRC_FILL;
tmd->cd_channel = GET_BUS_VAL(aep->at_iid);
tmd->cd_iid = GET_IID_VAL(aep->at_iid);
tmd->cd_tgt = aep->at_tgt;
FLATLUN_TO_L0LUN(tmd->cd_lun, aep->at_lun);
if (aep->at_flags & AT_NODISC) {
tmd->cd_flags |= CDF_NODISC;
}
if (status & QLTM_SVALID) {
MEMCPY(tmd->cd_sense, aep->at_sense, QLTM_SENSELEN);
tmd->cd_flags |= CDF_SNSVALID;
}
MEMCPY(tmd->cd_cdb, aep->at_cdb, min(TMD_CDBLEN, ATIO_CDBLEN));
AT_MAKE_TAGID(tmd->cd_tagval, tmd->cd_channel, isp->isp_unit, aep);
tmd->cd_tagtype = aep->at_tag_type;
tmd->cd_hba = isp;
isp_prt(isp, ISP_LOGTDEBUG0, "ATIO[%llx] CDB=0x%x bus %d iid%d->lun%d ttype 0x%x %s", tmd->cd_tagval, aep->at_cdb[0] & 0xff,
GET_BUS_VAL(aep->at_iid), GET_IID_VAL(aep->at_iid), aep->at_lun, aep->at_tag_type,
(aep->at_flags & AT_NODISC)? "nondisc" : "disconnecting");
if (isp->isp_osinfo.hcb == 0) {
tmd->cd_next = NULL;
if (isp->isp_osinfo.tfreelist) {
isp->isp_osinfo.bfreelist->cd_next = tmd;
} else {
isp->isp_osinfo.tfreelist = tmd;
}
isp->isp_osinfo.bfreelist = tmd;
isp_endcmd(isp, aep, SCSI_BUSY, 0);
} else {
CALL_PARENT_TMD(isp, tmd, QOUT_TMD_START);
}
}
static void
isp_handle_platform_atio2(ispsoftc_t *isp, at2_entry_t *aep)
{
tmd_cmd_t *tmd;
uint16_t lun, loopid;
isp->isp_osinfo.cmds_started++;
/*
* The firmware status (except for the QLTM_SVALID bit)
* indicates why this ATIO was sent to us.
*
* If QLTM_SVALID is set, the firware has recommended Sense Data.
*/
if ((aep->at_status & ~QLTM_SVALID) != AT_CDB) {
isp_prt(isp, ISP_LOGERR, "bad atio (0x%x) leaked to platform", aep->at_status);
isp_endcmd(isp, aep, SCSI_BUSY, 0);
return;
}
if (ISP_CAP_2KLOGIN(isp)) {
at2e_entry_t *aep2 = (at2e_entry_t *) aep;
lun = aep2->at_scclun;
loopid = aep2->at_iid;
} else {
loopid = aep->at_iid;
if (ISP_CAP_SCCFW(isp)) {
lun = aep->at_scclun;
} else {
lun = aep->at_lun;
}
}
/*
* If we're out of resources, just send a BUSY status back.
*/
if ((tmd = isp->isp_osinfo.tfreelist) == NULL) {
if (isp->isp_osinfo.out_of_tmds == 0) {
isp_prt(isp, ISP_LOGWARN, "out of TMDs");
isp->isp_osinfo.out_of_tmds = jiffies;
}
isp_endcmd(isp, aep, SCSI_BUSY, 0);
if (jiffies - isp->isp_osinfo.out_of_tmds > 30 * HZ) {
isp_prt(isp, ISP_LOGERR, "out of TMDs too long: disabling port");
ISP_THREAD_EVENT(isp, ISP_THREAD_REINIT, NULL, 0, __FUNCTION__, __LINE__);
}
return;
}
isp->isp_osinfo.out_of_tmds = 0;
if ((isp->isp_osinfo.tfreelist = tmd->cd_next) == NULL) {
isp->isp_osinfo.bfreelist = NULL;
}
MEMZERO(tmd, TMD_SIZE);
/*
* Set the local flags to BUSY. Also set the flags
* to force a resource replenish just in case we never
* get around to sending a CTIO.
*/
tmd->cd_lflags = CDFL_BUSY|CDFL_RESRC_FILL;
tmd->cd_iid =
(((uint64_t) aep->at_wwpn[0]) << 48) |
(((uint64_t) aep->at_wwpn[1]) << 32) |
(((uint64_t) aep->at_wwpn[2]) << 16) |
(((uint64_t) aep->at_wwpn[3]) << 0);
tmd->cd_oxid = aep->at_oxid;
tmd->cd_nphdl = loopid;
tmd->cd_tgt = FCPARAM(isp, 0)->isp_wwpn;
FLATLUN_TO_L0LUN(tmd->cd_lun, lun);
MEMCPY(tmd->cd_cdb, aep->at_cdb, min(TMD_CDBLEN, ATIO2_CDBLEN));
switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) {
case ATIO2_TC_ATTR_SIMPLEQ:
tmd->cd_tagtype = CD_SIMPLE_TAG;
break;
case ATIO2_TC_ATTR_HEADOFQ:
tmd->cd_tagtype = CD_HEAD_TAG;
break;
case ATIO2_TC_ATTR_ORDERED:
tmd->cd_tagtype = CD_ORDERED_TAG;
break;
case ATIO2_TC_ATTR_ACAQ:
tmd->cd_tagtype = CD_ACA_TAG;
break;
case ATIO2_TC_ATTR_UNTAGGED:
default:
tmd->cd_tagtype = CD_UNTAGGED;
break;
}
switch (aep->at_execodes & (ATIO2_EX_WRITE|ATIO2_EX_READ)) {
case ATIO2_EX_WRITE:
tmd->cd_flags |= CDF_DATA_OUT;
break;
case ATIO2_EX_READ:
tmd->cd_flags |= CDF_DATA_IN;
break;
case ATIO2_EX_WRITE|ATIO2_EX_READ:
tmd->cd_flags |= CDF_BIDIR;
isp_prt(isp, ISP_LOGWARN, "ATIO2 with both read/write set");
break;
default:
break;
}
AT2_MAKE_TAGID(tmd->cd_tagval, 0, isp->isp_unit, aep);
tmd->cd_hba = isp;
tmd->cd_totlen = aep->at_datalen;
if ((isp->isp_dblev & ISP_LOGTDEBUG0) || isp->isp_osinfo.hcb == 0) {
const char *sstr;
switch (tmd->cd_flags & CDF_BIDIR) {
default:
sstr = "nodatadir";
break;
case CDF_DATA_OUT:
sstr = "DATA OUT";
break;
case CDF_DATA_IN:
sstr = "DATA IN";
break;
case CDF_DATA_OUT|CDF_DATA_IN:
sstr = "BIDIR";
break;
}
isp_prt(isp, ISP_LOGALL, "ATIO2[%llx] CDB=0x%x 0x%016llx for lun %d tcode 0x%x dlen %d %s", tmd->cd_tagval,
aep->at_cdb[0] & 0xff, tmd->cd_iid, lun, aep->at_taskcodes, aep->at_datalen, sstr);
}
if (isp->isp_osinfo.hcb == 0) {
isp_lcl_respond(isp, aep, tmd);
} else {
fcportdb_t *lp;
if (isp_find_pdb_by_loopid(isp, 0, tmd->cd_nphdl, &lp)) {
tmd->cd_portid = lp->portid;
CALL_PARENT_TMD(isp, tmd, QOUT_TMD_START);
} else {
tmd->cd_portid = PORT_NONE;
isp_add_wwn_entry(isp, 0, tmd->cd_iid, tmd->cd_nphdl, PORT_NONE);
(void) ISP_THREAD_EVENT(isp, ISP_THREAD_FINDPORTID, tmd, 0, __FUNCTION__, __LINE__);
}
}
}
static void
isp_handle_platform_atio7(ispsoftc_t *isp, at7_entry_t *aep)
{
int tattr, iulen, cdbxlen;
uint16_t lun, chan, nphdl = NIL_HANDLE;
uint32_t did, sid;
uint64_t iid = INI_NONE;
fcportdb_t *lp;
fcparam *fcp;
tmd_cmd_t *tmd;
isp->isp_osinfo.cmds_started++;
tattr = aep->at_ta_len >> 12;
iulen = aep->at_ta_len & 0xffffff;
did = (aep->at_hdr.d_id[0] << 16) | (aep->at_hdr.d_id[1] << 8) | aep->at_hdr.d_id[2];
sid = (aep->at_hdr.s_id[0] << 16) | (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2];
lun = (aep->at_cmnd.fcp_cmnd_lun[0] << 8) | aep->at_cmnd.fcp_cmnd_lun[1];
/*
* Find the N-port handle, and Virtual Port Index for this command.
*
* If we can't, we're somewhat in trouble because we can't actually respond w/o that information.
* We also, as a matter of course, need to know the WWN of the initiator too.
*/
/*
* Find the right channel based upon D_ID
*/
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcp = FCPARAM(isp, chan);
if ((fcp->role & ISP_ROLE_TARGET) == 0 || fcp->isp_fwstate != FW_READY || fcp->isp_loopstate < LOOP_PDB_RCVD) {
continue;
}
if (fcp->isp_portid == did) {
break;
}
}
if (chan == isp->isp_nchan) {
/*
* If this happens, it could be because one of our channels is busy starting up.
*
* It's Hackaroni time...
* It's Hackaroni time...
* It's Hackaroni time...
* It's Hackaroni time...
*/
if ((tmd = isp->isp_osinfo.tfreelist) == NULL || ++aep->at_count == 250) {
isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel- dropping", __FUNCTION__, aep->at_rxid, did);
return;
}
if ((isp->isp_osinfo.tfreelist = tmd->cd_next) == NULL) {
isp->isp_osinfo.bfreelist = NULL;
}
MEMCPY(tmd, aep, sizeof (at7_entry_t));
/* we should be safe here ... */
tmd->cd_lflags = CDFL_BUSY;
tmd->cd_next = isp->isp_osinfo.waiting_t;
isp->isp_osinfo.waiting_t = tmd;
tmd->cd_lastoff = 1;
return;
}
isp_prt(isp, ISP_LOGTDEBUG0, "%s: [RX_ID 0x%x] D_ID 0x%06x found on Chan %d for S_ID 0x%06x", __FUNCTION__, aep->at_rxid, did, chan, sid);
if (isp_find_pdb_by_sid(isp, chan, sid, &lp)) {
nphdl = lp->handle;
iid = lp->port_wwn;
} else {
/*
* If we're not in the port database, do a tentative entry.
*/
isp_prt(isp, ISP_LOGTINFO, "%s: [RX_ID 0x%x] D_ID 0x%06x found on Chan %d for S_ID 0x%06x wasn't in PDB already", __FUNCTION__, aep->at_rxid, did, chan, sid);
isp_add_wwn_entry(isp, chan, INI_NONE, NIL_HANDLE, sid);
}
/*
* If the f/w is out of resources, just send a BUSY status back.
*/
if (aep->at_rxid == AT7_NORESRC_RXID) {
isp_endcmd(isp, aep, nphdl, chan, SCSI_BUSY, 0);
return;
}
/*
* If we're out of resources, just send a BUSY status back.
*/
if ((tmd = isp->isp_osinfo.tfreelist) == NULL) {
if (isp->isp_osinfo.out_of_tmds == 0) {
isp_prt(isp, ISP_LOGWARN, "out of TMDs");
isp->isp_osinfo.out_of_tmds = jiffies;
}
isp_endcmd(isp, aep, chan, SCSI_BUSY, 0);
if (jiffies - isp->isp_osinfo.out_of_tmds > 30 * HZ) {
isp_prt(isp, ISP_LOGERR, "out of TMDs too long: disabling port");
ISP_THREAD_EVENT(isp, ISP_THREAD_REINIT, NULL, 0, __FUNCTION__, __LINE__);
}
return;
}
isp->isp_osinfo.out_of_tmds = 0;
if ((isp->isp_osinfo.tfreelist = tmd->cd_next) == NULL) {
isp->isp_osinfo.bfreelist = NULL;
}
MEMZERO(tmd, TMD_SIZE);
/*
* Set the local flags to BUSY.
*/
tmd->cd_lflags = CDFL_BUSY;
cdbxlen = aep->at_cmnd.fcp_cmnd_alen_datadir >> FCP_CMND_ADDTL_CDBLEN_SHIFT;
if (cdbxlen) {
isp_prt(isp, ISP_LOGWARN, "additional CDBLEN ignored");
}
cdbxlen = sizeof (aep->at_cmnd.cdb_dl.sf.fcp_cmnd_cdb);
MEMCPY(tmd->cd_cdb, aep->at_cmnd.cdb_dl.sf.fcp_cmnd_cdb, cdbxlen);
tmd->cd_totlen = aep->at_cmnd.cdb_dl.sf.fcp_cmnd_dl;
switch (aep->at_cmnd.fcp_cmnd_task_attribute & FCP_CMND_TASK_ATTR_MASK) {
case FCP_CMND_TASK_ATTR_SIMPLE:
tmd->cd_tagtype = CD_SIMPLE_TAG;
break;
case FCP_CMND_TASK_ATTR_HEAD:
tmd->cd_tagtype = CD_HEAD_TAG;
break;
case FCP_CMND_TASK_ATTR_ORDERED:
tmd->cd_tagtype = CD_ORDERED_TAG;
break;
case FCP_CMND_TASK_ATTR_ACA:
tmd->cd_tagtype = CD_ACA_TAG;
break;
case FCP_CMND_TASK_ATTR_UNTAGGED:
tmd->cd_tagtype = CD_UNTAGGED;
break;
default:
isp_prt(isp, ISP_LOGWARN, "unknown task attribute %x", aep->at_cmnd.fcp_cmnd_task_attribute & FCP_CMND_TASK_ATTR_MASK);
tmd->cd_tagtype = CD_UNTAGGED;
break;
}
switch (aep->at_cmnd.fcp_cmnd_alen_datadir & FCP_CMND_DATA_DIR_MASK) {
case FCP_CMND_DATA_WRITE:
tmd->cd_flags |= CDF_DATA_OUT;
break;
case FCP_CMND_DATA_READ:
tmd->cd_flags |= CDF_DATA_IN;
break;
case FCP_CMND_DATA_READ|FCP_CMND_DATA_WRITE:
tmd->cd_flags |= CDF_BIDIR;
isp_prt(isp, ISP_LOGINFO, "FCP_CMND_IU with both read/write set");
break;
default:
break;
}
tmd->cd_tgt = FCPARAM(isp, chan)->isp_wwpn; /* channel is valid at this point */
tmd->cd_nphdl = nphdl; /* nphdl is either known or NIL- in either case, we can set it if needed */
AT2_MAKE_TAGID(tmd->cd_tagval, chan, isp->isp_unit, aep); /* we can construct a tag value at this point */
tmd->cd_iid = iid; /* iid is either INI_NONE or known */
MEMCPY(tmd->cd_lun, aep->at_cmnd.fcp_cmnd_lun, sizeof (tmd->cd_lun));
tmd->cd_portid = sid;
tmd->cd_channel = chan;
tmd->cd_hba = isp;
tmd->cd_oxid = aep->at_hdr.ox_id;
if ((isp->isp_dblev & ISP_LOGTDEBUG0) || isp->isp_osinfo.hcb == 0) {
const char *sstr;
switch (tmd->cd_flags & CDF_BIDIR) {
default:
sstr = "nodatadir";
break;
case CDF_DATA_OUT:
sstr = "DATA OUT";
break;
case CDF_DATA_IN:
sstr = "DATA IN";
break;
case CDF_DATA_OUT|CDF_DATA_IN:
sstr = "BIDIR";
break;
}
isp_prt(isp, ISP_LOGALL, "ATIO7[%llx] cdb0=0x%x from 0x%016llx/0x%06x ox_id 0x%x N-Port Handle 0x%02x for lun %u dlen %d %s", tmd->cd_tagval,
tmd->cd_cdb[0] & 0xff, (unsigned long long) tmd->cd_iid, tmd->cd_portid, tmd->cd_oxid, tmd->cd_nphdl, lun, tmd->cd_totlen, sstr);
}
if (isp->isp_osinfo.hcb == 0) {
isp_lcl_respond(isp, aep, tmd);
return;
}
if (VALID_INI(tmd->cd_iid)) {
CALL_PARENT_TMD(isp, tmd, QOUT_TMD_START);
} else {
isp_prt(isp, ISP_LOGTDEBUG0, "[0x%llx] asking taskthread to find iid of initiator", (unsigned long long) tmd->cd_tagval);
if (ISP_THREAD_EVENT(isp, ISP_THREAD_FINDIID, tmd, 0, __FUNCTION__, __LINE__)) {
isp_endcmd(isp, aep, nphdl, chan, SCSI_BUSY, 0);
MEMZERO(tmd, TMD_SIZE);
if (isp->isp_osinfo.tfreelist) {
isp->isp_osinfo.bfreelist->cd_next = tmd;
} else {
isp->isp_osinfo.tfreelist = tmd;
}
isp->isp_osinfo.bfreelist = tmd;
}
}
}
/*
* Terminate a command
*/
static int
isp_terminate_cmd(ispsoftc_t *isp, tmd_cmd_t *tmd)
{
ct7_entry_t local, *cto = &local;
if (IS_24XX(isp)) {
isp_prt(isp, ISP_LOGTINFO, "isp_terminate_cmd: [%llx] is being terminated", (unsigned long long) tmd->cd_tagval);
MEMZERO(&local, sizeof (local));
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7;
cto->ct_header.rqs_entry_count = 1;
cto->ct_nphdl = tmd->cd_nphdl;
cto->ct_rxid = AT2_GET_TAG(tmd->cd_tagval);
cto->ct_vpindex = AT2_GET_BUS(tmd->cd_tagval);
cto->ct_iid_lo = tmd->cd_portid;
cto->ct_iid_hi = tmd->cd_portid >> 16;
cto->ct_oxid = tmd->cd_oxid;
cto->ct_flags = CT7_TERMINATE;
cto->ct_syshandle = 0;
return (isp_target_put_entry(isp, &local));
} else {
return (-1);
}
}
static void
isp_handle_platform_ctio(ispsoftc_t *isp, void *arg)
{
tmd_xact_t *xact;
tmd_cmd_t *tmd;
char *ctstr;
int sentstatus = 0, ok, resid = 0, id;
int status, flags;
/*
* CTIO, CTIO2, and CTIO7 are close enough....
*/
if (IS_24XX(isp)) {
ct7_entry_t *ct = arg;
xact = (tmd_xact_t *) isp_find_xs_tgt(isp, ct->ct_syshandle);
if (xact == NULL) {
isp_prt(isp, ISP_LOGERR, "isp_handle_platform_ctio: null xact");
return;
}
tmd = xact->td_cmd;
isp_destroy_tgt_handle(isp, ct->ct_syshandle);
status = ct->ct_nphdl;
flags = ct->ct_flags;
sentstatus = (flags & CT7_SENDSTATUS) != 0;
ok = status == CT7_OK;
ctstr = "CTIO7";
if ((ct->ct_flags & CT7_DATAMASK) != CT7_NO_DATA) {
resid = ct->ct_resid;
}
id = ct->ct_iid_lo | (ct->ct_iid_hi << 16);
} else if (IS_FC(isp)) {
ct2_entry_t *ct = arg;
xact = (tmd_xact_t *) isp_find_xs_tgt(isp, ct->ct_syshandle);
if (xact == NULL) {
isp_prt(isp, ISP_LOGERR, "isp_handle_platform_ctio: null xact");
return;
}
tmd = xact->td_cmd;
isp_destroy_tgt_handle(isp, ct->ct_syshandle);
status = ct->ct_status;
flags = ct->ct_flags;
sentstatus = (flags & CT2_SENDSTATUS) != 0;
ok = (status & ~QLTM_SVALID) == CT_OK;
ctstr = "CTIO2";
if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) {
resid = ct->ct_resid;
}
id = ct->ct_iid;
} else {
ct_entry_t *ct = arg;
xact = (tmd_xact_t *) isp_find_xs_tgt(isp, ct->ct_syshandle);
if (xact == NULL) {
isp_prt(isp, ISP_LOGERR, "isp_handle_platform_ctio: null xact");
return;
}
tmd = xact->td_cmd;
isp_destroy_tgt_handle(isp, ct->ct_syshandle);
status = ct->ct_status;
flags = ct->ct_flags;
sentstatus = (flags & CT_SENDSTATUS) != 0;
ok = (status & ~QLTM_SVALID) == CT_OK;
ctstr = "CTIO";
if (ct->ct_status & QLTM_SVALID) {
char *sp = (char *)ct;
sp += CTIO_SENSE_OFFSET;
MEMCPY(tmd->cd_sense, sp, QLTM_SENSELEN);
tmd->cd_flags |= CDF_SNSVALID;
}
if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) {
resid = ct->ct_resid;
}
id = ct->ct_iid;
}
if (sentstatus) {
xact->td_lflags |= TDFL_SENTSTATUS;
}
if (ok && sentstatus && (xact->td_hflags & TDFH_SNSVALID)) {
xact->td_lflags |= TDFL_SENTSENSE;
}
tmd->cd_moved -= resid;
isp_prt(isp, ISP_LOGTDEBUG0, "%s[%llx] status 0x%x flg 0x%x %s", ctstr, tmd->cd_tagval, status, flags, sentstatus? "FIN" : "MID");
/*
* We're here either because intermediate data transfers are done
* and/or the final status CTIO (which may have joined with a
* Data Transfer) is done.
*
* In any case, for this platform, the upper layers figure out
* what to do next, so all we do here is collect status and
* pass information along.
*/
isp_prt(isp, ISP_LOGTDEBUG0, "%s CTIO done (moved %u)", (sentstatus)? " FINAL " : "MIDTERM ", tmd->cd_moved);
if (!ok) {
const char *cx;
if (IS_24XX(isp)) {
cx = "O7";
} else if (IS_FC(isp)) {
cx = "O2";
} else {
cx = "O";
}
if ((status & ~QLTM_SVALID) == CT_ABORTED) {
isp_prt(isp, ISP_LOGINFO, "[%llx] CTI%s aborted", tmd->cd_tagval, cx);
tmd->cd_lflags |= CDFL_ABORTED;
} else if ((status & QLTM_SVALID) == CT_LOGOUT) {
isp_prt(isp, ISP_LOGINFO, "[%llx] CTI%s killed by Port Logout", tmd->cd_tagval, cx);
} else {
isp_prt(isp, ISP_LOGINFO, "[%llx] CTI%s ended with badstate (0x%x)", tmd->cd_tagval, cx, status);
}
xact->td_error = -EIO;
if (isp_target_putback_atio(isp, tmd)) {
tmd->cd_lflags |= CDFL_RESRC_FILL;
}
if ((status & ~QLTM_SVALID) == CT_LOGOUT) {
int i;
for (i = 0; i < MAX_FC_TARG; i++) {
if (FCPARAM(isp, tmd->cd_channel)->portdb[i].target_mode == 0) {
continue;
}
if (IS_24XX(isp)) {
if (id != FCPARAM(isp, tmd->cd_channel)->portdb[i].portid) {
continue;
}
} else {
if (id != FCPARAM(isp, tmd->cd_channel)->portdb[i].handle) {
continue;
}
}
ISP_THREAD_EVENT(isp, ISP_THREAD_LOGOUT, &FCPARAM(isp, tmd->cd_channel)->portdb[i], 0, __FUNCTION__, __LINE__);
break;
}
}
}
isp_complete_ctio(isp, xact);
}
static int
isp_target_putback_atio(ispsoftc_t *isp, tmd_cmd_t *tmd)
{
uint32_t nxti;
uint8_t local[QENTRY_LEN];
void *qe;
tmd->cd_lflags &= ~CDFL_RESRC_FILL;
if (IS_24XX(isp)) {
return (0);
}
if (isp_getrqentry(isp, &nxti, NULL, &qe)) {
isp_prt(isp, ISP_LOGWARN, "%s: Request Queue Overflow", __FUNCTION__);
return (-ENOMEM);
}
isp_prt(isp, ISP_LOGTDEBUG0, "[%llx] resource putback being sent", tmd->cd_tagval);
MEMZERO(local, sizeof (local));
if (IS_FC(isp)) {
at2_entry_t *at = (at2_entry_t *) local;
at->at_header.rqs_entry_type = RQSTYPE_ATIO2;
at->at_header.rqs_entry_count = 1;
at->at_status = CT_OK;
at->at_rxid = AT2_GET_TAG(tmd->cd_tagval);
if (ISP_CAP_2KLOGIN(isp)) {
at2e_entry_t *ate = (at2e_entry_t *) local;
FLATLUN_TO_L0LUN(tmd->cd_lun, ate->at_scclun);
} else {
if (ISP_CAP_SCCFW(isp)) {
FLATLUN_TO_L0LUN(tmd->cd_lun, at->at_scclun);
} else {
FLATLUN_TO_L0LUN(tmd->cd_lun, at->at_lun);
}
}
isp_put_atio2(isp, at, qe);
} else {
at_entry_t *at = (at_entry_t *)local;
at->at_header.rqs_entry_type = RQSTYPE_ATIO;
at->at_header.rqs_entry_count = 1;
at->at_iid = tmd->cd_iid;
at->at_iid |= tmd->cd_channel << 7;
at->at_tgt = tmd->cd_tgt;
FLATLUN_TO_L0LUN(tmd->cd_lun, at->at_lun);
at->at_status = CT_OK;
at->at_tag_val = AT_GET_TAG(tmd->cd_tagval);
at->at_handle = AT_GET_HANDLE(tmd->cd_tagval);
isp_put_atio(isp, at, qe);
}
ISP_TDQE(isp, "isp_target_putback_atio", isp->isp_reqidx, qe);
ISP_ADD_REQUEST(isp, nxti);
return (0);
}
static void
isp_complete_ctio(ispsoftc_t *isp, tmd_xact_t *xact)
{
tmd_cmd_t *tmd = xact->td_cmd;
isp->isp_osinfo.cmds_completed++;
if (isp->isp_osinfo.hcb || (tmd->cd_lflags & CDFL_LCL)) {
if (isp->isp_osinfo.hcb == 0) {
isp_prt(isp, ISP_LOGWARN, "nobody to tell about CTIO complete, leaking xact structure");
MEMZERO(tmd, TMD_SIZE);
if (isp->isp_osinfo.tfreelist) {
isp->isp_osinfo.bfreelist->cd_next = tmd;
} else {
isp->isp_osinfo.tfreelist = tmd;
}
isp->isp_osinfo.bfreelist = tmd;
} else {
CALL_PARENT_XFR(isp, xact);
}
}
}
int
isp_fc_change_role(ispsoftc_t *isp, int chan, int new_role)
{
fcparam *fcp;
if (IS_SCSI(isp)) {
return (0);
}
if (chan >= isp->isp_nchan) {
isp_prt(isp, ISP_LOGWARN, "%s: bad channel %d", __FUNCTION__, chan);
return (-ENXIO);
}
fcp = FCPARAM(isp, chan);
ISP_DATA(isp, chan)->blocked = 1;
if (chan == 0 || new_role == ISP_ROLE_NONE) {
SET_DEFAULT_ROLE(isp, chan, new_role);
isp_reinit(isp);
} else {
mbreg_t mbs;
vp_modify_t *vp;
uint8_t qe[QENTRY_LEN], *scp;
MEMZERO(qe, QENTRY_LEN);
/* Acquire Scratch */
FC_SCRATCH_ACQUIRE(isp, chan);
scp = fcp->isp_scratch;
/*
* Build a VP MODIFY command in memory
*/
vp = (vp_modify_t *) qe;
vp->vp_mod_hdr.rqs_entry_type = RQSTYPE_VP_MODIFY;
vp->vp_mod_hdr.rqs_entry_count = 1;
vp->vp_mod_cnt = 1;
vp->vp_mod_idx0 = chan;
vp->vp_mod_cmd = VP_MODIFY_ENA;
vp->vp_mod_ports[0].options = ICB2400_VPOPT_ENABLED;
if (new_role & ISP_ROLE_INITIATOR) {
vp->vp_mod_ports[0].options |= ICB2400_VPOPT_INI_ENABLE;
}
if ((new_role & ISP_ROLE_TARGET) == 0) {
vp->vp_mod_ports[0].options |= ICB2400_VPOPT_TGT_DISABLE;
}
MAKE_NODE_NAME_FROM_WWN(vp->vp_mod_ports[0].wwpn, fcp->isp_wwpn);
MAKE_NODE_NAME_FROM_WWN(vp->vp_mod_ports[0].wwnn, fcp->isp_wwnn);
isp_put_vp_modify(isp, vp, (vp_modify_t *) scp);
/*
* Build a EXEC IOCB A64 command that points to the VP MODIFY command
*/
MEMZERO(&mbs, sizeof (mbs));
mbs.param[0] = MBOX_EXEC_COMMAND_IOCB_A64;
mbs.param[1] = QENTRY_LEN;
mbs.param[2] = DMA_WD1(fcp->isp_scdma);
mbs.param[3] = DMA_WD0(fcp->isp_scdma);
mbs.param[6] = DMA_WD3(fcp->isp_scdma);
mbs.param[7] = DMA_WD2(fcp->isp_scdma);
mbs.logval = MBLOGALL;
MEMORYBARRIER(isp, SYNC_SFORDEV, 0, 2 * QENTRY_LEN);
isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
FC_SCRATCH_RELEASE(isp, chan);
ISP_DATA(isp, chan)->blocked = 0;
return (-EIO);
}
MEMORYBARRIER(isp, SYNC_SFORCPU, QENTRY_LEN, QENTRY_LEN);
isp_get_vp_modify(isp, (vp_modify_t *)&scp[QENTRY_LEN], vp);
/*
* Release Scratch
*/
FC_SCRATCH_RELEASE(isp, chan);
if (vp->vp_mod_status != VP_STS_OK) {
isp_prt(isp, ISP_LOGERR, "%s: VP_MODIFY of Chan %d failed with status %d", __FUNCTION__, chan, vp->vp_mod_status);
ISP_DATA(isp, chan)->blocked = 0;
return (-EIO);
}
SET_DEFAULT_ROLE(isp, chan, new_role);
fcp->role = new_role;
}
ISP_DATA(isp, chan)->blocked = 0;
return (0);
}
int
isp_enable_lun(ispsoftc_t *isp, uint16_t bus, uint16_t lun)
{
DECLARE_MUTEX_LOCKED(rsem);
tgt_enalun_t *axl;
uint16_t rstat;
int cmd, r;
unsigned long flags;
/*
* Validity check the bus argument.
*/
if (bus >= isp->isp_nchan) {
return (-ENODEV);
}
/*
* Allocate a sparse lun descriptor
*/
axl = isp_kzalloc(sizeof (tgt_enalun_t), GFP_KERNEL);
if (axl == NULL) {
return (-ENOMEM);
}
/*
* Snag the semaphore on the return state value on enables/disables.
*/
if (down_interruptible(&isp->isp_osinfo.tgt_inisem)) {
isp_kfree(axl, sizeof (tgt_enalun_t));
return (-EINTR);
}
/*
* If this lun is enabled on this bus already, that's an error.
*/
if (lunenabled(isp, bus, lun)) {
up(&isp->isp_osinfo.tgt_inisem);
isp_kfree(axl, sizeof (tgt_enalun_t));
return (-EEXIST);
}
/*
* Validity check with our enable ruleset.
* We can't enable busses > 1 without enabling bus 0
* first in some kind of role.
*/
if (IS_FC(isp) && bus != 0 && (((FCPARAM(isp, 0)->role & ISP_ROLE_TARGET) && nolunsenabled(isp, 0)) || (FCPARAM(isp, 0)->role == ISP_ROLE_NONE))) {
isp_prt(isp, ISP_LOGWARN, "%s: must enable Chan 0 before Chan %u", __FUNCTION__, bus);
up(&isp->isp_osinfo.tgt_inisem);
isp_kfree(axl, sizeof (tgt_enalun_t));
return (-EINVAL);
}
/*
* If this bus is wildcarded, we don't allow any further actions.
*/
if (lun != LUN_ANY) {
if (lunenabled(isp, bus, LUN_ANY)) {
up(&isp->isp_osinfo.tgt_inisem);
isp_kfree(axl, sizeof (tgt_enalun_t));
return (-EEXIST);
}
}
/*
* If this is the first lun being enabled for this bus make sure we're set up for
* being in target mode.
*/
if (nolunsenabled(isp, bus)) {
if (IS_SCSI(isp)) {
uint16_t tb;
for (tb = 0; tb < isp->isp_nchan; tb++) {
if (tb == bus)
continue;
if (nolunsenabled(isp, tb) == 0) {
break;
}
}
/*
* This is the very first bus being enabled.
*/
if (tb < isp->isp_nchan) {
mbreg_t mbs;
MEMZERO(&mbs, sizeof (mbs));
mbs.param[0] = MBOX_ENABLE_TARGET_MODE;
mbs.param[1] = ENABLE_TARGET_FLAG|ENABLE_TQING_FLAG;
mbs.param[2] = bus << 7;
mbs.logval = MBLOGALL;
ISP_LOCK_SOFTC(isp);
r = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
ISP_UNLK_SOFTC(isp);
if (r < 0 || mbs.param[0] != MBOX_COMMAND_COMPLETE) {
up(&isp->isp_osinfo.tgt_inisem);
isp_kfree(axl, sizeof (tgt_enalun_t));
return (-EIO);
}
}
} else {
fcparam *fcp = FCPARAM(isp, bus);
if ((fcp->role & ISP_ROLE_TARGET) == 0) {
ISP_LOCK_SOFTC(isp);
r = isp_fc_change_role(isp, bus, fcp->role | ISP_ROLE_TARGET);
if (r) {
ISP_UNLK_SOFTC(isp);
up(&isp->isp_osinfo.tgt_inisem);
isp_kfree(axl, sizeof (tgt_enalun_t));
return (r);
}
ISP_UNLK_SOFTC(isp);
}
}
}
ISP_LOCK_SOFTC(isp);
isp->isp_osinfo.rsemap = &rsem;
if (IS_24XX(isp)) {
rstat = LUN_OK;
} else {
int n, ulun = lun;
cmd = RQSTYPE_ENABLE_LUN;
n = DFLT_INOT_CNT;
if (IS_FC(isp)) {
if (lun != 0 && lun != LUN_ANY) {
cmd = RQSTYPE_MODIFY_LUN;
n = 0;
} else if (lun == LUN_ANY) {
/*
* For SCC firmware, we only deal with setting
* (enabling or modifying) lun 0.
*/
ulun = 0;
}
}
rstat = LUN_ERR;
if (isp_lun_cmd(isp, cmd, bus, ulun, DFLT_CMND_CNT, n)) {
isp_prt(isp, ISP_LOGERR, "isp_lun_cmd failed");
goto out;
}
ISP_UNLK_SOFTC(isp);
down(isp->isp_osinfo.rsemap);
ISP_LOCK_SOFTC(isp);
isp->isp_osinfo.rsemap = NULL;
rstat = isp->isp_osinfo.rstatus;
if (rstat != LUN_OK) {
isp_prt(isp, ISP_LOGERR, "MODIFY/ENABLE LUN returned 0x%x", rstat);
goto out;
}
}
out:
if (rstat != LUN_OK) {
isp_prt(isp, ISP_LOGERR, "lun %u enable failed", lun);
ISP_UNLK_SOFTC(isp);
up(&isp->isp_osinfo.tgt_inisem);
isp_kfree(axl, sizeof (tgt_enalun_t));
return (-EIO);
}
addlun(isp, axl, bus, lun);
ISP_UNLK_SOFTC(isp);
if (lun == LUN_ANY) {
isp_prt(isp, ISP_LOGINFO, "All luns now enabled for target mode on channel %d", bus);
} else {
isp_prt(isp, ISP_LOGINFO, "lun %u now disabled for target mode on channel %d", lun, bus);
}
/*
* Make sure we stay resident while we have a lun enabled
*/
if (try_module_get(isp->isp_osinfo.host->hostt->module)) {
isp->isp_osinfo.isget++;
}
up(&isp->isp_osinfo.tgt_inisem);
return (0);
}
int
isp_disable_lun(ispsoftc_t *isp, uint16_t bus, uint16_t lun)
{
DECLARE_MUTEX_LOCKED(rsem);
uint16_t rstat;
tgt_enalun_t *axl;
int cmd;
unsigned long flags;
/*
* Snag the semaphore on the return state value on enables/disables.
*/
if (down_interruptible(&isp->isp_osinfo.tgt_inisem)) {
return (-EINTR);
}
if (lunenabled(isp, bus, lun) == 0) {
up(&isp->isp_osinfo.tgt_inisem);
return (-ENODEV);
}
ISP_LOCK_SOFTC(isp);
/*
* Validate this disable based upon our rulesets.
*/
if (IS_FC(isp)) {
if (bus == 0 && (FCPARAM(isp, 0)->role & ISP_ROLE_TARGET) != 0) {
for (rstat = isp->isp_nchan - 1; rstat > 0; rstat--) {
if (nolunsenabled(isp, rstat) == 0) {
break;
}
}
if (rstat > 0) {
isp_prt(isp, ISP_LOGERR, "%s: must disable Chan %u before Chan 0\n", __FUNCTION__, rstat);
ISP_UNLK_SOFTC(isp);
up(&isp->isp_osinfo.tgt_inisem);
return (-EINVAL);
}
}
}
isp->isp_osinfo.rsemap = &rsem;
if (IS_24XX(isp)) {
rstat = LUN_OK;
} else {
int n, ulun = lun;
rstat = LUN_ERR;
cmd = -RQSTYPE_MODIFY_LUN;
n = DFLT_INOT_CNT;
if (IS_FC(isp) && lun != 0) {
n = 0;
/*
* For SCC firmware, we only deal with setting
* (enabling or modifying) lun 0.
*/
ulun = 0;
}
if (isp_lun_cmd(isp, cmd, bus, ulun, DFLT_CMND_CNT, n)) {
isp_prt(isp, ISP_LOGINFO, "isp_lun_cmd failed");
/* but proceed anyway */
rstat = LUN_OK;
}
ISP_UNLK_SOFTC(isp);
down(isp->isp_osinfo.rsemap);
ISP_LOCK_SOFTC(isp);
isp->isp_osinfo.rsemap = NULL;
rstat = isp->isp_osinfo.rstatus;
if (rstat != LUN_OK) {
/* but proceed anyway */
isp_prt(isp, ISP_LOGINFO, "MODIFY LUN returned 0x%x", rstat);
/* but proceed anyway */
rstat = LUN_OK;
}
if (IS_FC(isp) && lun) {
goto out;
}
isp->isp_osinfo.rsemap = &rsem;
rstat = LUN_ERR;
cmd = -RQSTYPE_ENABLE_LUN;
if (isp_lun_cmd(isp, cmd, bus, lun, 0, 0)) {
isp_prt(isp, ISP_LOGERR, "isp_lun_cmd failed");
goto out;
}
ISP_UNLK_SOFTC(isp);
down(isp->isp_osinfo.rsemap);
ISP_LOCK_SOFTC(isp);
isp->isp_osinfo.rsemap = NULL;
rstat = isp->isp_osinfo.rstatus;
if (rstat != LUN_OK) {
isp_prt(isp, ISP_LOGINFO, "DISABLE LUN returned 0x%x", rstat);
/* but proceed anyway */
rstat = LUN_OK;
}
}
out:
axl = remlun(isp, bus, lun);
ISP_UNLK_SOFTC(isp);
if (axl) {
isp_kfree(axl, sizeof (tgt_enalun_t));
} else {
isp_prt(isp, ISP_LOGWARN, "%s: Chan %d lun %u unable to find axl to delete", __FUNCTION__, bus, lun);
}
if (rstat != LUN_OK) {
isp_prt(isp, ISP_LOGERR, "lun %u disable failed", lun);
/* but continue anyway */
}
if (lun == LUN_ANY) {
isp_prt(isp, ISP_LOGINFO, "All luns now disabled for target mode on channel %d", bus);
} else {
isp_prt(isp, ISP_LOGINFO, "lun %u now disabled for target mode on channel %d", lun, bus);
}
if (isp->isp_osinfo.isget) {
module_put(isp->isp_osinfo.host->hostt->module);
isp->isp_osinfo.isget--;
}
if (IS_SCSI(isp)) {
for (bus = 0; bus < isp->isp_nchan; bus++) {
if (!nolunsenabled(isp, bus)) {
break;
}
}
if (bus == isp->isp_nchan) {
int r;
mbreg_t mbs;
MEMZERO(&mbs, sizeof (mbs));
mbs.param[0] = MBOX_ENABLE_TARGET_MODE;
mbs.param[2] = bus << 7;
mbs.logval = MBLOGNONE;
ISP_LOCK_SOFTC(isp);
r = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
ISP_UNLK_SOFTC(isp);
if (r < 0 || mbs.param[0] != MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGERR, "Chan %d unable to disable target mode", bus);
}
}
} else {
if (nolunsenabled(isp, bus)) {
fcparam *fcp = FCPARAM(isp, bus);
ISP_LOCK_SOFTC(isp);
if (isp_fc_change_role(isp, bus, fcp->role & ~ISP_ROLE_TARGET)) {
isp_prt(isp, ISP_LOGERR, "Chan %d unable to disable target mode", bus);
}
ISP_UNLK_SOFTC(isp);
}
}
up(&isp->isp_osinfo.tgt_inisem);
return (0);
}
#endif
void
isp_async(ispsoftc_t *isp, ispasync_t cmd, ...)
{
static const char prom0[] = "Chan %d PortID 0x%06x handle 0x%x role %s %s WWNN 0x%016llx WWPN 0x%016llx";
static const char prom2[] = "Chan %d PortID 0x%06x handle 0x%x role %s %s tgt %u WWNN 0x%016llx WWPN 0x%016llx";
fcportdb_t *lp;
fcparam *fcp;
va_list ap;
int bus, tgt;
switch (cmd) {
case ISPASYNC_NEW_TGT_PARAMS:
if (IS_SCSI(isp)) {
sdparam *sdp;
char *wt;
int mhz, flags, period;
va_start(ap, cmd);
bus = va_arg(ap, int);
tgt = va_arg(ap, int);
va_end(ap);
sdp = SDPARAM(isp, bus);
flags = sdp->isp_devparam[tgt].actv_flags;
period = sdp->isp_devparam[tgt].actv_period;
if ((flags & DPARM_SYNC) && period && (sdp->isp_devparam[tgt].actv_offset) != 0) {
if (sdp->isp_lvdmode || period < 0xc) {
switch (period) {
case 0x9:
mhz = 80;
break;
case 0xa:
mhz = 40;
break;
case 0xb:
mhz = 33;
break;
case 0xc:
mhz = 25;
break;
default:
mhz = 1000 / (period * 4);
break;
}
} else {
mhz = 1000 / (period * 4);
}
} else {
mhz = 0;
}
switch (flags & (DPARM_WIDE|DPARM_TQING)) {
case DPARM_WIDE:
wt = ", 16 bit wide";
break;
case DPARM_TQING:
wt = ", Tagged Queueing Enabled";
break;
case DPARM_WIDE|DPARM_TQING:
wt = ", 16 bit wide, Tagged Queueing Enabled";
break;
default:
wt = " ";
break;
}
if (mhz) {
isp_prt(isp, ISP_LOGINFO, "Chan %d Target %d at %dMHz Max Offset %d%s", bus, tgt, mhz, sdp->isp_devparam[tgt].actv_offset, wt);
} else {
isp_prt(isp, ISP_LOGINFO, "Chan %d Target %d Async Mode%s", bus, tgt, wt);
}
}
break;
case ISPASYNC_LIP:
isp_prt(isp, ISP_LOGINFO, "LIP Received");
break;
case ISPASYNC_LOOP_RESET:
va_start(ap, cmd);
bus = va_arg(ap, int);
va_end(ap);
isp_prt(isp, ISP_LOGINFO, "Chan %d Loop Reset Received", bus);
break;
case ISPASYNC_BUS_RESET:
va_start(ap, cmd);
bus = va_arg(ap, int);
va_end(ap);
isp_prt(isp, ISP_LOGINFO, "SCSI bus %d reset detected", bus);
break;
case ISPASYNC_LOOP_DOWN:
va_start(ap, cmd);
bus = va_arg(ap, int);
va_end(ap);
isp_prt(isp, ISP_LOGINFO, "Chan %d Loop DOWN", bus);
break;
case ISPASYNC_LOOP_UP:
va_start(ap, cmd);
bus = va_arg(ap, int);
va_end(ap);
isp_prt(isp, ISP_LOGINFO, "Chan %d Loop UP", bus);
break;
case ISPASYNC_DEV_ARRIVED:
va_start(ap, cmd);
bus = va_arg(ap, int);
lp = va_arg(ap, fcportdb_t *);
va_end(ap);
fcp = FCPARAM(isp, bus);
if ((fcp->role & ISP_ROLE_INITIATOR) != 0 && (lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT))) {
int dbidx = lp - fcp->portdb;
int i;
for (i = 0; i < MAX_FC_TARG; i++) {
if (i >= FL_ID && i <= SNS_ID) {
continue;
}
if (fcp->isp_ini_map[i] == 0) {
break;
}
}
if (i < MAX_FC_TARG) {
fcp->isp_ini_map[i] = dbidx + 1;
lp->ini_map_idx = i + 1;
} else {
isp_prt(isp, ISP_LOGWARN, "out of target ids");
isp_dump_portdb(isp, bus);
}
}
if (lp->ini_map_idx) {
tgt = lp->ini_map_idx - 1;
isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, isp_class3_roles[lp->roles], "arrived at", tgt, lp->node_wwn, lp->port_wwn);
} else {
isp_prt(isp, ISP_LOGCONFIG, prom0, bus, lp->portid, lp->handle, isp_class3_roles[lp->roles], "arrived", lp->node_wwn, lp->port_wwn);
}
break;
case ISPASYNC_DEV_CHANGED:
va_start(ap, cmd);
bus = va_arg(ap, int);
lp = va_arg(ap, fcportdb_t *);
va_end(ap);
fcp = FCPARAM(isp, bus);
lp->portid = lp->new_portid;
lp->roles = lp->new_roles;
if (lp->ini_map_idx) {
int t = lp->ini_map_idx - 1;
fcp->isp_ini_map[t] = (lp - fcp->portdb) + 1;
tgt = lp->ini_map_idx - 1;
isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, isp_class3_roles[lp->roles], "changed at", tgt, lp->node_wwn, lp->port_wwn);
} else {
isp_prt(isp, ISP_LOGCONFIG, prom0, bus, lp->portid, lp->handle, isp_class3_roles[lp->roles], "changed", lp->node_wwn, lp->port_wwn);
}
break;
case ISPASYNC_DEV_STAYED:
va_start(ap, cmd);
bus = va_arg(ap, int);
lp = va_arg(ap, fcportdb_t *);
va_end(ap);
if (lp->ini_map_idx) {
tgt = lp->ini_map_idx - 1;
isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, isp_class3_roles[lp->roles], "stayed at", tgt, lp->node_wwn, lp->port_wwn);
} else {
isp_prt(isp, ISP_LOGCONFIG, prom0, bus, lp->portid, lp->handle, isp_class3_roles[lp->roles], "stayed", lp->node_wwn, lp->port_wwn);
}
break;
case ISPASYNC_DEV_GONE:
va_start(ap, cmd);
bus = va_arg(ap, int);
lp = va_arg(ap, fcportdb_t *);
va_end(ap);
fcp = FCPARAM(isp, bus);
if (lp->ini_map_idx) {
tgt = lp->ini_map_idx - 1;
fcp->isp_ini_map[tgt] = 0;
lp->state = FC_PORTDB_STATE_NIL;
lp->ini_map_idx = 0;
isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, isp_class3_roles[lp->roles], "departed", tgt, lp->node_wwn, lp->port_wwn);
} else if (lp->reserved == 0) {
isp_prt(isp, ISP_LOGCONFIG, prom0, bus, lp->portid, lp->handle, isp_class3_roles[lp->roles], "departed", lp->node_wwn, lp->port_wwn);
}
break;
case ISPASYNC_CHANGE_NOTIFY:
{
int chg, nphdl, nlstate, reason;
va_start(ap, cmd);
bus = va_arg(ap, int);
chg = va_arg(ap, int);
if (chg == ISPASYNC_CHANGE_PDB) {
nphdl = va_arg(ap, int);
nlstate = va_arg(ap, int);
reason = va_arg(ap, int);
} else {
nphdl = NIL_HANDLE;
nlstate = reason = 0;
}
va_end(ap);
fcp = FCPARAM(isp, bus);
if (chg == ISPASYNC_CHANGE_PDB) {
if (IS_24XX(isp)) {
isp_prt(isp, ISP_LOGINFO, "Chan %d Port Database Changed, N-Port Handle 0x%04x nlstate %x reason 0x%02x", bus, nphdl, nlstate, reason);
} else {
isp_prt(isp, ISP_LOGINFO, "Chan %d Port Database Changed", bus);
}
} else if (chg == ISPASYNC_CHANGE_SNS) {
isp_prt(isp, ISP_LOGINFO, "Chan %d Name Server Database Changed", bus);
} else {
isp_prt(isp, ISP_LOGINFO, "Chan %d Other Change Notify occurred", bus);
}
if (isp->isp_state >= ISP_INITSTATE) {
ISP_THREAD_EVENT(isp, ISP_THREAD_FC_RESCAN, fcp, 0, __FUNCTION__, __LINE__);
}
break;
}
#ifdef ISP_TARGET_MODE
case ISPASYNC_TARGET_NOTIFY:
{
isp_notify_t *ins;
tmd_notify_t *mp;
va_start(ap, cmd);
mp = va_arg(ap, tmd_notify_t *);
va_end(ap);
if (isp->isp_osinfo.hcb == 0) {
isp_prt(isp, ISP_LOGWARN, "ISPASYNC_TARGET_NOTIFY with target mode not enabled");
isp_notify_ack(isp, mp->nt_lreserved);
break;
}
ins = isp->isp_osinfo.nfreelist;
if (ins == NULL) {
isp_prt(isp, ISP_LOGERR, "out of TMD NOTIFY structs");
isp_notify_ack(isp, mp->nt_lreserved);
break;
}
isp->isp_osinfo.nfreelist = ins->notify.nt_lreserved;
MEMCPY(&ins->notify, mp, sizeof (tmd_notify_t));
if (mp->nt_lreserved) {
MEMCPY(ins->qentry, mp->nt_lreserved, QENTRY_LEN);
ins->qevalid = 1;
} else {
ins->qevalid = 0;
}
mp = &ins->notify;
if (IS_24XX(isp)) {
at7_entry_t *aep = mp->nt_lreserved;
uint32_t sid;
int i;
if (aep) {
sid = (aep->at_hdr.s_id[0] << 16) | (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2];
} else {
sid = 0xffffff;
}
switch (mp->nt_ncode) {
case NT_HBA_RESET:
case NT_LINK_UP:
case NT_LINK_DOWN:
break;
case NT_LUN_RESET:
case NT_TARGET_RESET:
/*
* Mark all pertinent commands as dead and needing cleanup.
*/
for (i = 0; i < NTGT_CMDS; i++) {
tmd_cmd_t *tmd = &isp->isp_osinfo.pool[i];
if (tmd->cd_lflags & CDFL_BUSY) {
if (mp->nt_channel == tmd->cd_channel && (mp->nt_lun == LUN_ANY || mp->nt_lun == L0LUN_TO_FLATLUN(tmd->cd_lun))) {
tmd->cd_lflags |= CDFL_ABORTED|CDFL_NEED_CLNUP;
}
}
}
/* FALLTHROUGH */
default:
if (isp_find_pdb_by_sid(isp, mp->nt_channel, sid, &lp)) {
mp->nt_iid = lp->port_wwn;
}
break;
}
/*
* Replace target with our port WWN.
*/
mp->nt_tgt = FCPARAM(isp, mp->nt_channel)->isp_wwpn;
} else if (IS_FC(isp)) {
uint16_t loopid;
FC_TAG_INSERT_INST(mp->nt_tagval, isp->isp_unit);
/*
* The outer layer just set the loopid into nt_iid. We try and find the WWPN.
*/
loopid = ins->notify.nt_iid;
switch (mp->nt_ncode) {
case NT_HBA_RESET:
case NT_LINK_UP:
case NT_LINK_DOWN:
ins->notify.nt_iid = INI_NONE;
break;
default:
if (isp_find_pdb_by_loopid(isp, mp->nt_channel, loopid, &lp) == 0) {
isp_prt(isp, ISP_LOGTINFO, "cannot find WWN for loopid 0x%x for notify action 0x%x", loopid, mp->nt_ncode);
ins->notify.nt_iid = INI_NONE;
} else {
ins->notify.nt_iid = lp->port_wwn;
}
break;
}
/*
* Replace target with our port WWN.
*/
mp->nt_tgt = FCPARAM(isp, mp->nt_channel)->isp_wwpn;
} else {
TAG_INSERT_INST(mp->nt_tagval, isp->isp_unit);
}
isp_prt(isp, ISP_LOGTINFO, "Notify Code 0x%x iid 0x%016llx tgt 0x%016llx lun %u tag %llx",
mp->nt_ncode, (unsigned long long) mp->nt_iid, (unsigned long long) mp->nt_tgt,
mp->nt_lun, mp->nt_tagval);
CALL_PARENT_NOTIFY(isp, ins);
break;
}
case ISPASYNC_TARGET_ACTION:
{
void *qe;
va_start(ap, cmd);
qe = va_arg(ap, void *);
va_end(ap);
switch (((isphdr_t *)qe)->rqs_entry_type) {
case RQSTYPE_ATIO:
if (IS_24XX(isp)) {
isp_handle_platform_atio7(isp, (at7_entry_t *) qe);
} else {
isp_handle_platform_atio(isp, (at_entry_t *) qe);
}
break;
case RQSTYPE_ATIO2:
isp_handle_platform_atio2(isp, (at2_entry_t *)qe);
break;
case RQSTYPE_CTIO7:
case RQSTYPE_CTIO3:
case RQSTYPE_CTIO2:
case RQSTYPE_CTIO:
isp_handle_platform_ctio(isp, qe);
break;
case RQSTYPE_ENABLE_LUN:
case RQSTYPE_MODIFY_LUN:
isp->isp_osinfo.rstatus = ((lun_entry_t *)qe)->le_status;
if (isp->isp_osinfo.rsemap) {
up(isp->isp_osinfo.rsemap);
}
break;
case RQSTYPE_ABTS_RCVD:
{
isp_notify_t *ins = NULL;
abts_t *abts = qe;
abts_rsp_t *rsp = qe;
int chan, i;
uint32_t sid;
rsp->abts_rsp_header.rqs_entry_type = RQSTYPE_ABTS_RSP;
sid = (abts->abts_sid_hi << 16) | abts->abts_sid_lo;
if (isp->isp_osinfo.hcb == 0) {
isp_prt(isp, ISP_LOGINFO, "RQSTYPE_ABTS_RCVD: with no upstream listener");
rsp->abts_rsp_ctl_flags = ISP24XX_ABTS_RSP_TERMINATE;
isp_notify_ack(isp, qe);
break;
}
if (abts->abts_rxid_task == ISP24XX_NO_TASK) {
isp_prt(isp, ISP_LOGTINFO, "ABTS from N-Port handle 0x%x Port 0x%06x has no task id (rx_id 0x%04x ox_id 0x%04x)",
abts->abts_nphdl, sid, abts->abts_rx_id, abts->abts_ox_id);
for (i = 0; i < NTGT_CMDS; i++) {
tmd_cmd_t *tmd = &isp->isp_osinfo.pool[i];
if (tmd->cd_lflags & CDFL_BUSY) {
if (AT2_GET_TAG(tmd->cd_tagval) == abts->abts_rx_id && tmd->cd_oxid == abts->abts_ox_id) {
isp_prt(isp, ISP_LOGTINFO, "... but found tmd to to mark as aborted");
tmd->cd_lflags |= CDFL_ABORTED|CDFL_NEED_CLNUP;
break;
}
}
}
rsp->abts_rsp_ctl_flags = ISP24XX_ABTS_RSP_TERMINATE;
isp_notify_ack(isp, qe);
break;
}
ins = isp->isp_osinfo.nfreelist;
if (ins == NULL) {
isp_prt(isp, ISP_LOGTINFO, "out of TMD NOTIFY structs for RQSTYPE_ABTS_RCVD");
rsp->abts_rsp_ctl_flags = ISP24XX_ABTS_RSP_TERMINATE;
isp_notify_ack(isp, qe);
break;
}
isp->isp_osinfo.nfreelist = ins->notify.nt_lreserved;
MEMZERO(&ins->notify, sizeof (tmd_notify_t));
lp = NULL;
for (chan = 0; chan < isp->isp_nchan; chan++) {
if (isp_find_pdb_by_loopid(isp, chan, abts->abts_nphdl, &lp)) {
break;
}
}
if (lp == NULL) {
isp_prt(isp, ISP_LOGTINFO, "cannot find WWN for N-port handle 0x%x for ABTS", abts->abts_nphdl);
ins->notify.nt_iid = INI_ANY;
} else {
ins->notify.nt_iid = lp->port_wwn;
}
MEMCPY(ins->qentry, qe, QENTRY_LEN);
ins->qevalid = 1;
ins->notify.nt_hba = isp;
ins->notify.nt_tgt = FCPARAM(isp, chan)->isp_wwpn;
ins->notify.nt_lun = LUN_ANY;
ins->notify.nt_tagval = abts->abts_rxid_task;
FC_TAG_INSERT_INST(ins->notify.nt_tagval, isp->isp_unit);
ins->notify.nt_ncode = NT_ABORT_TASK;
ins->notify.nt_need_ack = 1;
ins->notify.nt_channel = chan;
/*
* Find the command if possible and mark it aborted and needing cleanup
*/
for (i = 0; i < NTGT_CMDS; i++) {
tmd_cmd_t *tmd = &isp->isp_osinfo.pool[i];
if (tmd->cd_lflags & CDFL_BUSY) {
if (ins->notify.nt_tagval == tmd->cd_tagval && ins->notify.nt_channel == tmd->cd_channel) {
isp_prt(isp, ISP_LOGTINFO, "[0x%llx] marked as aborted", (unsigned long long) tmd->cd_tagval);
tmd->cd_lflags |= CDFL_ABORTED|CDFL_NEED_CLNUP;
break;
}
}
}
isp_prt(isp, ISP_LOGTINFO, "ABTS [%llx] from 0x%016llx", ins->notify.nt_tagval, ins->notify.nt_iid);
CALL_PARENT_NOTIFY(isp, ins);
break;
}
case RQSTYPE_NOTIFY:
{
isp_notify_t *ins = NULL;
uint16_t status, nphdl;
uint32_t lun, seqid, portid;
if (isp->isp_osinfo.hcb == 0) {
isp_prt(isp, ISP_LOGWARN, "TARGET_NOTIFY with no upstream listener");
isp_notify_ack(isp, qe);
break;
}
if (qe == NULL) {
isp_prt(isp, ISP_LOGERR, "null argument for RQSTYPE_NOTIFY");
break;
}
if (IS_SCSI(isp)) {
in_entry_t *inot = qe;
status = inot->in_status;
if (inot->in_status == IN_ABORT_TASK) {
ins = isp->isp_osinfo.nfreelist;
if (ins == NULL) {
isp_prt(isp, ISP_LOGERR, "out of TMD NOTIFY structs for RQSTYPE_NOTIFY!");
isp_notify_ack(isp, qe);
break;
}
isp->isp_osinfo.nfreelist = ins->notify.nt_lreserved;
MEMZERO(&ins->notify, sizeof (tmd_notify_t));
MEMCPY(ins->qentry, qe, QENTRY_LEN);
ins->qevalid = 1;
ins->notify.nt_hba = isp;
ins->notify.nt_iid = GET_IID_VAL(inot->in_iid);
ins->notify.nt_tgt = inot->in_tgt;
ins->notify.nt_lun = inot->in_lun;
IN_MAKE_TAGID(ins->notify.nt_tagval, GET_BUS_VAL(inot->in_iid), isp->isp_unit, inot);
ins->notify.nt_ncode = NT_ABORT_TASK;
ins->notify.nt_need_ack = 1;
isp_prt(isp, ISP_LOGINFO, "ABORT TASK [%llx] from iid %u to lun %u", ins->notify.nt_tagval,
(uint32_t) ins->notify.nt_iid, inot->in_lun);
CALL_PARENT_NOTIFY(isp, ins);
break;
} else {
isp_notify_ack(isp, qe);
}
break;
} else if (IS_24XX(isp)) {
in_fcentry_24xx_t *inot = qe;
uint64_t wwn;
nphdl = inot->in_nphdl;
if (nphdl != NIL_HANDLE) {
portid = inot->in_portid_hi << 16 | inot->in_portid_lo;
} else {
portid = PORT_ANY;
}
status = inot->in_status;
seqid = inot->in_rxid;
lun = 0;
switch (status) {
case IN24XX_ELS_RCVD:
{
char *msg = NULL;
switch (inot->in_status_subcode) {
case LOGO:
msg = "LOGO";
if (ISP_FW_NEWER_THAN(isp, 4, 0, 25)) {
uint8_t *ptr = qe; /* point to unswizzled entry! */
wwn =
(((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF]) << 56) |
(((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+1]) << 48) |
(((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+2]) << 40) |
(((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+3]) << 32) |
(((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+4]) << 24) |
(((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+5]) << 16) |
(((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+6]) << 8) |
(((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+7]));
} else {
wwn = INI_ANY;
}
isp_del_wwn_entry(isp, inot->in_vpindex, wwn, nphdl, portid);
break;
case PRLO:
msg = "PRLO";
break;
case PLOGI:
msg = "PLOGI";
if (ISP_FW_NEWER_THAN(isp, 4, 0, 25)) {
uint8_t *ptr = qe; /* point to unswizzled entry! */
wwn =
(((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF]) << 56) |
(((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+1]) << 48) |
(((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+2]) << 40) |
(((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+3]) << 32) |
(((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+4]) << 24) |
(((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+5]) << 16) |
(((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+6]) << 8) |
(((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+7]));
} else {
wwn = INI_NONE;
}
isp_add_wwn_entry(isp, inot->in_vpindex, wwn, nphdl, portid);
break;
case PRLI:
msg = "PRLI";
break;
case PDISC:
isp_prt(isp, ISP_LOGTINFO, "%s: Chan %d IID N-Port Handle 0x%x Port ID 0x%06x PDISC",
__FUNCTION__, inot->in_vpindex, nphdl, portid);
break;
default:
isp_prt(isp, ISP_LOGTINFO, "ELS CODE 0x%x Received from 0x%06x", inot->in_status_subcode, portid);
break;
}
if (msg) {
isp_prt(isp, ISP_LOGTINFO, "%s Chan %d ELS N-port handle %x PortID 0x%06x", msg, inot->in_vpindex, nphdl, portid);
}
if ((inot->in_flags & IN24XX_FLAG_PUREX_IOCB) == 0) {
isp_notify_ack(isp, qe);
}
break;
}
case IN24XX_PORT_CHANGED:
case IN24XX_PORT_LOGOUT:
ins = isp->isp_osinfo.nfreelist;
if (ins == NULL) {
isp_prt(isp, ISP_LOGERR, "out of TMD NOTIFY structs for RQSTYPE_NOTIFY!");
isp_notify_ack(isp, qe);
break;
}
isp->isp_osinfo.nfreelist = ins->notify.nt_lreserved;
MEMZERO(ins, sizeof (*ins));
if (isp_find_pdb_by_loopid(isp, inot->in_vpindex, nphdl, &lp) == 0) {
isp_prt(isp, ISP_LOGWARN, "cannot find WWN for N-port handle 0x%x for ABORT TASK", nphdl);
isp_notify_ack(isp, qe);
ins->notify.nt_lreserved = isp->isp_osinfo.nfreelist;
isp->isp_osinfo.nfreelist = ins;
break;
}
ins->notify.nt_iid = lp->port_wwn;
MEMCPY(ins->qentry, qe, QENTRY_LEN);
ins->qevalid = 1;
ins->notify.nt_hba = isp;
ins->notify.nt_ncode = NT_LOGOUT;
isp_prt(isp, ISP_LOGTINFO, "%s: isp_del_wwn being called on Chan %d because of PC/PL(0x%x) for 0x%016llx loopid 0x%02x",
__FUNCTION__, inot->in_vpindex, status, (unsigned long long) ins->notify.nt_iid, nphdl);
isp_del_wwn_entry(isp, inot->in_vpindex, ins->notify.nt_iid, nphdl, portid);
ins->notify.nt_tagval = seqid;
isp_prt(isp, ISP_LOGINFO, "PORT %s [%llx] from 0x%016llx", status == IN24XX_PORT_CHANGED? "CHANGED" : "LOGOUT",
ins->notify.nt_tagval, ins->notify.nt_iid);
CALL_PARENT_NOTIFY(isp, ins);
break;
case IN24XX_LIP_RESET: /* XXX: SHOULD BE TREATED LIKE BUS RESET */
case IN24XX_LINK_RESET: /* XXX: EXCEPT THAT WE HAVE TO HAVE THE */
case IN24XX_LINK_FAILED:/* XXX: ENTRY TO ACK BACK WITH */
case IN24XX_SRR_RCVD:
default:
isp_notify_ack(isp, qe);
break;
}
} else if (IS_FC(isp)) {
if (ISP_CAP_2KLOGIN(isp)) {
in_fcentry_e_t *inot = qe;
nphdl = inot->in_iid;
status = inot->in_status;
seqid = inot->in_seqid;
lun = inot->in_scclun;
} else {
in_fcentry_t *inot = qe;
nphdl = inot->in_iid;
status = inot->in_status;
seqid = inot->in_seqid;
if (ISP_CAP_SCCFW(isp)) {
lun = inot->in_scclun;
} else {
lun = inot->in_lun;
}
}
if (status == IN_ABORT_TASK || status == IN_PORT_LOGOUT || status == IN_GLOBAL_LOGO) {
ins = isp->isp_osinfo.nfreelist;
if (ins == NULL) {
isp_prt(isp, ISP_LOGWARN, "out of TMD NOTIFY structs for RQSTYPE_NOTIFY!");
isp_notify_ack(isp, qe);
break;
}
isp->isp_osinfo.nfreelist = ins->notify.nt_lreserved;
MEMZERO(&ins->notify, sizeof (tmd_notify_t));
MEMCPY(ins->qentry, qe, QENTRY_LEN);
ins->qevalid = 1;
ins->notify.nt_hba = isp;
} else {
isp_prt(isp, ISP_LOGINFO, "skipping handling of Notify Status 0x%x", status);
isp_notify_ack(isp, qe);
break;
}
if (status == IN_ABORT_TASK) {
if (isp_find_pdb_by_loopid(isp, 0, nphdl, &lp) == 0) {
isp_prt(isp, ISP_LOGINFO, "cannot find WWN for loopid 0x%x for ABORT TASK", nphdl);
ins->notify.nt_iid = INI_NONE;
} else {
ins->notify.nt_iid = lp->port_wwn;
}
ins->notify.nt_channel = 0;
ins->notify.nt_tgt = FCPARAM(isp, 0)->isp_wwpn;
ins->notify.nt_lun = lun;
ins->notify.nt_need_ack = 1;
IN_FC_MAKE_TAGID(ins->notify.nt_tagval, 0, isp->isp_unit, seqid);
ins->notify.nt_ncode = NT_ABORT_TASK;
isp_prt(isp, ISP_LOGINFO, "ABORT TASK [%llx] from 0x%016llx to lun %u", ins->notify.nt_tagval,
(unsigned long long) ins->notify.nt_iid, lun);
CALL_PARENT_NOTIFY(isp, ins);
break;
} else if (status == IN_PORT_LOGOUT) {
/*
* The port specified by the loop id listed in nphdl has logged out. We need to tell our upstream listener about it.
*/
if (isp_find_pdb_by_loopid(isp, 0, nphdl, &lp)) {
ins->notify.nt_iid = lp->port_wwn;
ins->notify.nt_ncode = NT_LOGOUT;
isp_prt(isp, ISP_LOGTINFO, "%s: isp_del_wwn called for 0x%016llx due to PORT_LOGOUT", __FUNCTION__, (unsigned long long)ins->notify.nt_iid);
isp_del_wwn_entry(isp, 0, ins->notify.nt_iid, nphdl, PORT_ANY);
IN_FC_MAKE_TAGID(ins->notify.nt_tagval, 0, isp->isp_unit, seqid);
isp_prt(isp, ISP_LOGINFO, "PORT LOGOUT [%llx] from 0x%016llx", ins->notify.nt_tagval, (unsigned long long) ins->notify.nt_iid);
ins->notify.nt_need_ack = 1;
CALL_PARENT_NOTIFY(isp, ins);
break;
}
/*
* We don't know the WWPN for this loop ID. This likely
* is because we've not as yet received a command from
* this initiator (and we're not maintaining an active
* port database ourselves).
*
* We could turn this into a (synthesized) global
* logout, but it's just as well that we just ack
* it and move on. After all, if we've not yet received
* a command for this initiator, we don't have to
* note that it left as it hasn't really arrived yet
* (at least to any upstream command interpreter),
* now has it?
*/
ins->notify.nt_lreserved = isp->isp_osinfo.nfreelist;
isp->isp_osinfo.nfreelist = ins;
isp_prt(isp, ISP_LOGINFO, "Port Logout at handle 0x%x (seqid 0x%x) but have no WWPN for it- just ACKing", nphdl, seqid);
isp_notify_ack(isp, qe);
} else if (status == IN_GLOBAL_LOGO) {
/*
* Everyone Logged Out
*/
isp_prt(isp, ISP_LOGTINFO, "%s: isp_del_wwn called for everyone due to GLOBAL PORT_LOGOUT", __FUNCTION__);
isp_del_wwn_entry(isp, 0, INI_ANY, NIL_HANDLE, PORT_ANY);
ins->notify.nt_iid = INI_ANY;
ins->notify.nt_ncode = NT_LOGOUT;
ins->notify.nt_need_ack = 1;
CALL_PARENT_NOTIFY(isp, ins);
} else {
isp_prt(isp, ISP_LOGINFO, "%s: ACKing unknown status 0x%x", __FUNCTION__, status);
isp_notify_ack(isp, qe);
}
}
break;
}
default:
isp_prt(isp, ISP_LOGWARN, "event 0x%x for unhandled target action", ((isphdr_t *)qe)->rqs_entry_type);
break;
}
break;
}
#endif
case ISPASYNC_FW_CRASH:
{
uint16_t mbox1, mbox6;
mbox1 = ISP_READ(isp, OUTMAILBOX1);
if (IS_DUALBUS(isp)) {
mbox6 = ISP_READ(isp, OUTMAILBOX6);
} else {
mbox6 = 0;
}
isp_prt(isp, ISP_LOGERR, "Internal F/W Error on bus %d @ RISC Address 0x%x", mbox6, mbox1);
ISP_DATA(isp, mbox6)->blocked = 1;
#ifdef ISP_FW_CRASH_DUMP
if (IS_FC(isp)) {
ISP_THREAD_EVENT(isp, ISP_THREAD_FW_CRASH_DUMP, NULL, 0, __FUNCTION__, __LINE__);
break;
}
#endif
ISP_RESET0(isp);
isp_shutdown(isp);
ISP_THREAD_EVENT(isp, ISP_THREAD_REINIT, NULL, 0, __FUNCTION__, __LINE__);
break;
}
case ISPASYNC_FW_RESTARTED:
{
if (IS_FC(isp)) {
int i;
for (i = 0; i < isp->isp_nchan; i++) {
ISP_THREAD_EVENT(isp, ISP_THREAD_FC_RESCAN, FCPARAM(isp, i), 0, __FUNCTION__, __LINE__);
}
}
break;
}
default:
break;
}
}
int
isplinux_biosparam(struct scsi_device *sdev, struct block_device *n, sector_t capacity, int ip[])
{
int size = capacity;
ip[0] = 64;
ip[1] = 32;
ip[2] = size >> 11;
if (ip[2] > 1024) {
ip[0] = 255;
ip[1] = 63;
ip[2] = size / (ip[0] * ip[1]);
}
return (0);
}
static int
isplinux_slave_configure(struct scsi_device * device)
{
if (device->tagged_supported) {
/*
* XXX: FIX LATER
*/
scsi_adjust_queue_depth(device, MSG_ORDERED_TAG, 63);
}
return 0;
}
int
isplinux_get_default_id(ispsoftc_t *isp, int chan)
{
if (IS_FC(isp))
return (isp_fc_id);
else
return (isp_spi_id);
}
int
isplinux_get_default_role(ispsoftc_t *isp, int chan)
{
return (ISP_DATA(isp, chan)->role);
}
void
isplinux_set_default_role(ispsoftc_t *isp, int chan, int role)
{
ISP_DATA(isp, chan)->role = role;
}
/*
* When we want to get the 'default' WWNs (when lacking NVRAM), we pick them up
* from our platform default (defww{p|n}n) and morph them based upon channel.
*
* When we want to get the 'active' WWNs, we get NVRAM WWNs and then morph
* them based upon channel.
*/
uint64_t
isplinux_default_wwn(ispsoftc_t *isp, int chan, int isactive, int iswwnn)
{
uint64_t seed;
isp_data *fc = ISP_DATA(isp, chan);
/*
* If we're asking for a active WWN, the default overrides get returned,
* otherwise the NVRAM value is picked.
*
* If we're asking for a default WWN, we just pick the default override.
*/
if (isactive) {
seed = iswwnn? fc->def_wwnn : fc->def_wwpn;
if (seed) {
return (seed);
}
seed = iswwnn? FCPARAM(isp, chan)->isp_wwnn_nvram : FCPARAM(isp, chan)->isp_wwpn_nvram;
} else {
seed = iswwnn? fc->def_wwnn : fc->def_wwpn;
}
/*
* For channel zero just return what we have. For either ACIIVE or DEFAULT cases,
* we depend on default override of NVRAM values for channel zero.
*/
if (chan == 0) {
return (seed);
}
/*
* For other channels, we are doing one of three things:
*
* 1. If what we have now is non-zero, return it. Otherwise
* we morph values from channel 0.
* 2. If we're here for a WWPN we synthesize it if
* Channel 0's wwpn has a type 2 NAA.
* 3. If we're here for a WWNN we synthesize it if
* Channel 0's wwnn has a type 2 NAA.
*/
if (seed) {
return (seed);
}
if (isactive) {
seed = iswwnn? FCPARAM(isp, 0)->isp_wwnn_nvram : FCPARAM(isp, 0)->isp_wwpn_nvram;
} else {
seed = iswwnn? ISP_DATA(isp, 0)->def_wwnn : ISP_DATA(isp, 0)->def_wwpn;
}
if (((seed >> 60) & 0xf) == 2) {
/*
* The type 2 NAA fields for QLogic cards appear be laid out thusly:
*
* bits 63..60 NAA == 2
* bits 59..57 unused/zero
* bit 56 port (1) or node (0) WWN distinguishor
* bit 48 physical port on dual-port chips (23XX/24XX)
*
* This is somewhat nutty, particularly since bit 48 is irrelevant
* as they assign seperate serial numbers to different physical ports
* anyway.
*
* We'll stick our channel number plus one first into bits 57..59 and
* thence into bits 52..55 which allows for 8 bits of channel which is
* comfortably more than our maximum (126) now.
*/
seed &= ~0x0FF0000000000000ULL;
if (iswwnn == 0) {
seed |= ((uint64_t) (chan+1) & 0xf) << 56;
seed |= ((uint64_t) ((chan+1) >> 4) & 0xf) << 52;
}
} else {
seed = 0;
}
return (seed);
}
/*
* Periodic watchdog timer.. the main purpose here is to restart
* commands that were pegged on resources, etc...
*/
void
isplinux_timer(unsigned long arg)
{
Scsi_Cmnd *Cmnd;
ispsoftc_t *isp = (ispsoftc_t *) arg;
uint32_t isr;
uint16_t sema, mbox;
unsigned long flags;
int i;
#ifdef ISP_TARGET_MODE
tmd_cmd_t *wt = NULL;
int didintr = 0;
#endif
ISP_ILOCK_SOFTC(isp);
isp->isp_osinfo.dogcnt++;
if (isp->isp_dead == 0) {
if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) {
isp_intr(isp, isr, sema, mbox);
if (isp->intsok) {
ISP_ENABLE_INTS(isp);
}
#ifdef ISP_TARGET_MODE
didintr = 1;
#endif
}
}
for (i = 0; i < isp->isp_nchan; i++) {
if (ISP_DATA(isp, i)->qfdelay) {
ISP_DATA(isp, i)->qfdelay--;
}
}
if (IS_FC(isp) && isp->isp_state == ISP_RUNSTATE) {
for (i = 0 ; i < isp->isp_nchan; i++) {
fcparam *fcp = FCPARAM(isp, i);
if (fcp->role != ISP_ROLE_NONE && ISP_DATA(isp, i)->fcrswdog && ISP_DATA(isp, i)->deadloop == 0) {
ISP_DATA(isp, i)->fcrswdog = 1;
ISP_THREAD_EVENT(isp, ISP_THREAD_FC_RESCAN, fcp, 0, __FUNCTION__, __LINE__);
}
}
}
isplinux_runwaitq(isp);
if ((Cmnd = isp->isp_osinfo.dqnext) != NULL) {
isp->isp_osinfo.dqnext = isp->isp_osinfo.dqtail = NULL;
}
if (isp->dogactive) {
isp->isp_osinfo.timer.expires = jiffies + ISP_WATCH_TIME;
add_timer(&isp->isp_osinfo.timer);
}
#ifdef ISP_TARGET_MODE
if (isp->isp_osinfo.dogcnt == 0) {
while ((wt = isp->isp_osinfo.waiting_t) != NULL) {
isp->isp_osinfo.waiting_t = wt->cd_next;
wt->cd_next = NULL;
if (wt->cd_lastoff == 0) {
ISP_THREAD_EVENT(isp, ISP_THREAD_FINDIID, wt, 0, __FUNCTION__, __LINE__);
} else {
ISP_THREAD_EVENT(isp, ISP_THREAD_RESTART_AT7, wt, 0, __FUNCTION__, __LINE__);
}
}
}
#endif
ISP_IUNLK_SOFTC(isp);
#ifdef ISP_TARGET_MODE
if (didintr) {
isp_tgt_tq(isp);
}
#endif
if (Cmnd) {
ISP_LOCK_SCSI_DONE(isp);
while (Cmnd) {
Scsi_Cmnd *f = (Scsi_Cmnd *) Cmnd->host_scribble;
Cmnd->host_scribble = NULL;
(*Cmnd->scsi_done)(Cmnd);
Cmnd = f;
}
ISP_UNLK_SCSI_DONE(isp);
}
}
void
isplinux_mbtimer(unsigned long arg)
{
ispsoftc_t *isp = (ispsoftc_t *) arg;
unsigned long flags;
ISP_ILOCK_SOFTC(isp);
if (isp->mbox_waiting) {
isp->mbox_waiting = 0;
up(&isp->mbox_c_sem);
}
ISP_IUNLK_SOFTC(isp);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
#define PTARG , struct pt_regs *pt
#else
#define PTARG
#endif
irqreturn_t
isplinux_intr(int irq, void *arg PTARG)
{
ispsoftc_t *isp = arg;
uint32_t isr;
uint16_t sema, mbox;
Scsi_Cmnd *Cmnd;
unsigned long flags;
ISP_ILOCK_SOFTC(isp);
isp->isp_intcnt++;
if (ISP_READ_ISR(isp, &isr, &sema, &mbox) == 0) {
isp->isp_intbogus++;
if (isp->intsok) {
ISP_ENABLE_INTS(isp);
}
ISP_IUNLK_SOFTC(isp);
return IRQ_NONE;
}
isp_intr(isp, isr, sema, mbox);
isplinux_runwaitq(isp);
if ((Cmnd = isp->isp_osinfo.dqnext) != NULL) {
isp->isp_osinfo.dqnext = isp->isp_osinfo.dqtail = NULL;
}
if (isp->intsok) {
ISP_ENABLE_INTS(isp);
}
ISP_IUNLK_SOFTC(isp);
if (Cmnd) {
ISP_LOCK_SCSI_DONE(isp);
while (Cmnd) {
Scsi_Cmnd *f = (Scsi_Cmnd *) Cmnd->host_scribble;
Cmnd->host_scribble = NULL;
(*Cmnd->scsi_done)(Cmnd);
Cmnd = f;
}
ISP_UNLK_SCSI_DONE(isp);
}
#ifdef ISP_TARGET_MODE
isp_tgt_tq(isp);
#endif
return IRQ_HANDLED;
}
/*
* roles=DEVID=role[,...]
*/
static int
isp_parse_rolearg(ispsoftc_t *isp, int chan, char *roles)
{
char *role = roles;
while (role && *role) {
unsigned int id;
char *eqtok, *commatok, *p, *q;
eqtok = role;
eqtok = strchr(role, '=');
if (eqtok == NULL) {
break;
}
*eqtok = 0;
commatok = strchr(eqtok+1, ',');
if (commatok) {
*commatok = 0;
}
if (strncmp(role, "0x", 2) == 0) {
q = role + 2;
} else {
q = role;
}
if (*q == '*') {
id = isp->isp_osinfo.device_id;
p = NULL;
} else {
id = simple_strtoul(q, &p, 16);
}
*eqtok = '=';
if (p != q && id == isp->isp_osinfo.device_id) {
p = eqtok + 1;
if (strcmp(p, "none") == 0) {
if (commatok) {
*commatok = ',';
}
return (ISP_ROLE_NONE);
}
if (strcmp(p, "target") == 0) {
if (commatok) {
*commatok = ',';
}
return (ISP_ROLE_TARGET);
}
if (strcmp(p, "initiator") == 0) {
if (commatok) {
*commatok = ',';
}
return (ISP_ROLE_INITIATOR);
}
if (strcmp(p, "both") == 0) {
if (commatok) {
*commatok = ',';
}
return (ISP_ROLE_BOTH);
}
break;
}
if (commatok) {
role = commatok+1;
*commatok = ',';
} else {
break;
}
}
return (ISP_DEFAULT_ROLES);
}
/*
* isp_wwnns=DEVID=[chan:]wwn[,...]
* isp_wwpns=DEVID=[chan:]wwn[,...]
*/
static uint64_t
isp_parse_wwnarg(ispsoftc_t *isp, int chan, char *wwns)
{
char *wwnt = wwns;
uint64_t wwn = 0;
while (wwn == 0 && wwnt && *wwnt) {
unsigned int id;
int thischan;
char *eqtok, *commatok, *colontok, *wwnstart, *p, *q;
eqtok = wwnt;
eqtok = strchr(wwnt, '=');
if (eqtok == NULL) {
break;
}
*eqtok = 0;
commatok = strchr(eqtok+1, ',');
if (commatok) {
*commatok = 0;
}
q = wwnt;
colontok = strchr(eqtok+1, ':');
thischan = 0;
if (colontok) {
*colontok = 0;
q = eqtok + 1;
thischan = simple_strtoul(q, &p, 0);
*colontok = ':';
if (p == q) {
thischan = 0;
}
q = wwnt;
wwnstart = colontok + 1;
} else {
wwnstart = eqtok + 1;
}
if (strncmp(q, "0x", 2) == 0) {
q += 2;
}
id = simple_strtoul(q, &p, 16);
if (p != q && id == isp->isp_osinfo.device_id && thischan == chan) {
unsigned long t, t2;
p = wwnstart;
while (*p) {
p++;
}
p -= 8;
if (p > wwnstart) {
char *q;
char c;
q = p;
t = simple_strtoul(p, &q, 16);
c = *p;
*p = 0;
t2 = simple_strtoul(wwnstart, NULL, 16);
*p = c;
} else {
t = simple_strtoul(wwnstart, NULL, 16);
t2 = 0;
}
wwn = (((uint64_t) t2) << 32) | (uint64_t) t;
}
*eqtok = '=';
if (commatok) {
wwnt = commatok+1;
*commatok = ',';
} else {
break;
}
}
return (wwn);
}
int
isplinux_common_init(ispsoftc_t *isp)
{
int retval, chan;
unsigned long flags;
if (isp_nofwreload & (1 << isp->isp_unit)) {
isp->isp_confopts |= ISP_CFG_NORELOAD;
}
if (isp_nonvram & (1 << isp->isp_unit)) {
isp->isp_confopts |= ISP_CFG_NONVRAM;
}
if (IS_FC(isp)) {
if (isp_nport_only & (1 << isp->isp_unit)) {
isp->isp_confopts |= ISP_CFG_NPORT_ONLY;
} else if (isp_loop_only & (1 << isp->isp_unit)) {
isp->isp_confopts |= ISP_CFG_LPORT_ONLY;
} else {
isp->isp_confopts |= ISP_CFG_NPORT;
}
isp->isp_osinfo.host->this_id = MAX_FC_TARG+1;
#ifdef ISP_FW_CRASH_DUMP
if (IS_2200(isp)) {
FCPARAM(isp, 0)->isp_dump_data = isp_kalloc(QLA2200_RISC_IMAGE_DUMP_SIZE, GFP_KERNEL);
} else if (IS_23XX(isp)) {
FCPARAM(isp, 0)->isp_dump_data = isp_kalloc(QLA2300_RISC_IMAGE_DUMP_SIZE, GFP_KERNEL);
}
if (FCPARAM(isp, 0)->isp_dump_data) {
isp_prt(isp, ISP_LOGCONFIG, "f/w crash dump area allocated");
FCPARAM(isp, 0)->isp_dump_data[0] = 0;
}
#endif
if (isp_default_frame_size) {
if (isp_default_frame_size != 512 && isp_default_frame_size != 1024 && isp_default_frame_size != 2048) {
isp_prt(isp, ISP_LOGERR, "bad frame size (%d), defaulting to (%d)", isp_default_frame_size, ICB_DFLT_FRMLEN);
isp_default_frame_size = 0;
}
}
if (isp_default_exec_throttle) {
if (isp_default_exec_throttle < 1 || isp_default_exec_throttle > 255) {
isp_prt(isp, ISP_LOGERR, "bad execution throttle size (%d), defaulting to (%d)", isp_default_exec_throttle, ICB_DFLT_THROTTLE);
isp_default_exec_throttle = 0;
}
}
if (isp_fcduplex & (1 << isp->isp_unit)) {
isp->isp_confopts |= ISP_CFG_FULL_DUPLEX;
}
isp->isp_osinfo.host->max_id = MAX_FC_TARG;
isp->isp_osinfo.host->max_cmd_len = 16;
for (chan = 0; chan < isp->isp_nchan; chan++) {
isp_data *fc = ISP_DATA(isp, chan);
fc->def_wwnn = isp_parse_wwnarg(isp, chan, isp_wwnns);
fc->def_wwpn = isp_parse_wwnarg(isp, chan, isp_wwpns);
if (isp_default_frame_size) {
isp->isp_confopts |= ISP_CFG_OWNFSZ;
DEFAULT_FRAMESIZE(isp) = isp_default_frame_size;
} else {
DEFAULT_FRAMESIZE(isp) = ICB_DFLT_FRMLEN;
}
if (isp_default_exec_throttle) {
isp->isp_confopts |= ISP_CFG_OWNEXCTHROTTLE;
DEFAULT_EXEC_THROTTLE(isp) = isp_default_exec_throttle;
} else {
DEFAULT_EXEC_THROTTLE(isp) = ICB_DFLT_THROTTLE;
}
fc->role = isp_parse_rolearg(isp, chan, isp_roles);
SET_DEFAULT_ROLE(isp, chan, fc->role);
}
} else {
isp->isp_osinfo.host->max_id = MAX_TARGETS;
isp->isp_osinfo.host->max_cmd_len = 12;
isp->isp_osinfo.host->this_id = 7; /* temp default */
for (chan = 0; chan < isp->isp_nchan; chan++) {
SDPARAM(isp, chan)->role = isp_parse_rolearg(isp, chan, isp_roles);
SET_DEFAULT_ROLE(isp, chan, SDPARAM(isp, chan)->role);
}
}
if (isp_own_id) {
isp->isp_confopts |= ISP_CFG_OWNLOOPID;
}
/*
* Initialize locks
*/
ISP_LOCK_INIT(isp);
ISP_TLOCK_INIT(isp);
sema_init(&isp->mbox_sem, 1);
sema_init(&isp->mbox_c_sem, 0);
sema_init(&isp->fcs_sem, 1);
#ifdef ISP_TARGET_MODE
/*
* Initialize target stuff here
*/
if (isp_init_target(isp)) {
return (-1);
}
#endif
/*
* Start watchdog timer, create FC handler thread and reinit hardware.
*/
if (IS_FC(isp)) {
ISP_THREAD_EXEC(isp);
}
ISP_LOCK_SOFTC(isp);
init_timer(&isp->isp_osinfo.timer);
isp->isp_osinfo.timer.data = (unsigned long) isp;
isp->isp_osinfo.timer.function = isplinux_timer;
isp->isp_osinfo.timer.expires = jiffies + ISP_WATCH_TIME;
add_timer(&isp->isp_osinfo.timer);
isp->dogactive = 1;
retval = isplinux_reinit(isp);
if (retval) {
isp_prt(isp, ISP_LOGWARN, "failed to init HBA port (%d): skipping it", retval);
del_timer(&isp->isp_osinfo.timer);
isp->dogactive = 0;
ISP_UNLK_SOFTC(isp);
#ifdef ISP_TARGET_MODE
isp_deinit_target(isp);
#endif
ISP_THREAD_KILL(isp);
return (-1);
}
ISP_UNLK_SOFTC(isp);
#ifdef ISP_TARGET_MODE
isp_attach_target(isp);
#endif
return (0);
}
int
isplinux_reinit(ispsoftc_t *isp)
{
int maxluns = isp_maxluns;
isp_reset(isp);
if (isp->isp_state != ISP_RESETSTATE) {
isp_prt(isp, ISP_LOGERR, "failed to enter RESET state");
return (-1);
}
/*
* Until the midlayer starts using REPORT LUNS to dertermine how many
* luns there are for SCSI-3 devices and sets a reasonable limit for
* SCSI-2 devices, we'll follow this ruleset:
*
* If our isp_maxluns parameter is unchanged from its default, we
* limit ourselves to 8 luns for parallel SCSI, 256 for FC-SCSI.
*
* If somebody has set isp_maxluns away from the default, we follow that.
*
* We filter any value through the HBA maximum
*/
if (isp_maxluns == 8) {
if (IS_FC(isp)) {
maxluns = 256;
}
}
isp->isp_osinfo.host->max_lun = min(maxluns, ISP_MAX_LUNS(isp));
#if 0
/*
* If we're not taking a role, set some 'defaults' and turn off lasers (for FC cards).
*/
if (isp->isp_osinfo.role == ISP_ROLE_NONE) {
isp->isp_osinfo.host->can_queue = 16;
isp->isp_osinfo.host->can_queue = 1;
isp->isp_osinfo.host->cmd_per_lun = 1;
isp->isp_osinfo.host->this_id = IS_FC(isp)? MAX_FC_TARG : 7;
isp_shutdown(isp);
return (0);
}
#else
isp->isp_osinfo.host->can_queue = 16;
isp->isp_osinfo.host->can_queue = 1;
isp->isp_osinfo.host->cmd_per_lun = 1;
isp->isp_osinfo.host->this_id = IS_FC(isp)? MAX_FC_TARG : 7;
#endif
isp_init(isp);
if (isp->isp_state != ISP_INITSTATE) {
isp_prt(isp, ISP_LOGERR, "failed to enter INIT state");
return (-1);
}
isp->isp_osinfo.host->can_queue = isp->isp_maxcmds;
if (IS_FC(isp)) {
isp->isp_osinfo.host->this_id = MAX_FC_TARG;
/*
* This is *not* the same as execution throttle- that is set
* in isplinux_sqd and is per-device.
*
* What we try and do here is take how much we can queue at
* a given time and spread it, reasonably, over all the luns
* we expect to run at a time.
*/
if (isp_cmd_per_lun) {
isp->isp_osinfo.host->cmd_per_lun = isp_cmd_per_lun;
} else {
/*
* JAWAG.
*/
isp->isp_osinfo.host->cmd_per_lun = isp->isp_maxcmds >> 3;
}
/*
* We seem to need a bit of settle time.
*/
USEC_SLEEP(isp, 1 * 1000000);
} else {
int chan;
if (isp_cmd_per_lun) {
isp->isp_osinfo.host->cmd_per_lun = isp_cmd_per_lun;
} else {
/*
* Maximum total commands spread over either 8 targets,
* or 4 targets, 2 luns, etc.
*/
isp->isp_osinfo.host->cmd_per_lun = isp->isp_maxcmds >> 3;
}
/*
* No way to give different ID's for the second bus.
*/
isp->isp_osinfo.host->this_id = SDPARAM(isp, 0)->isp_initiator_id;
for (chan = 0; chan < isp->isp_nchan; chan++) {
(void) isp_control(isp, ISPCTL_RESET_BUS, chan);
}
/*
* Bus Reset delay handled by firmware.
*/
}
isp->isp_state = ISP_RUNSTATE;
return (0);
}
int
isp_thread_event(ispsoftc_t *isp, int action, void *a, int dowait, const char *file, const int line)
{
DECLARE_MUTEX_LOCKED(sem);
int i;
isp_thread_action_t *tap;
unsigned long flags;
spin_lock_irqsave(&isp->isp_osinfo.tlock, flags);
if (isp->isp_osinfo.task_active == 0) {
spin_unlock_irqrestore(&isp->isp_osinfo.tlock, flags);
isp_prt(isp, ISP_LOGERR, "thread event %d from %s:%d sent when thread gone", action, file, line);
return (-1);
}
/*
* Check for duplicates
*/
for (i = 0; i < isp->isp_osinfo.nt_actions; i++) {
tap = &isp->isp_osinfo.t_actions[i];
if (tap->thread_action == action && tap->arg == a && dowait == 0) {
tap->count++;
spin_unlock_irqrestore(&isp->isp_osinfo.tlock, flags);
isp_prt(isp, ISP_LOGDEBUG1, "async thread event %d from %s:%d now has count %d", action, file, line, tap->count);
return (0);
}
}
if (isp->isp_osinfo.nt_actions >= MAX_THREAD_ACTION) {
spin_unlock_irqrestore(&isp->isp_osinfo.tlock, flags);
isp_prt(isp, ISP_LOGERR, "thread event %d from %s:%d sent with thread overflow", action, file, line);
return (-1);
}
tap = &isp->isp_osinfo.t_actions[isp->isp_osinfo.nt_actions++];
tap->count = 1;
tap->thread_action = action;
tap->arg = a;
if (dowait) {
tap->thread_waiter = &sem;
} else {
tap->thread_waiter = 0;
}
ISP_THREAD_WAKE(isp);
if (dowait) {
isp_prt(isp, ISP_LOGDEBUG1, "action %d sent from %s:%d and now waiting", action, file, line);
spin_unlock_irqrestore(&isp->isp_osinfo.tlock, flags);
down(&sem);
isp_prt(isp, ISP_LOGDEBUG1, "action %d done", action);
} else {
spin_unlock_irqrestore(&isp->isp_osinfo.tlock, flags);
isp_prt(isp, ISP_LOGDEBUG1, "action %d from %s:%d sent", action, file, line);
}
return (0);
}
static int
isp_task_thread(void *arg)
{
ispsoftc_t *isp = arg;
unsigned long flags;
int action, nactions, i;
isp_thread_action_t curactions[MAX_THREAD_ACTION];
lock_kernel();
daemonize("%s_thrd%d", ISP_NAME, isp->isp_unit);
unlock_kernel();
siginitsetinv(&current->blocked, 0);
ISP_THREAD_IACK(isp);
isp_prt(isp, ISP_LOGDEBUG1, "isp_task_thread starting");
for (;;) {
isp_prt(isp, ISP_LOGDEBUG1, "isp_task_thread sleeping");
ISP_THREAD_WAIT(isp);
isp_prt(isp, ISP_LOGDEBUG1, "isp_task_thread running");
spin_lock_irqsave(&isp->isp_osinfo.tlock, flags);
if (signal_pending(current)) {
isp_prt(isp, ISP_LOGALL, "%s received signal (%d)", current->comm, signal_pending(current));
flush_signals(current);
if (isp->isp_osinfo.task_active) {
spin_unlock_irqrestore(&isp->isp_osinfo.tlock, flags);
continue;
}
}
if (isp->isp_osinfo.task_active == 0) {
spin_unlock_irqrestore(&isp->isp_osinfo.tlock, flags);
break;
}
nactions = isp->isp_osinfo.nt_actions;
isp->isp_osinfo.nt_actions = 0;
for (action = 0; action < nactions; action++) {
curactions[action] = isp->isp_osinfo.t_actions[action];
}
spin_unlock_irqrestore(&isp->isp_osinfo.tlock, flags);
for (action = 0; action < nactions; action++) {
isp_thread_action_t *tap = &curactions[action];
isp_prt(isp, ISP_LOGDEBUG1, "isp_task_thread[%d]: action %d (%p)", action, tap->thread_action, tap->thread_waiter);
switch (tap->thread_action) {
case ISP_THREAD_NIL:
break;
#ifdef ISP_FW_CRASH_DUMP
case ISP_THREAD_FW_CRASH_DUMP:
ISP_LOCKU_SOFTC(isp);
FCPARAM(isp, 0)->isp_fwstate = FW_CONFIG_WAIT;
FCPARAM(isp, 0)->isp_loopstate = LOOP_NIL;
isp_fw_dump(isp);
ISP_THREAD_EVENT(isp, ISP_THREAD_REINIT, NULL, 0, __FUNCTION__, __LINE__);
ISP_UNLKU_SOFTC(isp);
break;
#endif
case ISP_THREAD_REINIT:
ISP_LOCKU_SOFTC(isp);
if (isp->isp_dead) {
isp_prt(isp, ISP_LOGERR, "chip marked dead- not restarting");
isp_shutdown(isp);
ISP_DISABLE_INTS(isp);
ISP_UNLKU_SOFTC(isp);
break;
}
isp_reinit(isp);
if (isp->isp_state == ISP_RUNSTATE) {
for (i = 0; i < isp->isp_nchan; i++) {
ISP_DATA(isp, i)->blocked = 0;
}
isp_async(isp, ISPASYNC_FW_RESTARTED);
} else {
isp_prt(isp, ISP_LOGERR, "unable to restart chip");
}
ISP_UNLKU_SOFTC(isp);
break;
case ISP_THREAD_FC_RESCAN:
{
fcparam *fcp = tap->arg;
int chan = fcp - FCPARAM(isp, 0);
fcp = FCPARAM(isp, chan);
ISP_LOCKU_SOFTC(isp);
ISP_DATA(isp, chan)->fcrswdog = 0;
if (isp_fc_runstate(isp, chan, 250000) == 0) {
ISP_DATA(isp, chan)->deadloop = 0;
ISP_DATA(isp, chan)->downcount = 0;
ISP_DATA(isp, chan)->blocked = 0;
isplinux_runwaitq(isp);
} else {
if (ISP_DATA(isp, chan)->downcount == 0) {
ISP_DATA(isp, chan)->downcount = jiffies;
}
/*
* Try again in a little while.
*/
if ((jiffies - ISP_DATA(isp, chan)->downcount) > (isp_deadloop_time * HZ)) {
fcp->loop_seen_once = 0;
ISP_DATA(isp, chan)->deadloop = 1;
ISP_DATA(isp, chan)->downcount = 0;
ISP_DATA(isp, chan)->blocked = 0;
isp_prt(isp, ISP_LOGWARN, "Chan %d assuming loop is dead", chan);
isplinux_flushwaitq(isp);
} else {
ISP_DATA(isp, chan)->fcrswdog = 1;
}
}
ISP_UNLKU_SOFTC(isp);
break;
}
#ifdef ISP_TARGET_MODE
case ISP_THREAD_LOGOUT:
{
mbreg_t mbs;
union {
isp_pdb_t pdb;
int id;
} u;
fcportdb_t *lp = tap->arg;
ISP_LOCKU_SOFTC(isp);
if (lp->state != FC_PORTDB_STATE_VALID) {
isp_prt(isp, ISP_LOGALL, "target entry no longer valid");
ISP_UNLKU_SOFTC(isp);
break;
}
MEMZERO(&u, sizeof (u));
u.id = lp->handle;
isp_prt(isp, ISP_LOGALL, "Doing Port Logout repair for 0x%016llx@0x%x (loop id) %u",
lp->port_wwn, lp->portid, lp->handle);
MEMZERO(&mbs, sizeof (mbs));
mbs.param[0] = MBOX_FABRIC_LOGOUT;
if (ISP_CAP_2KLOGIN(isp)) {
mbs.param[1] = lp->handle;
mbs.obits |= (1 << 10);
} else {
mbs.param[1] = lp->handle << 8;
}
mbs.logval = MBLOGNONE;
(void) isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGERR, "failed to get logout loop id %u", lp->handle);
lp->state = FC_PORTDB_STATE_PROBATIONAL;
ISP_UNLKU_SOFTC(isp);
break;
}
MEMZERO(&mbs, sizeof (mbs));
mbs.param[0] = MBOX_FABRIC_LOGIN;
if (ISP_CAP_2KLOGIN(isp)) {
mbs.param[1] = lp->handle;
mbs.obits |= (1 << 10);
} else {
mbs.param[1] = lp->handle << 8;
}
mbs.param[2] = lp->portid >> 16;
mbs.param[3] = lp->portid & 0xffff;
(void) isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
isp_prt(isp, ISP_LOGERR, "failed to get login port id %x at loop id %u", lp->portid, lp->handle);
lp->state = FC_PORTDB_STATE_PROBATIONAL;
ISP_UNLKU_SOFTC(isp);
break;
}
lp->state = FC_PORTDB_STATE_VALID;
ISP_UNLKU_SOFTC(isp);
break;
}
case ISP_THREAD_FINDIID:
{
tmd_cmd_t *tmd = tap->arg;
fcportdb_t *lp = NULL;
uint64_t iid = INI_NONE;
uint16_t nphdl = NIL_HANDLE;
if (tmd->cd_lflags & CDFL_ABORTED) {
isp_prt(isp, ISP_LOGTINFO, "[%llx] asking thread to terminate because it was marked aborted", (unsigned long long) tmd->cd_tagval);
ISP_THREAD_EVENT(isp, ISP_THREAD_TERMINATE, tmd, 0, __FUNCTION__, __LINE__);
break;
}
ISP_LOCKU_SOFTC(isp);
if (isp_find_pdb_by_sid(isp, tmd->cd_channel, tmd->cd_portid, &lp)) {
if (!VALID_INI(lp->port_wwn)) {
if (lp->handle == NIL_HANDLE) {
/*
* Ooops- all we have is the port id.
*/
uint16_t nphdl, max;
isp_pdb_t pdb;
if (IS_24XX(isp)) {
max = NPH_MAX_2K;
} else {
max = NPH_MAX;
}
for (nphdl = 0; nphdl != max; nphdl++) {
if (isp_control(isp, ISPCTL_GET_PDB, tmd->cd_channel, nphdl, &pdb)) {
continue;
}
isp_prt(isp, ISP_LOGTINFO, "%s: nphdl 0x%04x has portid 0x%06x", __FUNCTION__, nphdl, pdb.portid);
if (pdb.portid == tmd->cd_portid) {
lp->handle = nphdl;
break;
}
}
if (nphdl == max) {
ISP_UNLKU_SOFTC(isp);
isp_prt(isp, ISP_LOGTINFO, "[0x%llx] asking thread to terminate cmd [0x%02x] because because we can't find the N-Port handle", tmd->cd_tagval, tmd->cd_cdb[0] & 0xff);
isp_tgt_dump_pdb(isp, tmd->cd_channel);
ISP_THREAD_EVENT(isp, ISP_THREAD_TERMINATE, tmd, 0, __FUNCTION__, __LINE__);
break;
}
}
if (isp_control(isp, ISPCTL_GET_NAMES, tmd->cd_channel, lp->handle, NULL, &lp->port_wwn) == 0) {
nphdl = lp->handle;
iid = lp->port_wwn;
} else {
isp_prt(isp, ISP_LOGALL, "%s: Chan %d [0x%llx] failed to get name for handle 0x%02x for portid 0x%06x", __FUNCTION__, tmd->cd_channel, tmd->cd_tagval, lp->handle, tmd->cd_portid);
}
} else {
nphdl = lp->handle;
iid = lp->port_wwn;
}
} else {
/*
* If it's no longer in the port database, then some event between the receipt of the command and now
* has cleared it out. The command is probably already dead due to initiator port logout.
*/
ISP_UNLKU_SOFTC(isp);
isp_prt(isp, ISP_LOGTINFO, "[0x%llx] asking thread to terminate cmd [0x%02x] because PortID 0x%06x no longer in port database", tmd->cd_tagval, tmd->cd_cdb[0] & 0xff, tmd->cd_portid);
isp_tgt_dump_pdb(isp, tmd->cd_channel);
ISP_THREAD_EVENT(isp, ISP_THREAD_TERMINATE, tmd, 0, __FUNCTION__, __LINE__);
break;
}
if (iid == INI_NONE) {
isp_prt(isp, ISP_LOGTDEBUG0, "%s: [0x%llx] trying to find IID again...", __FUNCTION__, tmd->cd_tagval);
tmd->cd_next = isp->isp_osinfo.waiting_t;
isp->isp_osinfo.waiting_t = tmd;
tmd->cd_lastoff = 0;
ISP_UNLKU_SOFTC(isp);
break;
}
tmd->cd_tgt = FCPARAM(isp, tmd->cd_channel)->isp_wwpn;
tmd->cd_nphdl = nphdl;
tmd->cd_iid = iid;
isp_prt(isp, ISP_LOGTINFO, "%s: [0x%llx] Chan %d found initiator @ IID 0x%016llx N-Port Handle 0x%02x Port ID 0x%06x", __FUNCTION__,
tmd->cd_tagval, tmd->cd_channel, (unsigned long long)tmd->cd_iid, tmd->cd_nphdl, tmd->cd_portid);
CALL_PARENT_TMD(isp, tmd, QOUT_TMD_START);
ISP_UNLKU_SOFTC(isp);
isp_tgt_tq(isp);
break;
}
case ISP_THREAD_FINDPORTID:
{
tmd_cmd_t *tmd = tap->arg;
fcportdb_t *lp;
ISP_LOCKU_SOFTC(isp);
if (isp_find_pdb_by_loopid(isp, tmd->cd_channel, tmd->cd_nphdl, &lp)) {
if (lp->portid == PORT_NONE) {
isp_pdb_t pdb;
if (isp_control(isp, ISPCTL_GET_PDB, tmd->cd_channel, tmd->cd_nphdl, &pdb) == 0) {
tmd->cd_portid = lp->portid = pdb.portid;
}
} else {
tmd->cd_portid = lp->portid;
}
} else {
isp_prt(isp, ISP_LOGTINFO, "[0x%llx] not in port database at all any more", tmd->cd_tagval);
}
if (tmd->cd_portid != PORT_NONE) {
isp_prt(isp, ISP_LOGTINFO, "%s: [0x%llx] Chan %d found initiator @ IID 0x%016llx N-Port Handle 0x%02x Port ID 0x%06x", __FUNCTION__,
tmd->cd_tagval, tmd->cd_channel, (unsigned long long)tmd->cd_iid, tmd->cd_nphdl, tmd->cd_portid);
}
CALL_PARENT_TMD(isp, tmd, QOUT_TMD_START);
ISP_UNLKU_SOFTC(isp);
isp_tgt_tq(isp);
break;
}
case ISP_THREAD_TERMINATE:
{
fcportdb_t *lp;
tmd_cmd_t *tmd = tap->arg;
ISP_LOCKU_SOFTC(isp);
if (isp_find_pdb_by_sid(isp, tmd->cd_channel, tmd->cd_portid, &lp)) {
tmd->cd_iid = lp->port_wwn;
tmd->cd_nphdl = lp->handle;
CALL_PARENT_TMD(isp, tmd, QOUT_TMD_START);
ISP_UNLKU_SOFTC(isp);
isp_tgt_tq(isp);
isp_prt(isp, ISP_LOGINFO, "Chan %d [%llx] reprieved", (int) AT2_GET_BUS(tmd->cd_tagval), (unsigned long long) tmd->cd_tagval);
break;
}
isp_prt(isp, ISP_LOGTINFO, "%s now terminating [%llx] from 0x%06x", __FUNCTION__, (unsigned long long) tmd->cd_tagval, tmd->cd_portid);
if (isp_terminate_cmd(isp, tmd)) {
ISP_UNLKU_SOFTC(isp);
ISP_THREAD_EVENT(isp, ISP_THREAD_TERMINATE, tmd, 0, __FUNCTION__, __LINE__);
break;
}
tmd->cd_next = NULL;
if (isp->isp_osinfo.tfreelist) {
isp->isp_osinfo.bfreelist->cd_next = tmd;
} else {
isp->isp_osinfo.tfreelist = tmd;
}
isp->isp_osinfo.bfreelist = tmd;
ISP_UNLKU_SOFTC(isp);
break;
}
case ISP_THREAD_RESTART_AT7:
{
at7_entry_t at;
tmd_cmd_t *tmd = tap->arg;
MEMCPY(&at, tmd, sizeof (at7_entry_t));
ISP_LOCKU_SOFTC(isp);
MEMZERO(tmd, sizeof (tmd_cmd_t));
if (isp->isp_osinfo.tfreelist) {
isp->isp_osinfo.bfreelist->cd_next = tmd;
} else {
isp->isp_osinfo.tfreelist = tmd;
}
isp->isp_osinfo.bfreelist = tmd; /* remember to move the list tail pointer */
isp_handle_platform_atio7(isp, &at);
ISP_UNLKU_SOFTC(isp);
break;
}
case ISP_THREAD_FC_PUTBACK:
{
tmd_cmd_t *tmd = tap->arg;
ISP_LOCKU_SOFTC(isp);
isp_prt(isp, ISP_LOGTINFO, "%s: [%llx] calling putback", __FUNCTION__, tmd->cd_tagval);
if (isp_target_putback_atio(isp, tmd)) {
ISP_UNLKU_SOFTC(isp);
ISP_THREAD_EVENT(isp, ISP_THREAD_FC_PUTBACK, tmd, 0, __FUNCTION__, __LINE__);
break;
}
if (tmd->cd_lflags & CDFL_NEED_CLNUP) {
tmd->cd_lflags ^= CDFL_NEED_CLNUP;
isp_prt(isp, ISP_LOGTINFO, "Terminating %llx too", (unsigned long long) tmd->cd_tagval);
(void) isp_terminate_cmd(isp, tmd);
}
MEMZERO(tmd, sizeof (tmd_cmd_t));
if (isp->isp_osinfo.tfreelist) {
isp->isp_osinfo.bfreelist->cd_next = tmd;
} else {
isp->isp_osinfo.tfreelist = tmd;
}
isp->isp_osinfo.bfreelist = tmd; /* remember to move the list tail pointer */
isp_prt(isp, ISP_LOGTDEBUG0, "DONE freeing tmd %p [%llx] after retry", tmd, tmd->cd_tagval);
ISP_UNLKU_SOFTC(isp);
break;
}
#endif
default:
break;
}
if (tap->thread_waiter) {
isp_prt(isp, ISP_LOGDEBUG1, "isp_task_thread signalling %p", tap->thread_waiter);
up(tap->thread_waiter);
}
}
}
isp_prt(isp, ISP_LOGDEBUG1, "isp_task_thread exiting");
ISP_THREAD_XACK(isp);
return (0);
}
void
isp_prt(ispsoftc_t *isp, int level, const char *fmt, ...)
{
char buf[256];
char *prefl;
va_list ap;
if (level != ISP_LOGALL && (level & isp->isp_dblev) == 0) {
return;
}
if (level & ISP_LOGERR) {
prefl = KERN_ERR "%s: ";
} else if (level & ISP_LOGWARN) {
prefl = KERN_WARNING "%s: ";
} else if (level & ISP_LOGINFO) {
prefl = KERN_INFO "%s: ";
} else if (level & ISP_LOGCONFIG) {
prefl = KERN_INFO "%s: ";
} else {
prefl = "%s: ";
}
printk(prefl, isp->isp_name);
va_start(ap, fmt);
vsnprintf(buf, sizeof (buf), fmt, ap);
va_end(ap);
printk("%s\n", buf);
}
#ifndef ISP_LICENSE
#define ISP_LICENSE "GPL"
#endif
#ifdef MODULE
#ifdef MODULE_LICENSE
MODULE_LICENSE( ISP_LICENSE );
#endif
module_param(isp_debug, int, 0);
module_param(isp_disable, int, 0);
module_param(isp_nonvram, int, 0);
module_param(isp_nofwreload, int, 0);
module_param(isp_maxluns, int, 0);
module_param(isp_throttle, int, 0);
module_param(isp_cmd_per_lun, int, 0);
module_param(isp_maxsectors, int, 0);
module_param(isp_fcduplex, int, 0);
module_param(isp_nport_only, int, 0);
module_param(isp_loop_only, int, 0);
module_param(isp_deadloop_time, int, 0);
module_param(isp_fc_id, int, 0);
module_param(isp_spi_id, int, 0);
module_param(isp_own_id, int, 0);
module_param(isp_default_frame_size, int, 0);
module_param(isp_default_exec_throttle, int, 0);
module_param(isp_roles, charp, 0);
module_param(isp_wwpns, charp, 0);
module_param(isp_wwnns, charp, 0);
module_param(isp_vports, int, 0);
#else
static int __init isp_roleinit(char *str)
{
isp_roles = str;
return 0;
}
__setup("isp_roles=", isp_roleinit);
#endif
static struct scsi_host_template driver_template = {
.name = ISP_NAME,
.module = THIS_MODULE,
.info = isplinux_info,
.queuecommand = isplinux_queuecommand,
.eh_abort_handler = isplinux_abort,
.eh_device_reset_handler = isplinux_bdr,
.eh_bus_reset_handler = isplinux_sreset,
.eh_host_reset_handler = isplinux_hreset,
.slave_configure = isplinux_slave_configure,
.bios_param = isplinux_biosparam,
#if defined(CONFIG_PROC_FS)
.proc_info = isplinux_proc_info,
.proc_name = ISP_NAME,
#endif
.can_queue = 1,
.sg_tablesize = SG_ALL,
.use_clustering = ENABLE_CLUSTERING
};
struct scsi_host_template *isp_template = &driver_template;
/*
* vim:ts=4:sw=4:expandtab
*/