2005-01-06 01:43:34 +00:00
|
|
|
/*-
|
2009-08-01 01:04:26 +00:00
|
|
|
* Copyright (c) 1997-2009 by Matthew Jacob
|
2006-01-23 06:23:37 +00:00
|
|
|
* All rights reserved.
|
1998-04-22 17:54:58 +00:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice immediately at the beginning of the file, without modification,
|
|
|
|
* this list of conditions, and the following disclaimer.
|
2000-09-21 20:16:04 +00:00
|
|
|
* 2. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
1998-04-22 17:54:58 +00:00
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
|
|
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
2003-08-24 17:55:58 +00:00
|
|
|
|
2006-07-16 20:11:50 +00:00
|
|
|
/*
|
|
|
|
* Platform (FreeBSD) dependent common attachment code for Qlogic adapters.
|
|
|
|
*/
|
2003-08-24 17:55:58 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
1998-04-22 17:54:58 +00:00
|
|
|
#include <dev/isp/isp_freebsd.h>
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
#include <sys/unistd.h>
|
|
|
|
#include <sys/kthread.h>
|
|
|
|
#include <sys/conf.h>
|
2002-07-25 16:00:24 +00:00
|
|
|
#include <sys/module.h>
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
#include <sys/ioccom.h>
|
|
|
|
#include <dev/isp/isp_ioctl.h>
|
2007-03-12 04:54:30 +00:00
|
|
|
#include <sys/devicestat.h>
|
2006-11-14 08:45:48 +00:00
|
|
|
#include <cam/cam_periph.h>
|
2007-01-20 04:00:21 +00:00
|
|
|
#include <cam/cam_xpt_periph.h>
|
1998-04-22 17:54:58 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
#if __FreeBSD_version < 800002
|
|
|
|
#define THREAD_CREATE kthread_create
|
|
|
|
#else
|
|
|
|
#define THREAD_CREATE kproc_create
|
2006-12-18 23:50:30 +00:00
|
|
|
#endif
|
|
|
|
|
2002-07-25 16:00:24 +00:00
|
|
|
MODULE_VERSION(isp, 1);
|
2003-09-15 06:41:33 +00:00
|
|
|
MODULE_DEPEND(isp, cam, 1, 1, 1);
|
2002-07-11 03:25:04 +00:00
|
|
|
int isp_announced = 0;
|
2009-08-01 01:04:26 +00:00
|
|
|
int isp_fabric_hysteresis = 3;
|
|
|
|
int isp_loop_down_limit = 60; /* default loop down limit */
|
2006-11-14 08:45:48 +00:00
|
|
|
int isp_change_is_bad = 0; /* "changed" devices are bad */
|
2009-08-01 01:04:26 +00:00
|
|
|
int isp_quickboot_time = 7; /* don't wait more than N secs for loop up */
|
2006-11-14 08:45:48 +00:00
|
|
|
int isp_gone_device_time = 30; /* grace time before reporting device lost */
|
2009-08-01 01:04:26 +00:00
|
|
|
int isp_autoconfig = 1; /* automatically attach/detach devices */
|
2006-11-14 08:45:48 +00:00
|
|
|
static const char *roles[4] = {
|
|
|
|
"(none)", "Target", "Initiator", "Target/Initiator"
|
|
|
|
};
|
2009-08-01 01:04:26 +00:00
|
|
|
static const char prom3[] = "Chan %d PortID 0x%06x Departed from Target %u because of %s";
|
|
|
|
static const char rqo[] = "%s: Request Queue Overflow\n";
|
2002-07-11 03:25:04 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void isp_freeze_loopdown(ispsoftc_t *, int, char *);
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
static d_ioctl_t ispioctl;
|
2000-07-04 01:05:43 +00:00
|
|
|
static void isp_intr_enable(void *);
|
2006-02-15 00:31:48 +00:00
|
|
|
static void isp_cam_async(void *, uint32_t, struct cam_path *, void *);
|
1999-08-16 20:11:45 +00:00
|
|
|
static void isp_poll(struct cam_sim *);
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
static timeout_t isp_watchdog;
|
2006-11-14 08:45:48 +00:00
|
|
|
static timeout_t isp_ldt;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
static void isp_kthread(void *);
|
2000-01-03 23:55:30 +00:00
|
|
|
static void isp_action(struct cam_sim *, union ccb *);
|
2009-08-01 01:04:26 +00:00
|
|
|
#ifdef ISP_INTERNAL_TARGET
|
|
|
|
static void isp_target_thread_pi(void *);
|
|
|
|
static void isp_target_thread_fc(void *);
|
2006-09-01 04:57:14 +00:00
|
|
|
#endif
|
2009-08-01 01:04:26 +00:00
|
|
|
static void isp_timer(void *);
|
2000-05-09 01:08:21 +00:00
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
static struct cdevsw isp_cdevsw = {
|
2004-02-21 21:10:55 +00:00
|
|
|
.d_version = D_VERSION,
|
2003-03-03 12:15:54 +00:00
|
|
|
.d_ioctl = ispioctl,
|
|
|
|
.d_name = "isp",
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
};
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static int
|
|
|
|
isp_attach_chan(ispsoftc_t *isp, struct cam_devq *devq, int chan)
|
1998-09-15 08:42:56 +00:00
|
|
|
{
|
|
|
|
struct ccb_setasync csa;
|
1999-05-11 05:10:06 +00:00
|
|
|
struct cam_sim *sim;
|
|
|
|
struct cam_path *path;
|
|
|
|
|
1998-09-15 08:42:56 +00:00
|
|
|
/*
|
1999-05-11 05:10:06 +00:00
|
|
|
* Construct our SIM entry.
|
1998-09-15 08:42:56 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp, device_get_unit(isp->isp_dev), &isp->isp_osinfo.lock, isp->isp_maxcmds, isp->isp_maxcmds, devq);
|
|
|
|
|
1999-05-11 05:10:06 +00:00
|
|
|
if (sim == NULL) {
|
2009-08-01 01:04:26 +00:00
|
|
|
return (ENOMEM);
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
2000-07-04 01:05:43 +00:00
|
|
|
|
2007-05-13 17:45:00 +00:00
|
|
|
ISP_LOCK(isp);
|
2009-08-01 01:04:26 +00:00
|
|
|
if (xpt_bus_register(sim, isp->isp_dev, chan) != CAM_SUCCESS) {
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
cam_sim_free(sim, FALSE);
|
|
|
|
return (EIO);
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_UNLOCK(isp);
|
1998-09-15 08:42:56 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (xpt_create_path(&path, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
|
|
|
|
ISP_LOCK(isp);
|
1999-05-11 05:10:06 +00:00
|
|
|
xpt_bus_deregister(cam_sim_path(sim));
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
cam_sim_free(sim, FALSE);
|
|
|
|
return (ENXIO);
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
|
|
|
|
1999-05-11 05:10:06 +00:00
|
|
|
xpt_setup_ccb(&csa.ccb_h, path, 5);
|
1998-09-15 08:42:56 +00:00
|
|
|
csa.ccb_h.func_code = XPT_SASYNC_CB;
|
|
|
|
csa.event_enable = AC_LOST_DEVICE;
|
1999-01-30 07:29:00 +00:00
|
|
|
csa.callback = isp_cam_async;
|
1999-05-11 05:10:06 +00:00
|
|
|
csa.callback_arg = sim;
|
1998-09-15 08:42:56 +00:00
|
|
|
xpt_action((union ccb *)&csa);
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_SCSI(isp)) {
|
|
|
|
struct isp_spi *spi = ISP_SPI_PC(isp, chan);
|
|
|
|
spi->sim = sim;
|
|
|
|
spi->path = path;
|
|
|
|
#ifdef ISP_INTERNAL_TARGET
|
|
|
|
ISP_SET_PC(isp, chan, proc_active, 1);
|
|
|
|
if (THREAD_CREATE(isp_target_thread_pi, spi, &spi->target_proc, 0, 0, "%s: isp_test_tgt%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) {
|
|
|
|
ISP_SET_PC(isp, chan, proc_active, 0);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "cannot create test target thread");
|
1999-05-11 05:10:06 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
struct isp_fc *fc = ISP_FC_PC(isp, chan);
|
1999-05-11 05:10:06 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
fc->sim = sim;
|
|
|
|
fc->path = path;
|
|
|
|
fc->isp = isp;
|
|
|
|
|
|
|
|
callout_init_mtx(&fc->ldt, &isp->isp_osinfo.lock, 0);
|
|
|
|
callout_init_mtx(&fc->gdt, &isp->isp_osinfo.lock, 0);
|
|
|
|
|
|
|
|
if (THREAD_CREATE(isp_kthread, fc, &fc->kproc, 0, 0, "%s: fc_thrd%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) {
|
|
|
|
xpt_free_path(fc->path);
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_bus_deregister(cam_sim_path(fc->sim));
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
cam_sim_free(fc->sim, FALSE);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We start by being "loop down" if we have an initiator role
|
|
|
|
*/
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
if ((FCPARAM(isp, chan)->role & ISP_ROLE_INITIATOR) && fc->ldt_running == 0) {
|
|
|
|
isp_freeze_loopdown(isp, chan, "isp_attach");
|
|
|
|
fc->ldt_running = 1;
|
|
|
|
callout_reset(&fc->ldt, isp_quickboot_time * hz, isp_ldt, fc);
|
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Starting Initial Loop Down Timer @ %lu", (unsigned long) time_uptime);
|
1999-05-11 05:10:06 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
#ifdef ISP_INTERNAL_TARGET
|
|
|
|
ISP_SET_PC(isp, chan, proc_active, 1);
|
|
|
|
if (THREAD_CREATE(isp_target_thread_fc, fc, &fc->target_proc, 0, 0, "%s: isp_test_tgt%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) {
|
|
|
|
ISP_SET_PC(isp, chan, proc_active, 0);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "cannot create test target thread");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
isp_attach(ispsoftc_t *isp)
|
|
|
|
{
|
|
|
|
const char *nu = device_get_nameunit(isp->isp_osinfo.dev);
|
|
|
|
int du = device_get_unit(isp->isp_dev);
|
|
|
|
int chan;
|
1999-05-11 05:10:06 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.ehook.ich_func = isp_intr_enable;
|
|
|
|
isp->isp_osinfo.ehook.ich_arg = isp;
|
|
|
|
if (config_intrhook_establish(&isp->isp_osinfo.ehook) != 0) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "could not establish interrupt enable hook");
|
|
|
|
return (-EIO);
|
1999-05-11 05:10:06 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.ehook_active = 1;
|
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
|
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* Create the device queue for our SIM(s).
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.devq = cam_simq_alloc(isp->isp_maxcmds);
|
|
|
|
if (isp->isp_osinfo.devq == NULL) {
|
|
|
|
config_intrhook_disestablish(&isp->isp_osinfo.ehook);
|
|
|
|
return (EIO);
|
2001-02-11 03:47:39 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
for (chan = 0; chan < isp->isp_nchan; chan++) {
|
|
|
|
if (isp_attach_chan(isp, isp->isp_osinfo.devq, chan)) {
|
|
|
|
goto unwind;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
}
|
2007-05-05 20:17:23 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
callout_init_mtx(&isp->isp_osinfo.tmo, &isp->isp_osinfo.lock, 0);
|
|
|
|
callout_reset(&isp->isp_osinfo.tmo, hz, isp_timer, isp);
|
|
|
|
isp->isp_osinfo.timer_active = 1;
|
|
|
|
|
|
|
|
isp->isp_osinfo.cdev = make_dev(&isp_cdevsw, du, UID_ROOT, GID_OPERATOR, 0600, "%s", nu);
|
|
|
|
if (isp->isp_osinfo.cdev) {
|
|
|
|
isp->isp_osinfo.cdev->si_drv1 = isp;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
unwind:
|
|
|
|
while (--chan >= 0) {
|
|
|
|
struct cam_sim *sim;
|
|
|
|
struct cam_path *path;
|
|
|
|
if (IS_FC(isp)) {
|
|
|
|
sim = ISP_FC_PC(isp, chan)->sim;
|
|
|
|
path = ISP_FC_PC(isp, chan)->path;
|
|
|
|
} else {
|
|
|
|
sim = ISP_SPI_PC(isp, chan)->sim;
|
|
|
|
path = ISP_SPI_PC(isp, chan)->path;
|
2007-05-05 20:17:23 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_free_path(path);
|
2007-05-05 20:17:23 +00:00
|
|
|
ISP_LOCK(isp);
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_bus_deregister(cam_sim_path(sim));
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
cam_sim_free(sim, FALSE);
|
|
|
|
}
|
|
|
|
if (isp->isp_osinfo.ehook_active) {
|
|
|
|
config_intrhook_disestablish(&isp->isp_osinfo.ehook);
|
|
|
|
isp->isp_osinfo.ehook_active = 0;
|
|
|
|
}
|
|
|
|
if (isp->isp_osinfo.cdev) {
|
|
|
|
destroy_dev(isp->isp_osinfo.cdev);
|
|
|
|
isp->isp_osinfo.cdev = NULL;
|
|
|
|
}
|
|
|
|
cam_simq_free(isp->isp_osinfo.devq);
|
|
|
|
isp->isp_osinfo.devq = NULL;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isp_detach(ispsoftc_t *isp)
|
|
|
|
{
|
|
|
|
int chan;
|
|
|
|
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
if (isp->isp_osinfo.timer_active) {
|
|
|
|
callout_stop(&isp->isp_osinfo.tmo);
|
|
|
|
isp->isp_osinfo.timer_active = 0;
|
|
|
|
}
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
for (chan = isp->isp_nchan - 1; chan >= 0; chan -= 1) {
|
|
|
|
struct cam_sim *sim;
|
|
|
|
struct cam_path *path;
|
|
|
|
if (IS_FC(isp)) {
|
|
|
|
sim = ISP_FC_PC(isp, chan)->sim;
|
|
|
|
path = ISP_FC_PC(isp, chan)->path;
|
|
|
|
} else {
|
|
|
|
sim = ISP_SPI_PC(isp, chan)->sim;
|
|
|
|
path = ISP_SPI_PC(isp, chan)->path;
|
2007-05-05 20:17:23 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_free_path(path);
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_bus_deregister(cam_sim_path(sim));
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
cam_sim_free(sim, FALSE);
|
|
|
|
}
|
|
|
|
if (isp->isp_osinfo.cdev) {
|
|
|
|
destroy_dev(isp->isp_osinfo.cdev);
|
|
|
|
isp->isp_osinfo.cdev = NULL;
|
|
|
|
}
|
|
|
|
if (isp->isp_osinfo.ehook_active) {
|
|
|
|
config_intrhook_disestablish(&isp->isp_osinfo.ehook);
|
|
|
|
isp->isp_osinfo.ehook_active = 0;
|
|
|
|
}
|
|
|
|
if (isp->isp_osinfo.devq == NULL) {
|
|
|
|
cam_simq_free(isp->isp_osinfo.devq);
|
|
|
|
isp->isp_osinfo.devq = NULL;
|
2007-05-05 20:17:23 +00:00
|
|
|
}
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
|
|
|
|
2006-11-14 08:45:48 +00:00
|
|
|
static void
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_freeze_loopdown(ispsoftc_t *isp, int chan, char *msg)
|
2002-07-08 17:42:47 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_FC(isp)) {
|
|
|
|
struct isp_fc *fc = ISP_FC_PC(isp, chan);
|
|
|
|
if (fc->simqfrozen == 0) {
|
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "%s: freeze simq (loopdown) chan %d", msg, chan);
|
|
|
|
fc->simqfrozen = SIMQFRZ_LOOPDOWN;
|
|
|
|
xpt_freeze_simq(fc->sim, 1);
|
|
|
|
} else {
|
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "%s: mark frozen (loopdown) chan %d", msg, chan);
|
|
|
|
fc->simqfrozen |= SIMQFRZ_LOOPDOWN;
|
|
|
|
}
|
2002-07-08 17:42:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-21 18:30:01 +00:00
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
static int
|
2009-08-01 01:04:26 +00:00
|
|
|
ispioctl(struct cdev *dev, u_long c, caddr_t addr, int flags, struct thread *td)
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
{
|
2006-04-21 18:30:01 +00:00
|
|
|
ispsoftc_t *isp;
|
2009-08-01 01:04:26 +00:00
|
|
|
int nr, chan, retval = ENOTTY;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
isp = dev->si_drv1;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
|
2006-03-03 07:04:43 +00:00
|
|
|
switch (c) {
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
case ISP_SDBLEV:
|
|
|
|
{
|
|
|
|
int olddblev = isp->isp_dblev;
|
|
|
|
isp->isp_dblev = *(int *)addr;
|
|
|
|
*(int *)addr = olddblev;
|
|
|
|
retval = 0;
|
|
|
|
break;
|
|
|
|
}
|
2004-02-07 03:47:33 +00:00
|
|
|
case ISP_GETROLE:
|
2009-08-01 01:04:26 +00:00
|
|
|
chan = *(int *)addr;
|
|
|
|
if (chan < 0 || chan >= isp->isp_nchan) {
|
|
|
|
retval = -ENXIO;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (IS_FC(isp)) {
|
|
|
|
*(int *)addr = FCPARAM(isp, chan)->role;
|
|
|
|
} else {
|
|
|
|
*(int *)addr = SDPARAM(isp, chan)->role;
|
|
|
|
}
|
2004-02-07 03:47:33 +00:00
|
|
|
retval = 0;
|
|
|
|
break;
|
|
|
|
case ISP_SETROLE:
|
|
|
|
nr = *(int *)addr;
|
2009-08-01 01:04:26 +00:00
|
|
|
chan = nr >> 8;
|
|
|
|
if (chan < 0 || chan >= isp->isp_nchan) {
|
|
|
|
retval = -ENXIO;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
nr &= 0xff;
|
2004-02-07 03:47:33 +00:00
|
|
|
if (nr & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) {
|
|
|
|
retval = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_FC(isp)) {
|
2009-09-15 02:25:03 +00:00
|
|
|
/*
|
|
|
|
* We don't really support dual role at present on FC cards.
|
|
|
|
*
|
|
|
|
* We should, but a bunch of things are currently broken,
|
|
|
|
* so don't allow it.
|
|
|
|
*/
|
|
|
|
if (nr == ISP_ROLE_BOTH) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "cannot support dual role at present");
|
|
|
|
retval = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
*(int *)addr = FCPARAM(isp, chan)->role;
|
|
|
|
#ifdef ISP_INTERNAL_TARGET
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
retval = isp_fc_change_role(isp, chan, nr);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
#else
|
|
|
|
FCPARAM(isp, chan)->role = nr;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
*(int *)addr = SDPARAM(isp, chan)->role;
|
|
|
|
SDPARAM(isp, chan)->role = nr;
|
|
|
|
}
|
|
|
|
retval = 0;
|
|
|
|
break;
|
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
case ISP_RESETHBA:
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_LOCK(isp);
|
2009-08-01 01:04:26 +00:00
|
|
|
#ifdef ISP_TARGET_MODE
|
|
|
|
isp_del_all_wwn_entries(isp, ISP_NOCHAN);
|
|
|
|
#endif
|
|
|
|
isp_reinit(isp, 0);
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_UNLOCK(isp);
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
retval = 0;
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
|
2002-02-21 23:30:05 +00:00
|
|
|
case ISP_RESCAN:
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
if (IS_FC(isp)) {
|
2009-08-01 01:04:26 +00:00
|
|
|
chan = *(int *)addr;
|
|
|
|
if (chan < 0 || chan >= isp->isp_nchan) {
|
|
|
|
retval = -ENXIO;
|
|
|
|
break;
|
|
|
|
}
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_LOCK(isp);
|
2009-08-01 01:04:26 +00:00
|
|
|
if (isp_fc_runstate(isp, chan, 5 * 1000000)) {
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
retval = EIO;
|
|
|
|
} else {
|
|
|
|
retval = 0;
|
|
|
|
}
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_UNLOCK(isp);
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
case ISP_FC_LIP:
|
|
|
|
if (IS_FC(isp)) {
|
2009-08-01 01:04:26 +00:00
|
|
|
chan = *(int *)addr;
|
|
|
|
if (chan < 0 || chan >= isp->isp_nchan) {
|
|
|
|
retval = -ENXIO;
|
|
|
|
break;
|
|
|
|
}
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_LOCK(isp);
|
2009-08-01 01:04:26 +00:00
|
|
|
if (isp_control(isp, ISPCTL_SEND_LIP, chan)) {
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
retval = EIO;
|
|
|
|
} else {
|
|
|
|
retval = 0;
|
|
|
|
}
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_UNLOCK(isp);
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ISP_FC_GETDINFO:
|
|
|
|
{
|
|
|
|
struct isp_fc_device *ifc = (struct isp_fc_device *) addr;
|
2006-11-02 03:21:32 +00:00
|
|
|
fcportdb_t *lp;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
|
2006-07-10 22:39:32 +00:00
|
|
|
if (IS_SCSI(isp)) {
|
|
|
|
break;
|
|
|
|
}
|
2007-07-10 07:55:59 +00:00
|
|
|
if (ifc->loopid >= MAX_FC_TARG) {
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
retval = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
lp = &FCPARAM(isp, ifc->chan)->portdb[ifc->loopid];
|
|
|
|
if (lp->state == FC_PORTDB_STATE_VALID || lp->target_mode) {
|
2006-02-26 22:40:56 +00:00
|
|
|
ifc->role = lp->roles;
|
2006-11-02 03:21:32 +00:00
|
|
|
ifc->loopid = lp->handle;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
ifc->portid = lp->portid;
|
|
|
|
ifc->node_wwn = lp->node_wwn;
|
|
|
|
ifc->port_wwn = lp->port_wwn;
|
|
|
|
retval = 0;
|
|
|
|
} else {
|
|
|
|
retval = ENODEV;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2002-01-03 20:43:22 +00:00
|
|
|
case ISP_GET_STATS:
|
|
|
|
{
|
|
|
|
isp_stats_t *sp = (isp_stats_t *) addr;
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_MEMZERO(sp, sizeof (*sp));
|
2002-01-03 20:43:22 +00:00
|
|
|
sp->isp_stat_version = ISP_STATS_VERSION;
|
|
|
|
sp->isp_type = isp->isp_type;
|
|
|
|
sp->isp_revision = isp->isp_revision;
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_LOCK(isp);
|
2002-01-03 20:43:22 +00:00
|
|
|
sp->isp_stats[ISP_INTCNT] = isp->isp_intcnt;
|
|
|
|
sp->isp_stats[ISP_INTBOGUS] = isp->isp_intbogus;
|
|
|
|
sp->isp_stats[ISP_INTMBOXC] = isp->isp_intmboxc;
|
|
|
|
sp->isp_stats[ISP_INGOASYNC] = isp->isp_intoasync;
|
|
|
|
sp->isp_stats[ISP_RSLTCCMPLT] = isp->isp_rsltccmplt;
|
|
|
|
sp->isp_stats[ISP_FPHCCMCPLT] = isp->isp_fphccmplt;
|
|
|
|
sp->isp_stats[ISP_RSCCHIWAT] = isp->isp_rscchiwater;
|
|
|
|
sp->isp_stats[ISP_FPCCHIWAT] = isp->isp_fpcchiwater;
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_UNLOCK(isp);
|
2002-01-03 20:43:22 +00:00
|
|
|
retval = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ISP_CLR_STATS:
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_LOCK(isp);
|
2002-01-03 20:43:22 +00:00
|
|
|
isp->isp_intcnt = 0;
|
|
|
|
isp->isp_intbogus = 0;
|
|
|
|
isp->isp_intmboxc = 0;
|
|
|
|
isp->isp_intoasync = 0;
|
|
|
|
isp->isp_rsltccmplt = 0;
|
|
|
|
isp->isp_fphccmplt = 0;
|
|
|
|
isp->isp_rscchiwater = 0;
|
|
|
|
isp->isp_fpcchiwater = 0;
|
2007-06-11 19:15:49 +00:00
|
|
|
ISP_UNLOCK(isp);
|
2002-01-03 20:43:22 +00:00
|
|
|
retval = 0;
|
|
|
|
break;
|
2002-06-16 05:08:02 +00:00
|
|
|
case ISP_FC_GETHINFO:
|
|
|
|
{
|
|
|
|
struct isp_hba_device *hba = (struct isp_hba_device *) addr;
|
2009-08-01 01:04:26 +00:00
|
|
|
int chan = hba->fc_channel;
|
2006-07-10 22:39:32 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (chan < 0 || chan >= isp->isp_nchan) {
|
|
|
|
retval = ENXIO;
|
|
|
|
break;
|
|
|
|
}
|
2004-01-23 23:22:11 +00:00
|
|
|
hba->fc_fw_major = ISP_FW_MAJORX(isp->isp_fwrev);
|
|
|
|
hba->fc_fw_minor = ISP_FW_MINORX(isp->isp_fwrev);
|
|
|
|
hba->fc_fw_micro = ISP_FW_MICROX(isp->isp_fwrev);
|
2009-08-01 01:04:26 +00:00
|
|
|
hba->fc_nchannels = isp->isp_nchan;
|
2006-07-10 22:39:32 +00:00
|
|
|
if (IS_FC(isp)) {
|
2009-08-01 01:04:26 +00:00
|
|
|
hba->fc_nports = MAX_FC_TARG;
|
|
|
|
hba->fc_speed = FCPARAM(isp, hba->fc_channel)->isp_gbspeed;
|
|
|
|
hba->fc_topology = FCPARAM(isp, chan)->isp_topo + 1;
|
|
|
|
hba->fc_loopid = FCPARAM(isp, chan)->isp_loopid;
|
|
|
|
hba->nvram_node_wwn = FCPARAM(isp, chan)->isp_wwnn_nvram;
|
|
|
|
hba->nvram_port_wwn = FCPARAM(isp, chan)->isp_wwpn_nvram;
|
|
|
|
hba->active_node_wwn = FCPARAM(isp, chan)->isp_wwnn;
|
|
|
|
hba->active_port_wwn = FCPARAM(isp, chan)->isp_wwpn;
|
|
|
|
} else {
|
|
|
|
hba->fc_nports = MAX_TARGETS;
|
|
|
|
hba->fc_speed = 0;
|
|
|
|
hba->fc_topology = 0;
|
|
|
|
hba->nvram_node_wwn = 0ull;
|
|
|
|
hba->nvram_port_wwn = 0ull;
|
|
|
|
hba->active_node_wwn = 0ull;
|
|
|
|
hba->active_port_wwn = 0ull;
|
2006-07-10 22:39:32 +00:00
|
|
|
}
|
2002-06-16 05:08:02 +00:00
|
|
|
retval = 0;
|
|
|
|
break;
|
|
|
|
}
|
2005-10-29 02:46:59 +00:00
|
|
|
case ISP_TSK_MGMT:
|
|
|
|
{
|
|
|
|
int needmarker;
|
|
|
|
struct isp_fc_tsk_mgmt *fct = (struct isp_fc_tsk_mgmt *) addr;
|
2006-02-15 00:31:48 +00:00
|
|
|
uint16_t loopid;
|
2005-10-29 02:46:59 +00:00
|
|
|
mbreg_t mbs;
|
|
|
|
|
|
|
|
if (IS_SCSI(isp)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
chan = fct->chan;
|
|
|
|
if (chan < 0 || chan >= isp->isp_nchan) {
|
|
|
|
retval = -ENXIO;
|
2005-10-29 02:46:59 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
needmarker = retval = 0;
|
|
|
|
loopid = fct->loopid;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
uint8_t local[QENTRY_LEN];
|
|
|
|
isp24xx_tmf_t *tmf;
|
|
|
|
isp24xx_statusreq_t *sp;
|
|
|
|
fcparam *fcp = FCPARAM(isp, chan);
|
|
|
|
fcportdb_t *lp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_FC_TARG; i++) {
|
|
|
|
lp = &fcp->portdb[i];
|
|
|
|
if (lp->handle == loopid) {
|
|
|
|
break;
|
|
|
|
}
|
2005-10-29 02:46:59 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (i == MAX_FC_TARG) {
|
|
|
|
retval = ENXIO;
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* XXX VALIDATE LP XXX */
|
|
|
|
tmf = (isp24xx_tmf_t *) local;
|
|
|
|
ISP_MEMZERO(tmf, QENTRY_LEN);
|
|
|
|
tmf->tmf_header.rqs_entry_type = RQSTYPE_TSK_MGMT;
|
|
|
|
tmf->tmf_header.rqs_entry_count = 1;
|
|
|
|
tmf->tmf_nphdl = lp->handle;
|
|
|
|
tmf->tmf_delay = 2;
|
|
|
|
tmf->tmf_timeout = 2;
|
|
|
|
tmf->tmf_tidlo = lp->portid;
|
|
|
|
tmf->tmf_tidhi = lp->portid >> 16;
|
|
|
|
tmf->tmf_vpidx = ISP_GET_VPIDX(isp, chan);
|
|
|
|
tmf->tmf_lun[1] = fct->lun & 0xff;
|
|
|
|
if (fct->lun >= 256) {
|
|
|
|
tmf->tmf_lun[0] = 0x40 | (fct->lun >> 8);
|
|
|
|
}
|
|
|
|
switch (fct->action) {
|
|
|
|
case IPT_CLEAR_ACA:
|
|
|
|
tmf->tmf_flags = ISP24XX_TMF_CLEAR_ACA;
|
|
|
|
break;
|
|
|
|
case IPT_TARGET_RESET:
|
|
|
|
tmf->tmf_flags = ISP24XX_TMF_TARGET_RESET;
|
|
|
|
needmarker = 1;
|
|
|
|
break;
|
|
|
|
case IPT_LUN_RESET:
|
|
|
|
tmf->tmf_flags = ISP24XX_TMF_LUN_RESET;
|
|
|
|
needmarker = 1;
|
|
|
|
break;
|
|
|
|
case IPT_CLEAR_TASK_SET:
|
|
|
|
tmf->tmf_flags = ISP24XX_TMF_CLEAR_TASK_SET;
|
|
|
|
needmarker = 1;
|
|
|
|
break;
|
|
|
|
case IPT_ABORT_TASK_SET:
|
|
|
|
tmf->tmf_flags = ISP24XX_TMF_ABORT_TASK_SET;
|
|
|
|
needmarker = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
retval = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (retval) {
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
MBSINIT(&mbs, MBOX_EXEC_COMMAND_IOCB_A64, MBLOGALL, 5000000);
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (FC_SCRATCH_ACQUIRE(isp, chan)) {
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
retval = ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
isp_put_24xx_tmf(isp, tmf, fcp->isp_scratch);
|
|
|
|
MEMORYBARRIER(isp, SYNC_SFORDEV, 0, QENTRY_LEN);
|
|
|
|
sp = (isp24xx_statusreq_t *) local;
|
|
|
|
sp->req_completion_status = 1;
|
2005-10-29 02:46:59 +00:00
|
|
|
retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
|
2009-08-01 01:04:26 +00:00
|
|
|
MEMORYBARRIER(isp, SYNC_SFORCPU, QENTRY_LEN, QENTRY_LEN);
|
|
|
|
isp_get_24xx_response(isp, &((isp24xx_statusreq_t *)fcp->isp_scratch)[1], sp);
|
|
|
|
FC_SCRATCH_RELEASE(isp, chan);
|
|
|
|
if (retval || sp->req_completion_status != 0) {
|
|
|
|
FC_SCRATCH_RELEASE(isp, chan);
|
2005-10-29 02:46:59 +00:00
|
|
|
retval = EIO;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
if (retval == 0) {
|
|
|
|
if (needmarker) {
|
|
|
|
fcp->sendmarker = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
MBSINIT(&mbs, 0, MBLOGALL, 0);
|
|
|
|
if (ISP_CAP_2KLOGIN(isp) == 0) {
|
|
|
|
loopid <<= 8;
|
|
|
|
}
|
|
|
|
switch (fct->action) {
|
|
|
|
case IPT_CLEAR_ACA:
|
|
|
|
mbs.param[0] = MBOX_CLEAR_ACA;
|
|
|
|
mbs.param[1] = loopid;
|
|
|
|
mbs.param[2] = fct->lun;
|
|
|
|
break;
|
|
|
|
case IPT_TARGET_RESET:
|
|
|
|
mbs.param[0] = MBOX_TARGET_RESET;
|
|
|
|
mbs.param[1] = loopid;
|
|
|
|
needmarker = 1;
|
|
|
|
break;
|
|
|
|
case IPT_LUN_RESET:
|
|
|
|
mbs.param[0] = MBOX_LUN_RESET;
|
|
|
|
mbs.param[1] = loopid;
|
|
|
|
mbs.param[2] = fct->lun;
|
|
|
|
needmarker = 1;
|
|
|
|
break;
|
|
|
|
case IPT_CLEAR_TASK_SET:
|
|
|
|
mbs.param[0] = MBOX_CLEAR_TASK_SET;
|
|
|
|
mbs.param[1] = loopid;
|
|
|
|
mbs.param[2] = fct->lun;
|
|
|
|
needmarker = 1;
|
|
|
|
break;
|
|
|
|
case IPT_ABORT_TASK_SET:
|
|
|
|
mbs.param[0] = MBOX_ABORT_TASK_SET;
|
|
|
|
mbs.param[1] = loopid;
|
|
|
|
mbs.param[2] = fct->lun;
|
|
|
|
needmarker = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
retval = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (retval == 0) {
|
|
|
|
if (needmarker) {
|
|
|
|
FCPARAM(isp, chan)->sendmarker = 1;
|
|
|
|
}
|
|
|
|
retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
|
|
|
|
if (retval) {
|
|
|
|
retval = EIO;
|
|
|
|
}
|
|
|
|
}
|
2005-10-29 02:46:59 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_UNLOCK(isp);
|
2005-10-29 02:46:59 +00:00
|
|
|
break;
|
|
|
|
}
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (retval);
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2006-09-26 04:59:52 +00:00
|
|
|
static void
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_intr_enable(void *arg)
|
2006-09-26 04:59:52 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
int chan;
|
|
|
|
ispsoftc_t *isp = arg;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
for (chan = 0; chan < isp->isp_nchan; chan++) {
|
|
|
|
if (IS_FC(isp)) {
|
|
|
|
if (FCPARAM(isp, chan)->role != ISP_ROLE_NONE) {
|
|
|
|
ISP_ENABLE_INTS(isp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (SDPARAM(isp, chan)->role != ISP_ROLE_NONE) {
|
|
|
|
ISP_ENABLE_INTS(isp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-09-26 04:59:52 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
/* Release our hook so that the boot can continue. */
|
|
|
|
config_intrhook_disestablish(&isp->isp_osinfo.ehook);
|
|
|
|
}
|
2006-11-14 08:45:48 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* Local Inlines
|
|
|
|
*/
|
2006-11-14 08:45:48 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE int isp_get_pcmd(ispsoftc_t *, union ccb *);
|
|
|
|
static ISP_INLINE void isp_free_pcmd(ispsoftc_t *, union ccb *);
|
2006-11-14 08:45:48 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE int
|
|
|
|
isp_get_pcmd(ispsoftc_t *isp, union ccb *ccb)
|
2000-07-04 01:05:43 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_PCMD(ccb) = isp->isp_osinfo.pcmd_free;
|
|
|
|
if (ISP_PCMD(ccb) == NULL) {
|
|
|
|
return (-1);
|
2001-02-11 03:47:39 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.pcmd_free = ((struct isp_pcmd *)ISP_PCMD(ccb))->next;
|
|
|
|
return (0);
|
2000-07-04 01:05:43 +00:00
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE void
|
|
|
|
isp_free_pcmd(ispsoftc_t *isp, union ccb *ccb)
|
|
|
|
{
|
|
|
|
((struct isp_pcmd *)ISP_PCMD(ccb))->next = isp->isp_osinfo.pcmd_free;
|
|
|
|
isp->isp_osinfo.pcmd_free = ISP_PCMD(ccb);
|
|
|
|
ISP_PCMD(ccb) = NULL;
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
/*
|
|
|
|
* Put the target mode functions here, because some are inlines
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef ISP_TARGET_MODE
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE int is_lun_enabled(ispsoftc_t *, int, lun_id_t);
|
|
|
|
static ISP_INLINE tstate_t *get_lun_statep(ispsoftc_t *, int, lun_id_t);
|
|
|
|
static ISP_INLINE tstate_t *get_lun_statep_from_tag(ispsoftc_t *, int, uint32_t);
|
|
|
|
static ISP_INLINE void rls_lun_statep(ispsoftc_t *, tstate_t *);
|
|
|
|
static ISP_INLINE inot_private_data_t *get_ntp_from_tagdata(ispsoftc_t *, uint32_t, uint32_t, tstate_t **);
|
|
|
|
static ISP_INLINE atio_private_data_t *isp_get_atpd(ispsoftc_t *, tstate_t *, uint32_t);
|
|
|
|
static ISP_INLINE void isp_put_atpd(ispsoftc_t *, tstate_t *, atio_private_data_t *);
|
|
|
|
static ISP_INLINE inot_private_data_t *isp_get_ntpd(ispsoftc_t *, tstate_t *);
|
|
|
|
static ISP_INLINE inot_private_data_t *isp_find_ntpd(ispsoftc_t *, tstate_t *, uint32_t, uint32_t);
|
|
|
|
static ISP_INLINE void isp_put_ntpd(ispsoftc_t *, tstate_t *, inot_private_data_t *);
|
|
|
|
static cam_status create_lun_state(ispsoftc_t *, int, struct cam_path *, tstate_t **);
|
2006-04-21 18:30:01 +00:00
|
|
|
static void destroy_lun_state(ispsoftc_t *, tstate_t *);
|
2009-08-01 01:04:26 +00:00
|
|
|
static void isp_enable_lun(ispsoftc_t *, union ccb *);
|
|
|
|
static void isp_enable_deferred_luns(ispsoftc_t *, int);
|
|
|
|
static cam_status isp_enable_deferred(ispsoftc_t *, int, lun_id_t);
|
|
|
|
static void isp_disable_lun(ispsoftc_t *, union ccb *);
|
|
|
|
static int isp_enable_target_mode(ispsoftc_t *, int);
|
2006-04-21 18:30:01 +00:00
|
|
|
static void isp_ledone(ispsoftc_t *, lun_entry_t *);
|
2000-07-18 06:58:28 +00:00
|
|
|
static timeout_t isp_refire_putback_atio;
|
2001-04-04 21:58:29 +00:00
|
|
|
static void isp_complete_ctio(union ccb *);
|
|
|
|
static void isp_target_putback_atio(union ccb *);
|
2006-04-21 18:30:01 +00:00
|
|
|
static void isp_target_start_ctio(ispsoftc_t *, union ccb *);
|
2009-08-01 01:04:26 +00:00
|
|
|
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 void isp_handle_platform_ctio(ispsoftc_t *, void *);
|
|
|
|
static void isp_handle_platform_notify_scsi(ispsoftc_t *, in_entry_t *);
|
|
|
|
static void isp_handle_platform_notify_fc(ispsoftc_t *, in_fcentry_t *);
|
|
|
|
static void isp_handle_platform_notify_24xx(ispsoftc_t *, in_fcentry_24xx_t *);
|
|
|
|
static int isp_handle_platform_target_notify_ack(ispsoftc_t *, isp_notify_t *);
|
|
|
|
static void isp_handle_platform_target_tmf(ispsoftc_t *, isp_notify_t *);
|
|
|
|
static void isp_target_mark_aborted(ispsoftc_t *, union ccb *);
|
|
|
|
static void isp_target_mark_aborted_early(ispsoftc_t *, tstate_t *, uint32_t);
|
|
|
|
|
|
|
|
static ISP_INLINE int
|
2006-04-21 18:30:01 +00:00
|
|
|
is_lun_enabled(ispsoftc_t *isp, int bus, lun_id_t lun)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
|
|
|
tstate_t *tptr;
|
2009-08-01 01:04:26 +00:00
|
|
|
struct tslist *lhp;
|
|
|
|
|
|
|
|
ISP_GET_PC_ADDR(isp, bus, lun_hash[LUN_HASH_FUNC(lun)], lhp);
|
|
|
|
SLIST_FOREACH(tptr, lhp, next) {
|
|
|
|
if (xpt_path_lun_id(tptr->owner) == lun) {
|
2000-01-03 23:55:30 +00:00
|
|
|
return (1);
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
|
|
|
dump_tstates(ispsoftc_t *isp, int bus)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
int i, j;
|
|
|
|
struct tslist *lhp;
|
|
|
|
tstate_t *tptr = NULL;
|
|
|
|
|
|
|
|
if (bus >= isp->isp_nchan) {
|
|
|
|
return;
|
2001-04-04 21:58:29 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
for (i = 0; i < LUN_HASH_SIZE; i++) {
|
|
|
|
ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp);
|
|
|
|
j = 0;
|
|
|
|
SLIST_FOREACH(tptr, lhp, next) {
|
|
|
|
xpt_print(tptr->owner, "[%d, %d] atio_cnt=%d inot_cnt=%d\n", i, j, tptr->atio_count, tptr->inot_count);
|
|
|
|
j++;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE tstate_t *
|
2006-04-21 18:30:01 +00:00
|
|
|
get_lun_statep(ispsoftc_t *isp, int bus, lun_id_t lun)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
tstate_t *tptr = NULL;
|
2009-08-01 01:04:26 +00:00
|
|
|
struct tslist *lhp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (bus < isp->isp_nchan) {
|
|
|
|
for (i = 0; i < LUN_HASH_SIZE; i++) {
|
|
|
|
ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp);
|
|
|
|
SLIST_FOREACH(tptr, lhp, next) {
|
|
|
|
if (xpt_path_lun_id(tptr->owner) == lun) {
|
|
|
|
tptr->hold++;
|
|
|
|
return (tptr);
|
|
|
|
}
|
|
|
|
}
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
return (NULL);
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE tstate_t *
|
|
|
|
get_lun_statep_from_tag(ispsoftc_t *isp, int bus, uint32_t tagval)
|
|
|
|
{
|
|
|
|
tstate_t *tptr = NULL;
|
|
|
|
atio_private_data_t *atp;
|
|
|
|
struct tslist *lhp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (bus < isp->isp_nchan && tagval != 0) {
|
|
|
|
for (i = 0; i < LUN_HASH_SIZE; i++) {
|
|
|
|
ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp);
|
|
|
|
SLIST_FOREACH(tptr, lhp, next) {
|
|
|
|
atp = isp_get_atpd(isp, tptr, tagval);
|
|
|
|
if (atp && atp->tag == tagval) {
|
|
|
|
tptr->hold++;
|
|
|
|
return (tptr);
|
|
|
|
}
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
return (NULL);
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE inot_private_data_t *
|
|
|
|
get_ntp_from_tagdata(ispsoftc_t *isp, uint32_t tag_id, uint32_t seq_id, tstate_t **rslt)
|
|
|
|
{
|
|
|
|
inot_private_data_t *ntp;
|
|
|
|
tstate_t *tptr;
|
|
|
|
struct tslist *lhp;
|
|
|
|
int bus, i;
|
|
|
|
|
|
|
|
for (bus = 0; bus < isp->isp_nchan; bus++) {
|
|
|
|
for (i = 0; i < LUN_HASH_SIZE; i++) {
|
|
|
|
ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp);
|
|
|
|
SLIST_FOREACH(tptr, lhp, next) {
|
|
|
|
ntp = isp_find_ntpd(isp, tptr, tag_id, seq_id);
|
|
|
|
if (ntp) {
|
|
|
|
*rslt = tptr;
|
|
|
|
tptr->hold++;
|
|
|
|
return (ntp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
static ISP_INLINE void
|
2006-04-21 18:30:01 +00:00
|
|
|
rls_lun_statep(ispsoftc_t *isp, tstate_t *tptr)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
KASSERT((tptr->hold), ("tptr not held"));
|
|
|
|
tptr->hold--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_tmcmd_restart(ispsoftc_t *isp)
|
|
|
|
{
|
|
|
|
inot_private_data_t *ntp;
|
|
|
|
tstate_t *tptr;
|
|
|
|
struct tslist *lhp;
|
|
|
|
int bus, i;
|
|
|
|
|
|
|
|
for (bus = 0; bus < isp->isp_nchan; bus++) {
|
|
|
|
for (i = 0; i < LUN_HASH_SIZE; i++) {
|
|
|
|
ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp);
|
|
|
|
SLIST_FOREACH(tptr, lhp, next) {
|
|
|
|
inot_private_data_t *restart_queue = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = NULL;
|
|
|
|
while (restart_queue) {
|
|
|
|
ntp = restart_queue;
|
|
|
|
restart_queue = ntp->rd.nt.nt_hba;
|
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at7_entry_t *)ntp->rd.data)->at_rxid);
|
|
|
|
isp_handle_platform_atio7(isp, (at7_entry_t *) ntp->rd.data);
|
|
|
|
} else {
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at2_entry_t *)ntp->rd.data)->at_rxid);
|
|
|
|
isp_handle_platform_atio2(isp, (at2_entry_t *) ntp->rd.data);
|
|
|
|
}
|
|
|
|
isp_put_ntpd(isp, tptr, ntp);
|
|
|
|
if (tptr->restart_queue && restart_queue != NULL) {
|
|
|
|
ntp = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = restart_queue;
|
|
|
|
while (restart_queue->rd.nt.nt_hba) {
|
|
|
|
restart_queue = restart_queue->rd.nt.nt_hba;
|
|
|
|
}
|
|
|
|
restart_queue->rd.nt.nt_hba = ntp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE atio_private_data_t *
|
|
|
|
isp_get_atpd(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag)
|
2001-10-01 03:48:42 +00:00
|
|
|
{
|
|
|
|
atio_private_data_t *atp;
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
if (tag == 0) {
|
|
|
|
atp = tptr->atfree;
|
|
|
|
if (atp) {
|
|
|
|
tptr->atfree = atp->next;
|
|
|
|
}
|
|
|
|
return (atp);
|
|
|
|
}
|
|
|
|
for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) {
|
|
|
|
if (atp->tag == tag) {
|
2001-10-01 03:48:42 +00:00
|
|
|
return (atp);
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ISP_INLINE void
|
|
|
|
isp_put_atpd(ispsoftc_t *isp, tstate_t *tptr, atio_private_data_t *atp)
|
|
|
|
{
|
|
|
|
atp->tag = 0;
|
|
|
|
atp->dead = 0;
|
|
|
|
atp->next = tptr->atfree;
|
|
|
|
tptr->atfree = atp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_dump_atpd(ispsoftc_t *isp, tstate_t *tptr)
|
|
|
|
{
|
|
|
|
atio_private_data_t *atp;
|
|
|
|
const char *states[8] = { "Free", "ATIO", "CAM", "CTIO", "LAST_CTIO", "PDON", "?6", "7" };
|
|
|
|
|
|
|
|
for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) {
|
|
|
|
if (atp->tag == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
xpt_print(tptr->owner, "ATP: [0x%x] origdlen %u bytes_xfrd %u last_xfr %u lun %u nphdl 0x%04x s_id 0x%06x d_id 0x%06x oxid 0x%04x state %s\n",
|
|
|
|
atp->tag, atp->orig_datalen, atp->bytes_xfered, atp->last_xframt, atp->lun, atp->nphdl, atp->sid, atp->portid, atp->oxid, states[atp->state & 0x7]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ISP_INLINE inot_private_data_t *
|
|
|
|
isp_get_ntpd(ispsoftc_t *isp, tstate_t *tptr)
|
|
|
|
{
|
|
|
|
inot_private_data_t *ntp;
|
|
|
|
ntp = tptr->ntfree;
|
|
|
|
if (ntp) {
|
|
|
|
tptr->ntfree = ntp->next;
|
|
|
|
}
|
|
|
|
return (ntp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ISP_INLINE inot_private_data_t *
|
|
|
|
isp_find_ntpd(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag_id, uint32_t seq_id)
|
|
|
|
{
|
|
|
|
inot_private_data_t *ntp;
|
|
|
|
for (ntp = tptr->ntpool; ntp < &tptr->ntpool[ATPDPSIZE]; ntp++) {
|
|
|
|
if (ntp->rd.tag_id == tag_id && ntp->rd.seq_id == seq_id) {
|
|
|
|
return (ntp);
|
|
|
|
}
|
2001-10-01 03:48:42 +00:00
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE void
|
|
|
|
isp_put_ntpd(ispsoftc_t *isp, tstate_t *tptr, inot_private_data_t *ntp)
|
|
|
|
{
|
|
|
|
ntp->rd.tag_id = ntp->rd.seq_id = 0;
|
|
|
|
ntp->next = tptr->ntfree;
|
|
|
|
tptr->ntfree = ntp;
|
|
|
|
}
|
|
|
|
|
2000-01-03 23:55:30 +00:00
|
|
|
static cam_status
|
2009-08-01 01:04:26 +00:00
|
|
|
create_lun_state(ispsoftc_t *isp, int bus, struct cam_path *path, tstate_t **rslt)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
|
|
|
cam_status status;
|
|
|
|
lun_id_t lun;
|
2009-08-01 01:04:26 +00:00
|
|
|
struct tslist *lhp;
|
|
|
|
tstate_t *tptr;
|
|
|
|
int i;
|
2000-01-03 23:55:30 +00:00
|
|
|
|
|
|
|
lun = xpt_path_lun_id(path);
|
2009-08-01 01:04:26 +00:00
|
|
|
if (lun != CAM_LUN_WILDCARD) {
|
|
|
|
if (lun >= ISP_MAX_LUNS(isp)) {
|
|
|
|
return (CAM_LUN_INVALID);
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2001-04-04 21:58:29 +00:00
|
|
|
if (is_lun_enabled(isp, bus, lun)) {
|
2000-01-03 23:55:30 +00:00
|
|
|
return (CAM_LUN_ALRDY_ENA);
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
tptr = (tstate_t *) malloc(sizeof (tstate_t), M_DEVBUF, M_NOWAIT|M_ZERO);
|
|
|
|
if (tptr == NULL) {
|
2000-01-03 23:55:30 +00:00
|
|
|
return (CAM_RESRC_UNAVAIL);
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
status = xpt_create_path(&tptr->owner, NULL, xpt_path_path_id(path), xpt_path_target_id(path), lun);
|
2000-01-03 23:55:30 +00:00
|
|
|
if (status != CAM_REQ_CMP) {
|
2009-08-01 01:04:26 +00:00
|
|
|
free(tptr, M_DEVBUF);
|
2000-01-03 23:55:30 +00:00
|
|
|
return (status);
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
SLIST_INIT(&tptr->atios);
|
|
|
|
SLIST_INIT(&tptr->inots);
|
|
|
|
for (i = 0; i < ATPDPSIZE-1; i++) {
|
|
|
|
tptr->atpool[i].next = &tptr->atpool[i+1];
|
|
|
|
tptr->ntpool[i].next = &tptr->ntpool[i+1];
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
tptr->atfree = tptr->atpool;
|
|
|
|
tptr->ntfree = tptr->ntpool;
|
|
|
|
tptr->hold = 1;
|
|
|
|
ISP_GET_PC_ADDR(isp, bus, lun_hash[LUN_HASH_FUNC(xpt_path_lun_id(tptr->owner))], lhp);
|
|
|
|
SLIST_INSERT_HEAD(lhp, tptr, next);
|
|
|
|
*rslt = tptr;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, path, "created tstate\n");
|
2000-01-03 23:55:30 +00:00
|
|
|
return (CAM_REQ_CMP);
|
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static ISP_INLINE void
|
2006-04-21 18:30:01 +00:00
|
|
|
destroy_lun_state(ispsoftc_t *isp, tstate_t *tptr)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
struct tslist *lhp;
|
|
|
|
KASSERT((tptr->hold == 0), ("tptr still held"));
|
|
|
|
ISP_GET_PC_ADDR(isp, xpt_path_path_id(tptr->owner), lun_hash[LUN_HASH_FUNC(xpt_path_lun_id(tptr->owner))], lhp);
|
|
|
|
SLIST_REMOVE(lhp, tptr, tstate, next);
|
|
|
|
xpt_free_path(tptr->owner);
|
2000-01-03 23:55:30 +00:00
|
|
|
free(tptr, M_DEVBUF);
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* Enable a lun.
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
|
|
|
isp_enable_lun(ispsoftc_t *isp, union ccb *ccb)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
2007-06-08 01:39:04 +00:00
|
|
|
tstate_t *tptr = NULL;
|
2009-08-01 01:04:26 +00:00
|
|
|
int bus, tm_enabled, target_role;
|
|
|
|
target_id_t target;
|
2000-01-03 23:55:30 +00:00
|
|
|
lun_id_t lun;
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* We only support either a wildcard target/lun or a target ID of zero and a non-wildcard lun
|
|
|
|
*/
|
2004-02-08 19:17:56 +00:00
|
|
|
bus = XS_CHANNEL(ccb);
|
2009-08-01 01:04:26 +00:00
|
|
|
target = ccb->ccb_h.target_id;
|
2000-01-03 23:55:30 +00:00
|
|
|
lun = ccb->ccb_h.target_lun;
|
2009-08-01 01:04:26 +00:00
|
|
|
if (target != CAM_TARGET_WILDCARD && target != 0) {
|
|
|
|
ccb->ccb_h.status = CAM_TID_INVALID;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (target == CAM_TARGET_WILDCARD && lun != CAM_LUN_WILDCARD) {
|
|
|
|
ccb->ccb_h.status = CAM_LUN_INVALID;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (target != CAM_TARGET_WILDCARD && lun == CAM_LUN_WILDCARD) {
|
|
|
|
ccb->ccb_h.status = CAM_LUN_INVALID;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
2006-12-05 07:50:23 +00:00
|
|
|
if (isp->isp_dblev & ISP_LOGTDEBUG0) {
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_print(ccb->ccb_h.path, "enabling lun 0x%x on channel %d\n", lun, bus);
|
2006-12-05 07:50:23 +00:00
|
|
|
}
|
2000-08-27 23:38:44 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* Wait until we're not busy with the lun enables subsystem
|
|
|
|
*/
|
|
|
|
while (isp->isp_osinfo.tmbusy) {
|
|
|
|
isp->isp_osinfo.tmwanted = 1;
|
|
|
|
mtx_sleep(isp, &isp->isp_lock, PRIBIO, "want_isp_enable_lun", 0);
|
2000-08-27 23:38:44 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.tmbusy = 1;
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* This is as a good a place as any to check f/w capabilities.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (IS_FC(isp)) {
|
|
|
|
if (ISP_CAP_TMODE(isp) == 0) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "firmware does not support target mode\n");
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
|
2009-08-01 01:04:26 +00:00
|
|
|
goto done;
|
2001-02-11 03:47:39 +00:00
|
|
|
}
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* We *could* handle non-SCCLUN f/w, but we'd have to
|
|
|
|
* dork with our already fragile enable/disable code.
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_CAP_SCCFW(isp) == 0) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "firmware not SCCLUN capable\n");
|
2004-02-07 03:47:33 +00:00
|
|
|
ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
|
2009-08-01 01:04:26 +00:00
|
|
|
goto done;
|
2000-08-27 23:38:44 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
target_role = (FCPARAM(isp, bus)->role & ISP_ROLE_TARGET) != 0;
|
|
|
|
|
2001-08-31 21:39:04 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
target_role = (SDPARAM(isp, bus)->role & ISP_ROLE_TARGET) != 0;
|
2001-08-31 21:39:04 +00:00
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
|
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* Create the state pointer.
|
|
|
|
* It should not already exist.
|
2000-01-03 23:55:30 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
tptr = get_lun_statep(isp, bus, lun);
|
|
|
|
if (tptr) {
|
|
|
|
ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
ccb->ccb_h.status = create_lun_state(isp, bus, ccb->ccb_h.path, &tptr);
|
|
|
|
if (ccb->ccb_h.status != CAM_REQ_CMP) {
|
|
|
|
goto done;
|
2001-08-31 21:39:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* We have a tricky maneuver to perform here.
|
|
|
|
*
|
|
|
|
* If target mode isn't already enabled here,
|
|
|
|
* *and* our current role includes target mode,
|
|
|
|
* we enable target mode here.
|
|
|
|
*
|
2001-08-31 21:39:04 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_GET_PC(isp, bus, tm_enabled, tm_enabled);
|
|
|
|
if (tm_enabled == 0 && target_role != 0) {
|
|
|
|
if (isp_enable_target_mode(isp, bus)) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
|
|
|
destroy_lun_state(isp, tptr);
|
|
|
|
tptr = NULL;
|
|
|
|
goto done;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
tm_enabled = 1;
|
2001-08-31 21:39:04 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* Now check to see whether this bus is in target mode already.
|
|
|
|
*
|
|
|
|
* If not, a later role change into target mode will finish the job.
|
|
|
|
*/
|
|
|
|
if (tm_enabled == 0) {
|
|
|
|
ISP_SET_PC(isp, bus, tm_enable_defer, 1);
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_print(ccb->ccb_h.path, "Target Mode Not Enabled Yet- Lun Enables Deferred\n");
|
|
|
|
goto done;
|
2004-02-08 19:17:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* Enable the lun.
|
2004-02-08 19:17:56 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
ccb->ccb_h.status = isp_enable_deferred(isp, bus, lun);
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (ccb->ccb_h.status != CAM_REQ_CMP && tptr) {
|
|
|
|
destroy_lun_state(isp, tptr);
|
|
|
|
tptr = NULL;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (tptr) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
2004-02-08 19:17:56 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.tmbusy = 0;
|
|
|
|
if (isp->isp_osinfo.tmwanted) {
|
|
|
|
isp->isp_osinfo.tmwanted = 0;
|
|
|
|
wakeup(isp);
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
|
|
|
isp_enable_deferred_luns(ispsoftc_t *isp, int bus)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* XXX: not entirely implemented yet
|
|
|
|
*/
|
|
|
|
(void) isp_enable_deferred(isp, bus, 0);
|
|
|
|
}
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static uint32_t
|
|
|
|
isp_enable_deferred(ispsoftc_t *isp, int bus, lun_id_t lun)
|
|
|
|
{
|
|
|
|
cam_status status;
|
|
|
|
|
|
|
|
isp_prt(isp, ISP_LOGTINFO, "%s: bus %d lun %u", __func__, bus, lun);
|
|
|
|
if (IS_24XX(isp) || (IS_FC(isp) && ISP_FC_PC(isp, bus)->tm_luns_enabled)) {
|
|
|
|
status = CAM_REQ_CMP;
|
2000-01-03 23:55:30 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
int cmd_cnt, not_cnt;
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_23XX(isp)) {
|
|
|
|
cmd_cnt = DFLT_CMND_CNT;
|
|
|
|
not_cnt = DFLT_INOT_CNT;
|
|
|
|
} else {
|
|
|
|
cmd_cnt = 64;
|
|
|
|
not_cnt = 8;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
status = CAM_REQ_INPROG;
|
|
|
|
isp->isp_osinfo.rptr = &status;
|
|
|
|
if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, lun, DFLT_CMND_CNT, DFLT_INOT_CNT)) {
|
|
|
|
status = CAM_RESRC_UNAVAIL;
|
|
|
|
} else {
|
|
|
|
mtx_sleep(&status, &isp->isp_lock, PRIBIO, "isp_enable_deferred", 0);
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.rptr = NULL;
|
2004-02-08 19:17:56 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
if (status == CAM_REQ_CMP) {
|
|
|
|
ISP_SET_PC(isp, bus, tm_luns_enabled, 1);
|
|
|
|
isp_prt(isp, ISP_LOGTINFO, "bus %d lun %u now enabled for target mode", bus, lun);
|
|
|
|
}
|
|
|
|
return (status);
|
2004-02-08 19:17:56 +00:00
|
|
|
}
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
|
2004-02-08 19:17:56 +00:00
|
|
|
static void
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_disable_lun(ispsoftc_t *isp, union ccb *ccb)
|
2004-02-08 19:17:56 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
tstate_t *tptr = NULL;
|
|
|
|
int bus;
|
|
|
|
cam_status status;
|
|
|
|
target_id_t target;
|
|
|
|
lun_id_t lun;
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
bus = XS_CHANNEL(ccb);
|
|
|
|
target = ccb->ccb_h.target_id;
|
|
|
|
lun = ccb->ccb_h.target_lun;
|
|
|
|
if (target != CAM_TARGET_WILDCARD && target != 0) {
|
|
|
|
ccb->ccb_h.status = CAM_TID_INVALID;
|
|
|
|
xpt_done(ccb);
|
2004-02-08 19:17:56 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (target == CAM_TARGET_WILDCARD && lun != CAM_LUN_WILDCARD) {
|
|
|
|
ccb->ccb_h.status = CAM_LUN_INVALID;
|
|
|
|
xpt_done(ccb);
|
2004-02-08 19:17:56 +00:00
|
|
|
return;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2001-08-31 21:39:04 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (target != CAM_TARGET_WILDCARD && lun == CAM_LUN_WILDCARD) {
|
|
|
|
ccb->ccb_h.status = CAM_LUN_INVALID;
|
2004-02-08 19:17:56 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
if (isp->isp_dblev & ISP_LOGTDEBUG0) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "enabling lun 0x%x on channel %d\n", lun, bus);
|
2004-02-08 19:17:56 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* See if we're busy disabling a lun now.
|
|
|
|
*/
|
|
|
|
while (isp->isp_osinfo.tmbusy) {
|
|
|
|
isp->isp_osinfo.tmwanted = 1;
|
|
|
|
mtx_sleep(isp, &isp->isp_lock, PRIBIO, "want_isp_disable_lun", 0);
|
2004-02-08 19:17:56 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.tmbusy = 1;
|
2004-02-08 19:17:56 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* Find the state pointer.
|
|
|
|
*/
|
|
|
|
if ((tptr = get_lun_statep(isp, bus, lun)) == NULL) {
|
|
|
|
ccb->ccb_h.status = CAM_PATH_INVALID;
|
|
|
|
goto done;
|
2004-02-08 19:17:56 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* If we're a 24XX card, we're done.
|
|
|
|
*/
|
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
status = CAM_REQ_CMP;
|
|
|
|
goto done;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* For SCC FW, we only deal with lun zero.
|
|
|
|
*/
|
|
|
|
if (IS_FC(isp)) {
|
|
|
|
lun = 0;
|
|
|
|
}
|
2004-02-08 19:17:56 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
isp->isp_osinfo.rptr = &status;
|
|
|
|
status = CAM_REQ_INPROG;
|
|
|
|
if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, lun, 0, 0)) {
|
|
|
|
status = CAM_RESRC_UNAVAIL;
|
2000-01-03 23:55:30 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
mtx_sleep(ccb, &isp->isp_lock, PRIBIO, "isp_disable_lun", 0);
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
if (status == CAM_REQ_CMP) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "now disabled for target mode\n");
|
|
|
|
}
|
|
|
|
if (tptr) {
|
2000-01-03 23:55:30 +00:00
|
|
|
rls_lun_statep(isp, tptr);
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
isp->isp_osinfo.rptr = NULL;
|
|
|
|
isp->isp_osinfo.tmbusy = 0;
|
|
|
|
if (isp->isp_osinfo.tmwanted) {
|
|
|
|
isp->isp_osinfo.tmwanted = 0;
|
|
|
|
wakeup(isp);
|
|
|
|
}
|
|
|
|
xpt_done(ccb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
isp_enable_target_mode(ispsoftc_t *isp, int bus)
|
|
|
|
{
|
|
|
|
int ct;
|
|
|
|
|
|
|
|
ISP_GET_PC(isp, bus, tm_enabled, ct);
|
|
|
|
if (ct != 0) {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_SCSI(isp)) {
|
|
|
|
mbreg_t mbs;
|
|
|
|
|
|
|
|
MBSINIT(&mbs, MBOX_ENABLE_TARGET_MODE, MBLOGALL, 0);
|
|
|
|
mbs.param[0] = MBOX_ENABLE_TARGET_MODE;
|
|
|
|
mbs.param[1] = ENABLE_TARGET_FLAG|ENABLE_TQING_FLAG;
|
|
|
|
mbs.param[2] = bus << 7;
|
|
|
|
if (isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs) < 0 || mbs.param[0] != MBOX_COMMAND_COMPLETE) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "Unable to add Target Role to Bus %d", bus);
|
|
|
|
return (EIO);
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
SDPARAM(isp, bus)->role |= ISP_ROLE_TARGET;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_SET_PC(isp, bus, tm_enabled, 1);
|
|
|
|
isp_prt(isp, ISP_LOGINFO, "Target Role added to Bus %d", bus);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NEEDED
|
|
|
|
static int
|
|
|
|
isp_disable_target_mode(ispsoftc_t *isp, int bus)
|
|
|
|
{
|
|
|
|
int ct;
|
|
|
|
|
|
|
|
ISP_GET_PC(isp, bus, tm_enabled, ct);
|
|
|
|
if (ct == 0) {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_SCSI(isp)) {
|
|
|
|
mbreg_t mbs;
|
|
|
|
|
|
|
|
MBSINIT(&mbs, MBOX_ENABLE_TARGET_MODE, MBLOGALL, 0);
|
|
|
|
mbs.param[2] = bus << 7;
|
|
|
|
if (isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs) < 0 || mbs.param[0] != MBOX_COMMAND_COMPLETE) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "Unable to subtract Target Role to Bus %d", bus);
|
|
|
|
return (EIO);
|
|
|
|
}
|
|
|
|
SDPARAM(isp, bus)->role &= ~ISP_ROLE_TARGET;
|
|
|
|
}
|
|
|
|
ISP_SET_PC(isp, bus, tm_enabled, 0);
|
|
|
|
isp_prt(isp, ISP_LOGINFO, "Target Role subtracted from Bus %d", bus);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_ledone(ispsoftc_t *isp, lun_entry_t *lep)
|
|
|
|
{
|
|
|
|
uint32_t *rptr;
|
|
|
|
|
|
|
|
rptr = isp->isp_osinfo.rptr;
|
|
|
|
if (lep->le_status != LUN_OK) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "ENABLE/MODIFY LUN returned 0x%x", lep->le_status);
|
|
|
|
if (rptr) {
|
|
|
|
*rptr = CAM_REQ_CMP_ERR;
|
|
|
|
wakeup_one(rptr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (rptr) {
|
|
|
|
*rptr = CAM_REQ_CMP;
|
|
|
|
wakeup_one(rptr);
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-21 18:30:01 +00:00
|
|
|
static void
|
|
|
|
isp_target_start_ctio(ispsoftc_t *isp, union ccb *ccb)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
|
|
|
void *qe;
|
2009-08-01 01:04:26 +00:00
|
|
|
tstate_t *tptr;
|
|
|
|
atio_private_data_t *atp;
|
2000-01-15 01:50:48 +00:00
|
|
|
struct ccb_scsiio *cso = &ccb->csio;
|
2009-08-01 01:04:26 +00:00
|
|
|
uint32_t dmaresult, handle;
|
2006-02-15 00:31:48 +00:00
|
|
|
uint8_t local[QENTRY_LEN];
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* Do some sanity checks.
|
|
|
|
*/
|
|
|
|
if (cso->dxfer_len == 0) {
|
|
|
|
if ((ccb->ccb_h.flags & CAM_SEND_STATUS) == 0) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "a data transfer length of zero but no status to send is wrong\n");
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tptr = get_lun_statep(isp, XS_CHANNEL(ccb), XS_LUN(ccb));
|
|
|
|
if (tptr == NULL) {
|
|
|
|
tptr = get_lun_statep(isp, XS_CHANNEL(ccb), CAM_LUN_WILDCARD);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "%s: [0x%x] cannot find tstate pointer in %s\n", __func__, cso->tag_id);
|
|
|
|
dump_tstates(isp, XS_CHANNEL(ccb));
|
|
|
|
ccb->ccb_h.status = CAM_DEV_NOT_THERE;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
atp = isp_get_atpd(isp, tptr, cso->tag_id);
|
|
|
|
if (atp == NULL) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "%s: [0x%x] cannot find private data adjunct\n", __func__, cso->tag_id);
|
|
|
|
isp_dump_atpd(isp, tptr);
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (atp->dead) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "%s: [0x%x] stopping sending a CTIO for a dead command\n", __func__, cso->tag_id);
|
|
|
|
ccb->ccb_h.status = CAM_REQ_ABORTED;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to make sure we're still in target mode.
|
|
|
|
*/
|
|
|
|
if ((FCPARAM(isp, XS_CHANNEL(ccb))->role & ISP_ROLE_TARGET) == 0) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "%s: [0x%x] stopping sending a CTIO because we're no longer in target mode\n", __func__, cso->tag_id);
|
|
|
|
ccb->ccb_h.status = CAM_PROVIDE_FAIL;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
2000-07-18 06:58:28 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* Get some resources
|
|
|
|
*/
|
|
|
|
if (isp_get_pcmd(isp, ccb)) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
xpt_print(ccb->ccb_h.path, "out of PCMDs\n");
|
|
|
|
cam_freeze_devq(ccb->ccb_h.path);
|
|
|
|
cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 250, 0);
|
|
|
|
ccb->ccb_h.status = CAM_REQUEUE_REQ;
|
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
qe = isp_getrqentry(isp);
|
|
|
|
if (qe == NULL) {
|
|
|
|
xpt_print(ccb->ccb_h.path, rqo, __func__);
|
|
|
|
cam_freeze_devq(ccb->ccb_h.path);
|
|
|
|
cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 250, 0);
|
|
|
|
ccb->ccb_h.status = CAM_REQUEUE_REQ;
|
2006-04-21 18:30:01 +00:00
|
|
|
goto out;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2006-05-22 06:49:49 +00:00
|
|
|
memset(local, 0, QENTRY_LEN);
|
2000-01-03 23:55:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We're either moving data or completing a command here.
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
ct7_entry_t *cto = (ct7_entry_t *) local;
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7;
|
|
|
|
cto->ct_header.rqs_entry_count = 1;
|
|
|
|
cto->ct_header.rqs_seqno = 1;
|
|
|
|
cto->ct_nphdl = atp->nphdl;
|
|
|
|
cto->ct_rxid = atp->tag;
|
|
|
|
cto->ct_iid_lo = atp->portid;
|
|
|
|
cto->ct_iid_hi = atp->portid >> 16;
|
|
|
|
cto->ct_oxid = atp->oxid;
|
|
|
|
cto->ct_vpidx = ISP_GET_VPIDX(isp, XS_CHANNEL(ccb));
|
|
|
|
cto->ct_scsi_status = cso->scsi_status;
|
|
|
|
cto->ct_timeout = 120;
|
|
|
|
cto->ct_flags = atp->tattr << CT7_TASK_ATTR_SHIFT;
|
|
|
|
if (ccb->ccb_h.flags & CAM_SEND_STATUS) {
|
|
|
|
cto->ct_flags |= CT7_SENDSTATUS;
|
|
|
|
}
|
|
|
|
if (cso->dxfer_len == 0) {
|
|
|
|
cto->ct_flags |= CT7_FLAG_MODE1 | CT7_NO_DATA;
|
|
|
|
if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) {
|
|
|
|
int m = min(cso->sense_len, sizeof (struct scsi_sense_data));
|
|
|
|
cto->rsp.m1.ct_resplen = cto->ct_senselen = min(m, MAXRESPLEN_24XX);
|
|
|
|
memcpy(cto->rsp.m1.ct_resp, &cso->sense_data, cto->ct_senselen);
|
|
|
|
cto->ct_scsi_status |= (FCP_SNSLEN_VALID << 8);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cto->ct_flags |= CT7_FLAG_MODE0;
|
|
|
|
if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
|
|
|
|
cto->ct_flags |= CT7_DATA_IN;
|
|
|
|
} else {
|
|
|
|
cto->ct_flags |= CT7_DATA_OUT;
|
|
|
|
}
|
|
|
|
cto->rsp.m0.reloff = atp->bytes_xfered;
|
|
|
|
/*
|
|
|
|
* Don't overrun the limits placed on us
|
|
|
|
*/
|
|
|
|
if (atp->bytes_xfered + cso->dxfer_len > atp->orig_datalen) {
|
|
|
|
cso->dxfer_len = atp->orig_datalen - atp->bytes_xfered;
|
|
|
|
}
|
|
|
|
atp->last_xframt = cso->dxfer_len;
|
|
|
|
cto->rsp.m0.ct_xfrlen = cso->dxfer_len;
|
|
|
|
}
|
|
|
|
if (cto->ct_flags & CT7_SENDSTATUS) {
|
|
|
|
int lvl = (cso->scsi_status)? ISP_LOGTINFO : ISP_LOGTDEBUG0;
|
|
|
|
cto->ct_resid = atp->orig_datalen - (atp->bytes_xfered + cso->dxfer_len);
|
|
|
|
if (cto->ct_resid < 0) {
|
|
|
|
cto->ct_scsi_status |= (FCP_RESID_OVERFLOW << 8);
|
|
|
|
} else if (cto->ct_resid > 0) {
|
|
|
|
cto->ct_scsi_status |= (FCP_RESID_UNDERFLOW << 8);
|
|
|
|
}
|
|
|
|
atp->state = ATPD_STATE_LAST_CTIO;
|
|
|
|
ISP_PATH_PRT(isp, lvl, cso->ccb_h.path, "%s: CTIO7[%x] CDB0=%x scsi status %x flags %x resid %d xfrlen %u offset %u\n", __func__, cto->ct_rxid,
|
|
|
|
atp->cdb0, cto->ct_scsi_status, cto->ct_flags, cto->ct_resid, cso->dxfer_len, atp->bytes_xfered);
|
|
|
|
} else {
|
|
|
|
cto->ct_resid = 0;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, cso->ccb_h.path, "%s: CTIO7[%x] flags %x xfrlen %u offset %u\n", __func__, cto->ct_rxid, cto->ct_flags,
|
|
|
|
cso->dxfer_len, atp->bytes_xfered);
|
|
|
|
atp->state = ATPD_STATE_CTIO;
|
|
|
|
}
|
|
|
|
} else if (IS_FC(isp)) {
|
Major restructuring for swizzling to the request queue and unswizzling from
the response queue. Instead of the ad hoc ISP_SWIZZLE_REQUEST, we now have
a complete set of inline functions in isp_inline.h. Each platform is
responsible for providing just one of a set of ISP_IOX_{GET,PUT}{8,16,32}
macros.
The reason this needs to be done is that we need to have a single set of
functions that will work correctly on multiple architectures for both little
and big endian machines. It also needs to work correctly in the case that
we have the request or response queues in memory that has to be treated
specially (e.g., have ddi_dma_sync called on it for Solaris after we update
it or before we read from it). It also has to handle the SBus cards (for
platforms that have them) which, while on a Big Endian machine, do *not*
require *most* of the request/response queue entry fields to be swizzled
or unswizzled.
One thing that falls out of this is that we no longer build requests in the
request queue itself. Instead, we build the request locally (e.g., on the
stack) and then as part of the swizzling operation, copy it to the request
queue entry we've allocated. I thought long and hard about whether this was
too expensive a change to make as it in a lot of cases requires an extra
copy. On balance, the flexbility is worth it. With any luck, the entry that
we build locally stays in a processor writeback cache (after all, it's only
64 bytes) so that the cost of actually flushing it to the memory area that is
the shared queue with the PCI device is not all that expensive. We may examine
this again and try to get clever in the future to try and avoid copies.
Another change that falls out of this is that MEMORYBARRIER should be taken
a lot more seriously. The macro ISP_ADD_REQUEST does a MEMORYBARRIER on the
entry being added. But there had been many other places this had been missing.
It's now very important that it be done.
Additional changes:
Fix a longstanding buglet of sorts. When we get an entry via isp_getrqentry,
the iptr value that gets returned is the value we intend to eventually plug
into the ISP registers as the entry *one past* the last one we've written-
*not* the current entry we're updating. All along we've been calling sync
functions on the wrong index value. Argh. The 'fix' here is to rename all
'iptr' variables as 'nxti' to remember that this is the 'next' pointer-
not the current pointer.
Devote a single bit to mboxbsy- and set aside bits for output mbox registers
that we need to pick up- we can have at least one command which does not
have any defined output registers (MBOX_EXECUTE_FIRMWARE).
MFC after: 2 weeks
2001-12-11 00:18:45 +00:00
|
|
|
ct2_entry_t *cto = (ct2_entry_t *) local;
|
2000-01-15 01:50:48 +00:00
|
|
|
|
2000-01-03 23:55:30 +00:00
|
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
|
|
|
|
cto->ct_header.rqs_entry_count = 1;
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->ct_header.rqs_seqno = 1;
|
|
|
|
if (ISP_CAP_2KLOGIN(isp) == 0) {
|
2006-05-22 06:49:49 +00:00
|
|
|
((ct2e_entry_t *)cto)->ct_iid = cso->init_id;
|
|
|
|
} else {
|
|
|
|
cto->ct_iid = cso->init_id;
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_CAP_SCCFW(isp) == 0) {
|
2006-05-22 06:49:49 +00:00
|
|
|
cto->ct_lun = ccb->ccb_h.target_lun;
|
|
|
|
}
|
2000-06-18 04:50:26 +00:00
|
|
|
}
|
2001-10-01 03:48:42 +00:00
|
|
|
|
2000-07-18 06:58:28 +00:00
|
|
|
|
2000-01-15 01:50:48 +00:00
|
|
|
cto->ct_rxid = cso->tag_id;
|
|
|
|
if (cso->dxfer_len == 0) {
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->ct_flags |= CT2_FLAG_MODE1 | CT2_NO_DATA | CT2_SENDSTATUS;
|
|
|
|
cto->rsp.m1.ct_scsi_status = cso->scsi_status;
|
|
|
|
cto->ct_resid = atp->orig_datalen - atp->bytes_xfered;
|
|
|
|
if (cto->ct_resid < 0) {
|
|
|
|
cto->rsp.m1.ct_scsi_status |= CT2_DATA_OVER;
|
|
|
|
} else if (cto->ct_resid > 0) {
|
|
|
|
cto->rsp.m1.ct_scsi_status |= CT2_DATA_UNDER;
|
2000-07-18 06:58:28 +00:00
|
|
|
}
|
2000-01-15 01:50:48 +00:00
|
|
|
if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) {
|
|
|
|
int m = min(cso->sense_len, MAXRESPLEN);
|
2009-08-01 01:04:26 +00:00
|
|
|
memcpy(cto->rsp.m1.ct_resp, &cso->sense_data, m);
|
2000-01-15 01:50:48 +00:00
|
|
|
cto->rsp.m1.ct_senselen = m;
|
|
|
|
cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID;
|
2009-08-01 01:04:26 +00:00
|
|
|
} else if (cso->scsi_status == SCSI_STATUS_CHECK_COND) {
|
|
|
|
/*
|
|
|
|
* XXX: DEBUG
|
|
|
|
*/
|
|
|
|
xpt_print(ccb->ccb_h.path, "CHECK CONDITION being sent without associated SENSE DATA for CDB=0x%x\n", atp->cdb0);
|
2000-01-15 01:50:48 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cto->ct_flags |= CT2_FLAG_MODE0;
|
|
|
|
if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
|
|
|
|
cto->ct_flags |= CT2_DATA_IN;
|
|
|
|
} else {
|
|
|
|
cto->ct_flags |= CT2_DATA_OUT;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2002-06-16 05:08:02 +00:00
|
|
|
cto->ct_reloff = atp->bytes_xfered;
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->rsp.m0.ct_xfrlen = cso->dxfer_len;
|
|
|
|
/*
|
|
|
|
* Don't overrun the limits placed on us
|
|
|
|
*/
|
|
|
|
if (atp->bytes_xfered + cso->dxfer_len > atp->orig_datalen) {
|
|
|
|
cso->dxfer_len = atp->orig_datalen - atp->bytes_xfered;
|
|
|
|
}
|
2000-01-15 01:50:48 +00:00
|
|
|
if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) {
|
|
|
|
cto->ct_flags |= CT2_SENDSTATUS;
|
|
|
|
cto->rsp.m0.ct_scsi_status = cso->scsi_status;
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->ct_resid = atp->orig_datalen - (atp->bytes_xfered + cso->dxfer_len);
|
2002-06-16 05:08:02 +00:00
|
|
|
if (cto->ct_resid < 0) {
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->rsp.m0.ct_scsi_status |= CT2_DATA_OVER;
|
2002-06-16 05:08:02 +00:00
|
|
|
} else if (cto->ct_resid > 0) {
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->rsp.m0.ct_scsi_status |= CT2_DATA_UNDER;
|
2002-06-16 05:08:02 +00:00
|
|
|
}
|
2001-10-01 03:48:42 +00:00
|
|
|
} else {
|
|
|
|
atp->last_xframt = cso->dxfer_len;
|
2000-01-04 03:45:49 +00:00
|
|
|
}
|
2000-07-18 06:58:28 +00:00
|
|
|
/*
|
|
|
|
* If we're sending data and status back together,
|
|
|
|
* we can't also send back sense data as well.
|
|
|
|
*/
|
2000-01-15 01:50:48 +00:00
|
|
|
ccb->ccb_h.flags &= ~CAM_SEND_SENSE;
|
|
|
|
}
|
2001-10-01 03:48:42 +00:00
|
|
|
|
2001-03-21 00:46:44 +00:00
|
|
|
if (cto->ct_flags & CT2_SENDSTATUS) {
|
2009-08-01 01:04:26 +00:00
|
|
|
int lvl = (cso->scsi_status)? ISP_LOGTINFO : ISP_LOGTDEBUG0;
|
2001-04-04 21:58:29 +00:00
|
|
|
cto->ct_flags |= CT2_CCINCR;
|
2002-06-16 05:08:02 +00:00
|
|
|
atp->state = ATPD_STATE_LAST_CTIO;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_PATH_PRT(isp, lvl, cso->ccb_h.path, "%s: CTIO2[%x] CDB0=%x scsi status %x flags %x resid %d xfrlen %u offset %u\n", __func__, cto->ct_rxid,
|
|
|
|
atp->cdb0, cto->rsp.m0.ct_scsi_status, cto->ct_flags, cto->ct_resid, cso->dxfer_len, atp->bytes_xfered);
|
2006-04-21 18:30:01 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->ct_resid = 0;
|
2002-06-16 05:08:02 +00:00
|
|
|
atp->state = ATPD_STATE_CTIO;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: CTIO2[%x] flags %x xfrlen %u offset %u\n", __func__, cto->ct_rxid, cto->ct_flags,
|
|
|
|
cso->dxfer_len, atp->bytes_xfered);
|
2006-04-21 18:30:01 +00:00
|
|
|
}
|
2001-04-04 21:58:29 +00:00
|
|
|
cto->ct_timeout = 10;
|
2000-01-03 23:55:30 +00:00
|
|
|
} else {
|
Major restructuring for swizzling to the request queue and unswizzling from
the response queue. Instead of the ad hoc ISP_SWIZZLE_REQUEST, we now have
a complete set of inline functions in isp_inline.h. Each platform is
responsible for providing just one of a set of ISP_IOX_{GET,PUT}{8,16,32}
macros.
The reason this needs to be done is that we need to have a single set of
functions that will work correctly on multiple architectures for both little
and big endian machines. It also needs to work correctly in the case that
we have the request or response queues in memory that has to be treated
specially (e.g., have ddi_dma_sync called on it for Solaris after we update
it or before we read from it). It also has to handle the SBus cards (for
platforms that have them) which, while on a Big Endian machine, do *not*
require *most* of the request/response queue entry fields to be swizzled
or unswizzled.
One thing that falls out of this is that we no longer build requests in the
request queue itself. Instead, we build the request locally (e.g., on the
stack) and then as part of the swizzling operation, copy it to the request
queue entry we've allocated. I thought long and hard about whether this was
too expensive a change to make as it in a lot of cases requires an extra
copy. On balance, the flexbility is worth it. With any luck, the entry that
we build locally stays in a processor writeback cache (after all, it's only
64 bytes) so that the cost of actually flushing it to the memory area that is
the shared queue with the PCI device is not all that expensive. We may examine
this again and try to get clever in the future to try and avoid copies.
Another change that falls out of this is that MEMORYBARRIER should be taken
a lot more seriously. The macro ISP_ADD_REQUEST does a MEMORYBARRIER on the
entry being added. But there had been many other places this had been missing.
It's now very important that it be done.
Additional changes:
Fix a longstanding buglet of sorts. When we get an entry via isp_getrqentry,
the iptr value that gets returned is the value we intend to eventually plug
into the ISP registers as the entry *one past* the last one we've written-
*not* the current entry we're updating. All along we've been calling sync
functions on the wrong index value. Argh. The 'fix' here is to rename all
'iptr' variables as 'nxti' to remember that this is the 'next' pointer-
not the current pointer.
Devote a single bit to mboxbsy- and set aside bits for output mbox registers
that we need to pick up- we can have at least one command which does not
have any defined output registers (MBOX_EXECUTE_FIRMWARE).
MFC after: 2 weeks
2001-12-11 00:18:45 +00:00
|
|
|
ct_entry_t *cto = (ct_entry_t *) local;
|
2000-01-15 01:50:48 +00:00
|
|
|
|
2000-01-03 23:55:30 +00:00
|
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO;
|
|
|
|
cto->ct_header.rqs_entry_count = 1;
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->ct_header.rqs_seqno = 1;
|
2000-01-15 01:50:48 +00:00
|
|
|
cto->ct_iid = cso->init_id;
|
2001-04-04 21:58:29 +00:00
|
|
|
cto->ct_iid |= XS_CHANNEL(ccb) << 7;
|
2000-01-03 23:55:30 +00:00
|
|
|
cto->ct_tgt = ccb->ccb_h.target_id;
|
|
|
|
cto->ct_lun = ccb->ccb_h.target_lun;
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->ct_fwhandle = cso->tag_id >> 16;
|
2001-04-04 21:58:29 +00:00
|
|
|
if (AT_HAS_TAG(cso->tag_id)) {
|
2009-08-01 01:04:26 +00:00
|
|
|
cto->ct_tag_val = cso->tag_id;
|
2000-07-18 06:58:28 +00:00
|
|
|
cto->ct_flags |= CT_TQAE;
|
|
|
|
}
|
|
|
|
if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) {
|
|
|
|
cto->ct_flags |= CT_NODISC;
|
|
|
|
}
|
|
|
|
if (cso->dxfer_len == 0) {
|
2000-01-03 23:55:30 +00:00
|
|
|
cto->ct_flags |= CT_NO_DATA;
|
2000-01-15 01:50:48 +00:00
|
|
|
} else if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
|
|
|
|
cto->ct_flags |= CT_DATA_IN;
|
|
|
|
} else {
|
|
|
|
cto->ct_flags |= CT_DATA_OUT;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2000-07-18 06:58:28 +00:00
|
|
|
if (ccb->ccb_h.flags & CAM_SEND_STATUS) {
|
2001-10-01 03:48:42 +00:00
|
|
|
cto->ct_flags |= CT_SENDSTATUS|CT_CCINCR;
|
2000-01-15 01:50:48 +00:00
|
|
|
cto->ct_scsi_status = cso->scsi_status;
|
|
|
|
cto->ct_resid = cso->resid;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: CTIO[%x] scsi status %x resid %d tag_id %x\n", __func__,
|
|
|
|
cto->ct_fwhandle, cso->scsi_status, cso->resid, cso->tag_id);
|
2000-01-04 03:45:49 +00:00
|
|
|
}
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
ccb->ccb_h.flags &= ~CAM_SEND_SENSE;
|
2001-04-04 21:58:29 +00:00
|
|
|
cto->ct_timeout = 10;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2006-11-02 03:21:32 +00:00
|
|
|
if (isp_save_xs_tgt(isp, ccb, &handle)) {
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_print(ccb->ccb_h.path, "No XFLIST pointers for %s\n", __func__);
|
|
|
|
ccb->ccb_h.status = CAM_REQUEUE_REQ;
|
2006-04-21 18:30:01 +00:00
|
|
|
goto out;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2000-08-01 06:31:44 +00:00
|
|
|
* new things to play with. As with isp_start's usage of DMA setup,
|
2000-01-03 23:55:30 +00:00
|
|
|
* any swizzling is done in the machine dependent layer. Because
|
|
|
|
* of this, we put the request onto the queue area first in native
|
|
|
|
* format.
|
|
|
|
*/
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
ct7_entry_t *cto = (ct7_entry_t *) local;
|
|
|
|
cto->ct_syshandle = handle;
|
|
|
|
} else if (IS_FC(isp)) {
|
2006-11-02 03:21:32 +00:00
|
|
|
ct2_entry_t *cto = (ct2_entry_t *) local;
|
|
|
|
cto->ct_syshandle = handle;
|
|
|
|
} else {
|
|
|
|
ct_entry_t *cto = (ct_entry_t *) local;
|
|
|
|
cto->ct_syshandle = handle;
|
|
|
|
}
|
2001-04-04 21:58:29 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
dmaresult = ISP_DMASETUP(isp, cso, (ispreq_t *) local);
|
|
|
|
if (dmaresult == CMD_QUEUED) {
|
|
|
|
isp->isp_nactive++;
|
2006-04-21 18:30:01 +00:00
|
|
|
ccb->ccb_h.status |= CAM_SIM_QUEUED;
|
2009-08-01 01:04:26 +00:00
|
|
|
rls_lun_statep(isp, tptr);
|
2006-04-21 18:30:01 +00:00
|
|
|
return;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
if (dmaresult == CMD_EAGAIN) {
|
|
|
|
ccb->ccb_h.status = CAM_REQUEUE_REQ;
|
|
|
|
} else {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2006-11-02 03:21:32 +00:00
|
|
|
isp_destroy_tgt_handle(isp, handle);
|
2006-04-21 18:30:01 +00:00
|
|
|
out:
|
2009-08-01 01:04:26 +00:00
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
isp_free_pcmd(isp, ccb);
|
2006-04-21 18:30:01 +00:00
|
|
|
xpt_done(ccb);
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2001-04-04 21:58:29 +00:00
|
|
|
static void
|
|
|
|
isp_refire_putback_atio(void *arg)
|
2000-07-18 06:58:28 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
union ccb *ccb = arg;
|
|
|
|
ispsoftc_t *isp = XS_ISP(ccb);
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
isp_target_putback_atio(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
2001-04-04 21:58:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_target_putback_atio(union ccb *ccb)
|
|
|
|
{
|
2006-04-21 18:30:01 +00:00
|
|
|
ispsoftc_t *isp;
|
2001-04-04 21:58:29 +00:00
|
|
|
struct ccb_scsiio *cso;
|
|
|
|
void *qe;
|
|
|
|
|
|
|
|
isp = XS_ISP(ccb);
|
2000-07-18 06:58:28 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
qe = isp_getrqentry(isp);
|
|
|
|
if (qe == NULL) {
|
|
|
|
xpt_print(ccb->ccb_h.path, rqo, __func__);
|
2001-04-04 21:58:29 +00:00
|
|
|
(void) timeout(isp_refire_putback_atio, ccb, 10);
|
|
|
|
return;
|
2000-07-18 06:58:28 +00:00
|
|
|
}
|
2006-05-22 06:49:49 +00:00
|
|
|
memset(qe, 0, QENTRY_LEN);
|
2001-04-04 21:58:29 +00:00
|
|
|
cso = &ccb->csio;
|
2000-07-18 06:58:28 +00:00
|
|
|
if (IS_FC(isp)) {
|
Major restructuring for swizzling to the request queue and unswizzling from
the response queue. Instead of the ad hoc ISP_SWIZZLE_REQUEST, we now have
a complete set of inline functions in isp_inline.h. Each platform is
responsible for providing just one of a set of ISP_IOX_{GET,PUT}{8,16,32}
macros.
The reason this needs to be done is that we need to have a single set of
functions that will work correctly on multiple architectures for both little
and big endian machines. It also needs to work correctly in the case that
we have the request or response queues in memory that has to be treated
specially (e.g., have ddi_dma_sync called on it for Solaris after we update
it or before we read from it). It also has to handle the SBus cards (for
platforms that have them) which, while on a Big Endian machine, do *not*
require *most* of the request/response queue entry fields to be swizzled
or unswizzled.
One thing that falls out of this is that we no longer build requests in the
request queue itself. Instead, we build the request locally (e.g., on the
stack) and then as part of the swizzling operation, copy it to the request
queue entry we've allocated. I thought long and hard about whether this was
too expensive a change to make as it in a lot of cases requires an extra
copy. On balance, the flexbility is worth it. With any luck, the entry that
we build locally stays in a processor writeback cache (after all, it's only
64 bytes) so that the cost of actually flushing it to the memory area that is
the shared queue with the PCI device is not all that expensive. We may examine
this again and try to get clever in the future to try and avoid copies.
Another change that falls out of this is that MEMORYBARRIER should be taken
a lot more seriously. The macro ISP_ADD_REQUEST does a MEMORYBARRIER on the
entry being added. But there had been many other places this had been missing.
It's now very important that it be done.
Additional changes:
Fix a longstanding buglet of sorts. When we get an entry via isp_getrqentry,
the iptr value that gets returned is the value we intend to eventually plug
into the ISP registers as the entry *one past* the last one we've written-
*not* the current entry we're updating. All along we've been calling sync
functions on the wrong index value. Argh. The 'fix' here is to rename all
'iptr' variables as 'nxti' to remember that this is the 'next' pointer-
not the current pointer.
Devote a single bit to mboxbsy- and set aside bits for output mbox registers
that we need to pick up- we can have at least one command which does not
have any defined output registers (MBOX_EXECUTE_FIRMWARE).
MFC after: 2 weeks
2001-12-11 00:18:45 +00:00
|
|
|
at2_entry_t local, *at = &local;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_MEMZERO(at, sizeof (at2_entry_t));
|
2000-07-18 06:58:28 +00:00
|
|
|
at->at_header.rqs_entry_type = RQSTYPE_ATIO2;
|
|
|
|
at->at_header.rqs_entry_count = 1;
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_CAP_SCCFW(isp)) {
|
2001-04-04 21:58:29 +00:00
|
|
|
at->at_scclun = (uint16_t) ccb->ccb_h.target_lun;
|
2000-07-18 06:58:28 +00:00
|
|
|
} else {
|
2001-04-04 21:58:29 +00:00
|
|
|
at->at_lun = (uint8_t) ccb->ccb_h.target_lun;
|
2000-07-18 06:58:28 +00:00
|
|
|
}
|
|
|
|
at->at_status = CT_OK;
|
2001-04-04 21:58:29 +00:00
|
|
|
at->at_rxid = cso->tag_id;
|
2002-06-16 05:08:02 +00:00
|
|
|
at->at_iid = cso->ccb_h.target_id;
|
Major restructuring for swizzling to the request queue and unswizzling from
the response queue. Instead of the ad hoc ISP_SWIZZLE_REQUEST, we now have
a complete set of inline functions in isp_inline.h. Each platform is
responsible for providing just one of a set of ISP_IOX_{GET,PUT}{8,16,32}
macros.
The reason this needs to be done is that we need to have a single set of
functions that will work correctly on multiple architectures for both little
and big endian machines. It also needs to work correctly in the case that
we have the request or response queues in memory that has to be treated
specially (e.g., have ddi_dma_sync called on it for Solaris after we update
it or before we read from it). It also has to handle the SBus cards (for
platforms that have them) which, while on a Big Endian machine, do *not*
require *most* of the request/response queue entry fields to be swizzled
or unswizzled.
One thing that falls out of this is that we no longer build requests in the
request queue itself. Instead, we build the request locally (e.g., on the
stack) and then as part of the swizzling operation, copy it to the request
queue entry we've allocated. I thought long and hard about whether this was
too expensive a change to make as it in a lot of cases requires an extra
copy. On balance, the flexbility is worth it. With any luck, the entry that
we build locally stays in a processor writeback cache (after all, it's only
64 bytes) so that the cost of actually flushing it to the memory area that is
the shared queue with the PCI device is not all that expensive. We may examine
this again and try to get clever in the future to try and avoid copies.
Another change that falls out of this is that MEMORYBARRIER should be taken
a lot more seriously. The macro ISP_ADD_REQUEST does a MEMORYBARRIER on the
entry being added. But there had been many other places this had been missing.
It's now very important that it be done.
Additional changes:
Fix a longstanding buglet of sorts. When we get an entry via isp_getrqentry,
the iptr value that gets returned is the value we intend to eventually plug
into the ISP registers as the entry *one past* the last one we've written-
*not* the current entry we're updating. All along we've been calling sync
functions on the wrong index value. Argh. The 'fix' here is to rename all
'iptr' variables as 'nxti' to remember that this is the 'next' pointer-
not the current pointer.
Devote a single bit to mboxbsy- and set aside bits for output mbox registers
that we need to pick up- we can have at least one command which does not
have any defined output registers (MBOX_EXECUTE_FIRMWARE).
MFC after: 2 weeks
2001-12-11 00:18:45 +00:00
|
|
|
isp_put_atio2(isp, at, qe);
|
2000-07-18 06:58:28 +00:00
|
|
|
} else {
|
Major restructuring for swizzling to the request queue and unswizzling from
the response queue. Instead of the ad hoc ISP_SWIZZLE_REQUEST, we now have
a complete set of inline functions in isp_inline.h. Each platform is
responsible for providing just one of a set of ISP_IOX_{GET,PUT}{8,16,32}
macros.
The reason this needs to be done is that we need to have a single set of
functions that will work correctly on multiple architectures for both little
and big endian machines. It also needs to work correctly in the case that
we have the request or response queues in memory that has to be treated
specially (e.g., have ddi_dma_sync called on it for Solaris after we update
it or before we read from it). It also has to handle the SBus cards (for
platforms that have them) which, while on a Big Endian machine, do *not*
require *most* of the request/response queue entry fields to be swizzled
or unswizzled.
One thing that falls out of this is that we no longer build requests in the
request queue itself. Instead, we build the request locally (e.g., on the
stack) and then as part of the swizzling operation, copy it to the request
queue entry we've allocated. I thought long and hard about whether this was
too expensive a change to make as it in a lot of cases requires an extra
copy. On balance, the flexbility is worth it. With any luck, the entry that
we build locally stays in a processor writeback cache (after all, it's only
64 bytes) so that the cost of actually flushing it to the memory area that is
the shared queue with the PCI device is not all that expensive. We may examine
this again and try to get clever in the future to try and avoid copies.
Another change that falls out of this is that MEMORYBARRIER should be taken
a lot more seriously. The macro ISP_ADD_REQUEST does a MEMORYBARRIER on the
entry being added. But there had been many other places this had been missing.
It's now very important that it be done.
Additional changes:
Fix a longstanding buglet of sorts. When we get an entry via isp_getrqentry,
the iptr value that gets returned is the value we intend to eventually plug
into the ISP registers as the entry *one past* the last one we've written-
*not* the current entry we're updating. All along we've been calling sync
functions on the wrong index value. Argh. The 'fix' here is to rename all
'iptr' variables as 'nxti' to remember that this is the 'next' pointer-
not the current pointer.
Devote a single bit to mboxbsy- and set aside bits for output mbox registers
that we need to pick up- we can have at least one command which does not
have any defined output registers (MBOX_EXECUTE_FIRMWARE).
MFC after: 2 weeks
2001-12-11 00:18:45 +00:00
|
|
|
at_entry_t local, *at = &local;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_MEMZERO(at, sizeof (at_entry_t));
|
2000-07-18 06:58:28 +00:00
|
|
|
at->at_header.rqs_entry_type = RQSTYPE_ATIO;
|
|
|
|
at->at_header.rqs_entry_count = 1;
|
2001-04-04 21:58:29 +00:00
|
|
|
at->at_iid = cso->init_id;
|
|
|
|
at->at_iid |= XS_CHANNEL(ccb) << 7;
|
|
|
|
at->at_tgt = cso->ccb_h.target_id;
|
|
|
|
at->at_lun = cso->ccb_h.target_lun;
|
2000-07-18 06:58:28 +00:00
|
|
|
at->at_status = CT_OK;
|
2001-04-04 21:58:29 +00:00
|
|
|
at->at_tag_val = AT_GET_TAG(cso->tag_id);
|
|
|
|
at->at_handle = AT_GET_HANDLE(cso->tag_id);
|
Major restructuring for swizzling to the request queue and unswizzling from
the response queue. Instead of the ad hoc ISP_SWIZZLE_REQUEST, we now have
a complete set of inline functions in isp_inline.h. Each platform is
responsible for providing just one of a set of ISP_IOX_{GET,PUT}{8,16,32}
macros.
The reason this needs to be done is that we need to have a single set of
functions that will work correctly on multiple architectures for both little
and big endian machines. It also needs to work correctly in the case that
we have the request or response queues in memory that has to be treated
specially (e.g., have ddi_dma_sync called on it for Solaris after we update
it or before we read from it). It also has to handle the SBus cards (for
platforms that have them) which, while on a Big Endian machine, do *not*
require *most* of the request/response queue entry fields to be swizzled
or unswizzled.
One thing that falls out of this is that we no longer build requests in the
request queue itself. Instead, we build the request locally (e.g., on the
stack) and then as part of the swizzling operation, copy it to the request
queue entry we've allocated. I thought long and hard about whether this was
too expensive a change to make as it in a lot of cases requires an extra
copy. On balance, the flexbility is worth it. With any luck, the entry that
we build locally stays in a processor writeback cache (after all, it's only
64 bytes) so that the cost of actually flushing it to the memory area that is
the shared queue with the PCI device is not all that expensive. We may examine
this again and try to get clever in the future to try and avoid copies.
Another change that falls out of this is that MEMORYBARRIER should be taken
a lot more seriously. The macro ISP_ADD_REQUEST does a MEMORYBARRIER on the
entry being added. But there had been many other places this had been missing.
It's now very important that it be done.
Additional changes:
Fix a longstanding buglet of sorts. When we get an entry via isp_getrqentry,
the iptr value that gets returned is the value we intend to eventually plug
into the ISP registers as the entry *one past* the last one we've written-
*not* the current entry we're updating. All along we've been calling sync
functions on the wrong index value. Argh. The 'fix' here is to rename all
'iptr' variables as 'nxti' to remember that this is the 'next' pointer-
not the current pointer.
Devote a single bit to mboxbsy- and set aside bits for output mbox registers
that we need to pick up- we can have at least one command which does not
have any defined output registers (MBOX_EXECUTE_FIRMWARE).
MFC after: 2 weeks
2001-12-11 00:18:45 +00:00
|
|
|
isp_put_atio(isp, at, qe);
|
2000-07-18 06:58:28 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_TDQE(isp, "isp_target_putback_atio", isp->isp_reqidx, qe);
|
|
|
|
ISP_SYNC_REQUEST(isp);
|
2001-04-04 21:58:29 +00:00
|
|
|
isp_complete_ctio(ccb);
|
2000-07-18 06:58:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2001-04-04 21:58:29 +00:00
|
|
|
isp_complete_ctio(union ccb *ccb)
|
2000-07-18 06:58:28 +00:00
|
|
|
{
|
2001-04-04 21:58:29 +00:00
|
|
|
if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) {
|
|
|
|
ccb->ccb_h.status |= CAM_REQ_CMP;
|
2000-07-18 06:58:28 +00:00
|
|
|
}
|
2001-04-04 21:58:29 +00:00
|
|
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_free_pcmd(XS_ISP(ccb), ccb);
|
2001-04-04 21:58:29 +00:00
|
|
|
xpt_done(ccb);
|
2000-07-18 06:58:28 +00:00
|
|
|
}
|
|
|
|
|
2000-01-03 23:55:30 +00:00
|
|
|
/*
|
|
|
|
* Handle ATIO stuff that the generic code can't.
|
|
|
|
* This means handling CDBs.
|
|
|
|
*/
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
2006-04-21 18:30:01 +00:00
|
|
|
isp_handle_platform_atio(ispsoftc_t *isp, at_entry_t *aep)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
|
|
|
tstate_t *tptr;
|
2009-08-01 01:04:26 +00:00
|
|
|
int status, bus;
|
2000-01-03 23:55:30 +00:00
|
|
|
struct ccb_accept_tio *atiop;
|
2009-08-01 01:04:26 +00:00
|
|
|
atio_private_data_t *atp;
|
2000-01-03 23:55:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
* we're still connected on the SCSI bus.
|
2000-01-03 23:55:30 +00:00
|
|
|
*/
|
|
|
|
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 for CAM.
|
|
|
|
*/
|
2001-03-01 02:14:54 +00:00
|
|
|
isp_prt(isp, ISP_LOGWARN, "PHASE ERROR");
|
2000-01-03 23:55:30 +00:00
|
|
|
isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
|
2009-08-01 01:04:26 +00:00
|
|
|
return;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
if ((status & ~QLTM_SVALID) != AT_CDB) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGWARN, "bad atio (0x%x) leaked to platform", status);
|
2000-01-03 23:55:30 +00:00
|
|
|
isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
|
2009-08-01 01:04:26 +00:00
|
|
|
return;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
bus = GET_BUS_VAL(aep->at_iid);
|
2001-04-04 21:58:29 +00:00
|
|
|
tptr = get_lun_statep(isp, bus, aep->at_lun);
|
2000-01-03 23:55:30 +00:00
|
|
|
if (tptr == NULL) {
|
2001-04-04 21:58:29 +00:00
|
|
|
tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD);
|
2004-02-07 03:47:33 +00:00
|
|
|
if (tptr == NULL) {
|
2006-04-21 18:46:35 +00:00
|
|
|
/*
|
|
|
|
* Because we can't autofeed sense data back with
|
|
|
|
* a command for parallel SCSI, we can't give back
|
|
|
|
* a CHECK CONDITION. We'll give back a BUSY status
|
|
|
|
* instead. This works out okay because the only
|
|
|
|
* time we should, in fact, get this, is in the
|
|
|
|
* case that somebody configured us without the
|
|
|
|
* blackhole driver, so they get what they deserve.
|
|
|
|
*/
|
|
|
|
isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
|
2009-08-01 01:04:26 +00:00
|
|
|
return;
|
2004-02-07 03:47:33 +00:00
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
atp = isp_get_atpd(isp, tptr, 0);
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios);
|
2009-08-01 01:04:26 +00:00
|
|
|
if (atiop == NULL || atp == NULL) {
|
2000-01-03 23:55:30 +00:00
|
|
|
/*
|
|
|
|
* Because we can't autofeed sense data back with
|
|
|
|
* a command for parallel SCSI, we can't give back
|
|
|
|
* a CHECK CONDITION. We'll give back a QUEUE FULL status
|
|
|
|
* instead. This works out okay because the only time we
|
|
|
|
* should, in fact, get this, is in the case that we've
|
|
|
|
* run out of ATIOS.
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_print(tptr->owner, "no %s for lun %d from initiator %d\n", (atp == NULL && atiop == NULL)? "ATIOs *or* ATPS" :
|
|
|
|
((atp == NULL)? "ATPs" : "ATIOs"), aep->at_lun, aep->at_iid);
|
|
|
|
isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
|
|
|
|
if (atp) {
|
|
|
|
isp_put_atpd(isp, tptr, atp);
|
|
|
|
}
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
rls_lun_statep(isp, tptr);
|
2009-08-01 01:04:26 +00:00
|
|
|
return;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
atp->tag = aep->at_tag_val;
|
|
|
|
if (atp->tag == 0) {
|
|
|
|
atp->tag = ~0;
|
|
|
|
}
|
|
|
|
atp->state = ATPD_STATE_ATIO;
|
2000-01-03 23:55:30 +00:00
|
|
|
SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle);
|
2004-02-07 03:47:33 +00:00
|
|
|
tptr->atio_count--;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count);
|
|
|
|
atiop->ccb_h.target_id = aep->at_tgt;
|
|
|
|
atiop->ccb_h.target_lun = aep->at_lun;
|
2000-01-03 23:55:30 +00:00
|
|
|
if (aep->at_flags & AT_NODISC) {
|
2000-07-18 06:58:28 +00:00
|
|
|
atiop->ccb_h.flags = CAM_DIS_DISCONNECT;
|
|
|
|
} else {
|
|
|
|
atiop->ccb_h.flags = 0;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2000-07-18 06:58:28 +00:00
|
|
|
if (status & QLTM_SVALID) {
|
|
|
|
size_t amt = imin(QLTM_SENSELEN, sizeof (atiop->sense_data));
|
|
|
|
atiop->sense_len = amt;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_MEMCPY(&atiop->sense_data, aep->at_sense, amt);
|
2000-07-18 06:58:28 +00:00
|
|
|
} else {
|
|
|
|
atiop->sense_len = 0;
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
atiop->init_id = GET_IID_VAL(aep->at_iid);
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop->cdb_len = aep->at_cdblen;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen);
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop->ccb_h.status = CAM_CDB_RECVD;
|
2001-04-04 21:58:29 +00:00
|
|
|
/*
|
|
|
|
* Construct a tag 'id' based upon tag value (which may be 0..255)
|
|
|
|
* and the handle (which we have to preserve).
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
atiop->tag_id = atp->tag;
|
2001-04-04 21:58:29 +00:00
|
|
|
if (aep->at_flags & AT_TQAE) {
|
|
|
|
atiop->tag_action = aep->at_tag_type;
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop->ccb_h.status |= CAM_TAG_ACTION_VALID;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
atp->orig_datalen = 0;
|
|
|
|
atp->bytes_xfered = 0;
|
|
|
|
atp->last_xframt = 0;
|
|
|
|
atp->lun = aep->at_lun;
|
|
|
|
atp->nphdl = aep->at_iid;
|
|
|
|
atp->portid = PORT_NONE;
|
|
|
|
atp->oxid = 0;
|
|
|
|
atp->cdb0 = atiop->cdb_io.cdb_bytes[0];
|
|
|
|
atp->tattr = aep->at_tag_type;
|
|
|
|
atp->state = ATPD_STATE_CAM;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO[%x] CDB=0x%x lun %d\n", aep->at_tag_val, atp->cdb0, atp->lun);
|
2000-01-03 23:55:30 +00:00
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
2006-04-21 18:30:01 +00:00
|
|
|
isp_handle_platform_atio2(ispsoftc_t *isp, at2_entry_t *aep)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
2000-01-04 03:45:49 +00:00
|
|
|
lun_id_t lun;
|
2009-08-01 01:04:26 +00:00
|
|
|
fcportdb_t *lp;
|
2000-01-03 23:55:30 +00:00
|
|
|
tstate_t *tptr;
|
|
|
|
struct ccb_accept_tio *atiop;
|
2009-08-01 01:04:26 +00:00
|
|
|
uint16_t nphdl;
|
|
|
|
atio_private_data_t *atp = NULL;
|
|
|
|
inot_private_data_t *ntp;
|
2000-01-03 23:55:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGWARN, "bogus atio (0x%x) leaked to platform", aep->at_status);
|
2000-01-03 23:55:30 +00:00
|
|
|
isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
|
2009-08-01 01:04:26 +00:00
|
|
|
return;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_CAP_SCCFW(isp)) {
|
2000-06-18 04:50:26 +00:00
|
|
|
lun = aep->at_scclun;
|
|
|
|
} else {
|
|
|
|
lun = aep->at_lun;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_CAP_2KLOGIN(isp)) {
|
|
|
|
nphdl = ((at2e_entry_t *)aep)->at_iid;
|
|
|
|
} else {
|
|
|
|
nphdl = aep->at_iid;
|
|
|
|
}
|
2001-04-04 21:58:29 +00:00
|
|
|
tptr = get_lun_statep(isp, 0, lun);
|
2000-01-03 23:55:30 +00:00
|
|
|
if (tptr == NULL) {
|
2001-04-04 21:58:29 +00:00
|
|
|
tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD);
|
2004-02-07 03:47:33 +00:00
|
|
|
if (tptr == NULL) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] no state pointer for lun %d", aep->at_rxid, lun);
|
|
|
|
isp_endcmd(isp, aep, SCSI_STATUS_CHECK_COND | ECMD_SVALID | (0x5 << 12) | (0x25 << 16), 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start any commands pending resources first.
|
|
|
|
*/
|
|
|
|
if (tptr->restart_queue) {
|
|
|
|
inot_private_data_t *restart_queue = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = NULL;
|
|
|
|
while (restart_queue) {
|
|
|
|
ntp = restart_queue;
|
|
|
|
restart_queue = ntp->rd.nt.nt_hba;
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at2_entry_t *)ntp->rd.data)->at_rxid);
|
|
|
|
isp_handle_platform_atio2(isp, (at2_entry_t *) ntp->rd.data);
|
|
|
|
isp_put_ntpd(isp, tptr, ntp);
|
|
|
|
/*
|
|
|
|
* If a recursion caused the restart queue to start to fill again,
|
|
|
|
* stop and splice the new list on top of the old list and restore
|
|
|
|
* it and go to noresrc.
|
|
|
|
*/
|
|
|
|
if (tptr->restart_queue) {
|
|
|
|
ntp = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = restart_queue;
|
|
|
|
while (restart_queue->rd.nt.nt_hba) {
|
|
|
|
restart_queue = restart_queue->rd.nt.nt_hba;
|
|
|
|
}
|
|
|
|
restart_queue->rd.nt.nt_hba = ntp;
|
|
|
|
goto noresrc;
|
|
|
|
}
|
2004-02-07 03:47:33 +00:00
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios);
|
2009-08-01 01:04:26 +00:00
|
|
|
if (atiop == NULL) {
|
|
|
|
goto noresrc;
|
|
|
|
}
|
2004-02-07 03:47:33 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
atp = isp_get_atpd(isp, tptr, 0);
|
|
|
|
if (atp == NULL) {
|
|
|
|
goto noresrc;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
atp->tag = aep->at_rxid;
|
2002-06-16 05:08:02 +00:00
|
|
|
atp->state = ATPD_STATE_ATIO;
|
2000-01-03 23:55:30 +00:00
|
|
|
SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle);
|
2002-06-16 05:08:02 +00:00
|
|
|
tptr->atio_count--;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count);
|
|
|
|
atiop->ccb_h.target_id = FCPARAM(isp, 0)->isp_loopid;
|
|
|
|
atiop->ccb_h.target_lun = lun;
|
2000-07-18 06:58:28 +00:00
|
|
|
|
2001-02-27 00:14:39 +00:00
|
|
|
/*
|
|
|
|
* We don't get 'suggested' sense data as we do with SCSI cards.
|
|
|
|
*/
|
|
|
|
atiop->sense_len = 0;
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_CAP_2KLOGIN(isp)) {
|
|
|
|
/*
|
|
|
|
* NB: We could not possibly have 2K logins if we
|
|
|
|
* NB: also did not have SCC FW.
|
|
|
|
*/
|
|
|
|
atiop->init_id = ((at2e_entry_t *)aep)->at_iid;
|
|
|
|
} else {
|
|
|
|
atiop->init_id = aep->at_iid;
|
|
|
|
}
|
2000-07-18 06:58:28 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* If we're not in the port database, add ourselves.
|
|
|
|
*/
|
|
|
|
if (!IS_2100(isp) && isp_find_pdb_by_loopid(isp, 0, atiop->init_id, &lp) == 0) {
|
|
|
|
uint64_t 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);
|
|
|
|
/*
|
|
|
|
* However, make sure we delete ourselves if otherwise
|
|
|
|
* we were there but at a different loop id.
|
|
|
|
*/
|
|
|
|
if (isp_find_pdb_by_wwn(isp, 0, iid, &lp)) {
|
|
|
|
isp_del_wwn_entry(isp, 0, iid, lp->handle, lp->portid);
|
|
|
|
}
|
|
|
|
isp_add_wwn_entry(isp, 0, iid, atiop->init_id, PORT_ANY);
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop->cdb_len = ATIO2_CDBLEN;
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN);
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop->ccb_h.status = CAM_CDB_RECVD;
|
2009-08-01 01:04:26 +00:00
|
|
|
atiop->tag_id = atp->tag;
|
2000-01-03 23:55:30 +00:00
|
|
|
switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) {
|
|
|
|
case ATIO2_TC_ATTR_SIMPLEQ:
|
2009-08-01 01:04:26 +00:00
|
|
|
atiop->ccb_h.flags = CAM_TAG_ACTION_VALID;
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop->tag_action = MSG_SIMPLE_Q_TAG;
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case ATIO2_TC_ATTR_HEADOFQ:
|
|
|
|
atiop->ccb_h.flags = CAM_TAG_ACTION_VALID;
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop->tag_action = MSG_HEAD_OF_Q_TAG;
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case ATIO2_TC_ATTR_ORDERED:
|
|
|
|
atiop->ccb_h.flags = CAM_TAG_ACTION_VALID;
|
2000-01-03 23:55:30 +00:00
|
|
|
atiop->tag_action = MSG_ORDERED_Q_TAG;
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case ATIO2_TC_ATTR_ACAQ: /* ?? */
|
2000-01-03 23:55:30 +00:00
|
|
|
case ATIO2_TC_ATTR_UNTAGGED:
|
|
|
|
default:
|
|
|
|
atiop->tag_action = 0;
|
|
|
|
break;
|
|
|
|
}
|
2000-07-18 06:58:28 +00:00
|
|
|
|
2001-10-01 03:48:42 +00:00
|
|
|
atp->orig_datalen = aep->at_datalen;
|
|
|
|
atp->bytes_xfered = 0;
|
2009-08-01 01:04:26 +00:00
|
|
|
atp->last_xframt = 0;
|
|
|
|
atp->lun = lun;
|
|
|
|
atp->nphdl = atiop->init_id;
|
|
|
|
atp->sid = PORT_ANY;
|
|
|
|
atp->oxid = aep->at_oxid;
|
|
|
|
atp->cdb0 = aep->at_cdb[0];
|
|
|
|
atp->tattr = aep->at_taskflags & ATIO2_TC_ATTR_MASK;
|
2002-06-16 05:08:02 +00:00
|
|
|
atp->state = ATPD_STATE_CAM;
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_done((union ccb *)atiop);
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO2[%x] CDB=0x%x lun %d datalen %u\n", aep->at_rxid, atp->cdb0, lun, atp->orig_datalen);
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
return;
|
|
|
|
noresrc:
|
|
|
|
if (atp) {
|
|
|
|
isp_put_atpd(isp, tptr, atp);
|
|
|
|
}
|
|
|
|
ntp = isp_get_ntpd(isp, tptr);
|
|
|
|
if (ntp == NULL) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
isp_endcmd(isp, aep, nphdl, 0, SCSI_STATUS_BUSY, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memcpy(ntp->rd.data, aep, QENTRY_LEN);
|
|
|
|
ntp->rd.nt.nt_hba = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = ntp;
|
2000-01-03 23:55:30 +00:00
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
|
|
|
isp_handle_platform_atio7(ispsoftc_t *isp, at7_entry_t *aep)
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
int cdbxlen;
|
|
|
|
uint16_t lun, chan, nphdl = NIL_HANDLE;
|
|
|
|
uint32_t did, sid;
|
|
|
|
uint64_t wwn = INI_NONE;
|
|
|
|
fcportdb_t *lp;
|
|
|
|
tstate_t *tptr;
|
|
|
|
struct ccb_accept_tio *atiop;
|
|
|
|
atio_private_data_t *atp = NULL;
|
|
|
|
inot_private_data_t *ntp;
|
|
|
|
|
|
|
|
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];
|
2000-01-03 23:55:30 +00:00
|
|
|
|
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* 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.
|
2000-01-03 23:55:30 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_CAP_MULTI_ID(isp)) {
|
|
|
|
/*
|
|
|
|
* Find the right channel based upon D_ID
|
|
|
|
*/
|
|
|
|
isp_find_chan_by_did(isp, did, &chan);
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (chan == ISP_NOCHAN) {
|
|
|
|
NANOTIME_T now;
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* If we don't recognizer our own D_DID, terminate the exchange, unless we're within 2 seconds of startup
|
|
|
|
* It's a bit tricky here as we need to stash this command *somewhere*.
|
|
|
|
*/
|
|
|
|
GET_NANOTIME(&now);
|
|
|
|
if (NANOTIME_SUB(&isp->isp_init_time, &now) > 2000000000ULL) {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel- dropping", __func__, aep->at_rxid, did);
|
|
|
|
isp_endcmd(isp, aep, NIL_HANDLE, ISP_NOCHAN, ECMD_TERMINATE, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
tptr = get_lun_statep(isp, 0, 0);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel and no tptr- dropping", __func__, aep->at_rxid, did);
|
|
|
|
isp_endcmd(isp, aep, NIL_HANDLE, ISP_NOCHAN, ECMD_TERMINATE, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel- deferring", __func__, aep->at_rxid, did);
|
|
|
|
goto noresrc;
|
2002-06-16 05:08:02 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: [RX_ID 0x%x] D_ID 0x%06x found on Chan %d for S_ID 0x%06x", __func__, aep->at_rxid, did, chan, sid);
|
2000-01-03 23:55:30 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
chan = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the PDB entry for this initiator
|
|
|
|
*/
|
|
|
|
if (isp_find_pdb_by_sid(isp, chan, sid, &lp) == 0) {
|
|
|
|
/*
|
|
|
|
* If we're not in the port database terminate the exchange.
|
|
|
|
*/
|
|
|
|
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",
|
|
|
|
__func__, aep->at_rxid, did, chan, sid);
|
|
|
|
isp_endcmd(isp, aep, NIL_HANDLE, chan, ECMD_TERMINATE, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nphdl = lp->handle;
|
|
|
|
wwn = lp->port_wwn;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the tstate pointer
|
|
|
|
*/
|
|
|
|
tptr = get_lun_statep(isp, chan, lun);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
tptr = get_lun_statep(isp, chan, CAM_LUN_WILDCARD);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] no state pointer for lun %d or wildcard", aep->at_rxid, lun);
|
|
|
|
isp_endcmd(isp, aep, nphdl, chan, SCSI_STATUS_CHECK_COND | ECMD_SVALID | (0x5 << 12) | (0x25 << 16), 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start any commands pending resources first.
|
|
|
|
*/
|
|
|
|
if (tptr->restart_queue) {
|
|
|
|
inot_private_data_t *restart_queue = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = NULL;
|
|
|
|
while (restart_queue) {
|
|
|
|
ntp = restart_queue;
|
|
|
|
restart_queue = ntp->rd.nt.nt_hba;
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at7_entry_t *)ntp->rd.data)->at_rxid);
|
|
|
|
isp_handle_platform_atio7(isp, (at7_entry_t *) ntp->rd.data);
|
|
|
|
isp_put_ntpd(isp, tptr, ntp);
|
|
|
|
/*
|
|
|
|
* If a recursion caused the restart queue to start to fill again,
|
|
|
|
* stop and splice the new list on top of the old list and restore
|
|
|
|
* it and go to noresrc.
|
|
|
|
*/
|
|
|
|
if (tptr->restart_queue) {
|
|
|
|
if (restart_queue) {
|
|
|
|
ntp = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = restart_queue;
|
|
|
|
while (restart_queue->rd.nt.nt_hba) {
|
|
|
|
restart_queue = restart_queue->rd.nt.nt_hba;
|
|
|
|
}
|
|
|
|
restart_queue->rd.nt.nt_hba = ntp;
|
|
|
|
}
|
|
|
|
goto noresrc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the f/w is out of resources, just send a BUSY status back.
|
|
|
|
*/
|
|
|
|
if (aep->at_rxid == AT7_NORESRC_RXID) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
isp_endcmd(isp, aep, nphdl, chan, SCSI_BUSY, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're out of resources, just send a BUSY status back.
|
|
|
|
*/
|
|
|
|
atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios);
|
|
|
|
if (atiop == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] out of atios", aep->at_rxid);
|
|
|
|
goto noresrc;
|
|
|
|
}
|
|
|
|
|
|
|
|
atp = isp_get_atpd(isp, tptr, 0);
|
|
|
|
if (atp == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] out of atps", aep->at_rxid);
|
|
|
|
goto noresrc;
|
|
|
|
}
|
|
|
|
if (isp_get_atpd(isp, tptr, aep->at_rxid)) {
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] tag wraparound in isp_handle_platforms_atio7 (N-Port Handle 0x%04x S_ID 0x%04x OX_ID 0x%04x)\n",
|
|
|
|
aep->at_rxid, nphdl, sid, aep->at_hdr.ox_id);
|
|
|
|
/*
|
|
|
|
* It's not a "no resource" condition- but we can treat it like one
|
|
|
|
*/
|
|
|
|
goto noresrc;
|
|
|
|
}
|
|
|
|
|
|
|
|
atp->tag = aep->at_rxid;
|
|
|
|
atp->state = ATPD_STATE_ATIO;
|
|
|
|
SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle);
|
|
|
|
tptr->atio_count--;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count);
|
|
|
|
atiop->init_id = nphdl;
|
|
|
|
atiop->ccb_h.target_id = FCPARAM(isp, chan)->isp_loopid;
|
|
|
|
atiop->ccb_h.target_lun = lun;
|
|
|
|
atiop->sense_len = 0;
|
|
|
|
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);
|
|
|
|
ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cmnd.cdb_dl.sf.fcp_cmnd_cdb, cdbxlen);
|
|
|
|
atiop->cdb_len = cdbxlen;
|
|
|
|
atiop->ccb_h.status = CAM_CDB_RECVD;
|
|
|
|
atiop->tag_id = atp->tag;
|
|
|
|
switch (aep->at_cmnd.fcp_cmnd_task_attribute & FCP_CMND_TASK_ATTR_MASK) {
|
|
|
|
case FCP_CMND_TASK_ATTR_SIMPLE:
|
|
|
|
atiop->ccb_h.flags = CAM_TAG_ACTION_VALID;
|
|
|
|
atiop->tag_action = MSG_SIMPLE_Q_TAG;
|
|
|
|
break;
|
|
|
|
case FCP_CMND_TASK_ATTR_HEAD:
|
|
|
|
atiop->ccb_h.flags = CAM_TAG_ACTION_VALID;
|
|
|
|
atiop->tag_action = MSG_HEAD_OF_Q_TAG;
|
|
|
|
break;
|
|
|
|
case FCP_CMND_TASK_ATTR_ORDERED:
|
|
|
|
atiop->ccb_h.flags = CAM_TAG_ACTION_VALID;
|
|
|
|
atiop->tag_action = MSG_ORDERED_Q_TAG;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case FCP_CMND_TASK_ATTR_ACA:
|
|
|
|
case FCP_CMND_TASK_ATTR_UNTAGGED:
|
|
|
|
atiop->tag_action = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
atp->orig_datalen = aep->at_cmnd.cdb_dl.sf.fcp_cmnd_dl;
|
|
|
|
atp->bytes_xfered = 0;
|
|
|
|
atp->last_xframt = 0;
|
|
|
|
atp->lun = lun;
|
|
|
|
atp->nphdl = nphdl;
|
|
|
|
atp->portid = sid;
|
|
|
|
atp->oxid = aep->at_hdr.ox_id;
|
|
|
|
atp->cdb0 = atiop->cdb_io.cdb_bytes[0];
|
|
|
|
atp->tattr = aep->at_cmnd.fcp_cmnd_task_attribute & FCP_CMND_TASK_ATTR_MASK;
|
|
|
|
atp->state = ATPD_STATE_CAM;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO7[%x] CDB=0x%x lun %d datalen %u\n", aep->at_rxid, atp->cdb0, lun, atp->orig_datalen);
|
|
|
|
xpt_done((union ccb *)atiop);
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
return;
|
|
|
|
noresrc:
|
|
|
|
if (atp) {
|
|
|
|
isp_put_atpd(isp, tptr, atp);
|
|
|
|
}
|
|
|
|
ntp = isp_get_ntpd(isp, tptr);
|
|
|
|
if (ntp == NULL) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
isp_endcmd(isp, aep, nphdl, chan, SCSI_STATUS_BUSY, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memcpy(ntp->rd.data, aep, QENTRY_LEN);
|
|
|
|
ntp->rd.nt.nt_hba = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = ntp;
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_handle_platform_ctio(ispsoftc_t *isp, void *arg)
|
|
|
|
{
|
|
|
|
union ccb *ccb;
|
|
|
|
int sentstatus, ok, notify_cam, resid = 0;
|
|
|
|
tstate_t *tptr = NULL;
|
|
|
|
atio_private_data_t *atp = NULL;
|
|
|
|
int bus;
|
|
|
|
uint32_t tval, handle;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CTIO, CTIO2 and CTIO7 are close enough....
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (IS_SCSI(isp)) {
|
|
|
|
handle = ((ct_entry_t *)arg)->ct_syshandle;
|
|
|
|
} else {
|
|
|
|
handle = ((ct2_entry_t *)arg)->ct_syshandle;
|
|
|
|
}
|
|
|
|
ccb = isp_find_xs_tgt(isp, handle);
|
|
|
|
if (ccb == NULL) {
|
|
|
|
isp_print_bytes(isp, "null ccb in isp_handle_platform_ctio", QENTRY_LEN, arg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
isp_destroy_tgt_handle(isp, handle);
|
|
|
|
bus = XS_CHANNEL(ccb);
|
|
|
|
tptr = get_lun_statep(isp, bus, XS_LUN(ccb));
|
|
|
|
if (tptr == NULL) {
|
|
|
|
tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD);
|
|
|
|
}
|
|
|
|
KASSERT((tptr != NULL), ("cannot get state pointer"));
|
|
|
|
if (isp->isp_nactive) {
|
|
|
|
isp->isp_nactive++;
|
|
|
|
}
|
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
ct7_entry_t *ct = arg;
|
|
|
|
|
|
|
|
atp = isp_get_atpd(isp, tptr, ct->ct_rxid);
|
|
|
|
if (atp == NULL) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: cannot find adjunct for %x after I/O", __func__, ct->ct_rxid);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sentstatus = ct->ct_flags & CT7_SENDSTATUS;
|
|
|
|
ok = (ct->ct_nphdl == CT7_OK);
|
|
|
|
if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) {
|
|
|
|
ccb->ccb_h.status |= CAM_SENT_SENSE;
|
|
|
|
}
|
|
|
|
notify_cam = ct->ct_header.rqs_seqno & 0x1;
|
|
|
|
if ((ct->ct_flags & CT7_DATAMASK) != CT7_NO_DATA) {
|
|
|
|
resid = ct->ct_resid;
|
|
|
|
atp->bytes_xfered += (atp->last_xframt - resid);
|
|
|
|
atp->last_xframt = 0;
|
|
|
|
}
|
|
|
|
if (ct->ct_nphdl == CT_HBA_RESET) {
|
|
|
|
ok = 0;
|
|
|
|
notify_cam = 1;
|
|
|
|
sentstatus = 1;
|
|
|
|
ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR;
|
|
|
|
} else if (!ok) {
|
|
|
|
ccb->ccb_h.status |= CAM_REQ_CMP_ERR;
|
|
|
|
}
|
|
|
|
tval = atp->tag;
|
|
|
|
isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN, "%s: CTIO7[%x] sts 0x%x flg 0x%x sns %d resid %d %s", __func__,
|
|
|
|
ct->ct_rxid, ct->ct_nphdl, ct->ct_flags, (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, resid, sentstatus? "FIN" : "MID");
|
|
|
|
atp->state = ATPD_STATE_PDON; /* XXX: should really come after isp_complete_ctio */
|
|
|
|
} else if (IS_FC(isp)) {
|
|
|
|
ct2_entry_t *ct = arg;
|
|
|
|
|
|
|
|
atp = isp_get_atpd(isp, tptr, ct->ct_rxid);
|
|
|
|
if (atp == NULL) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: cannot find adjunct for %x after I/O", __func__, ct->ct_rxid);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sentstatus = ct->ct_flags & CT2_SENDSTATUS;
|
|
|
|
ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK;
|
|
|
|
if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) {
|
|
|
|
ccb->ccb_h.status |= CAM_SENT_SENSE;
|
|
|
|
}
|
|
|
|
notify_cam = ct->ct_header.rqs_seqno & 0x1;
|
|
|
|
if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) {
|
|
|
|
resid = ct->ct_resid;
|
|
|
|
atp->bytes_xfered += (atp->last_xframt - resid);
|
|
|
|
atp->last_xframt = 0;
|
|
|
|
}
|
|
|
|
if (ct->ct_status == CT_HBA_RESET) {
|
|
|
|
ok = 0;
|
|
|
|
notify_cam = 1;
|
|
|
|
sentstatus = 1;
|
|
|
|
ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR;
|
|
|
|
} else if (!ok) {
|
|
|
|
ccb->ccb_h.status |= CAM_REQ_CMP_ERR;
|
|
|
|
}
|
|
|
|
isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN, "%s: CTIO2[%x] sts 0x%x flg 0x%x sns %d resid %d %s", __func__,
|
|
|
|
ct->ct_rxid, ct->ct_status, ct->ct_flags, (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, resid, sentstatus? "FIN" : "MID");
|
|
|
|
tval = atp->tag;
|
|
|
|
atp->state = ATPD_STATE_PDON; /* XXX: should really come after isp_complete_ctio */
|
|
|
|
} else {
|
|
|
|
ct_entry_t *ct = arg;
|
|
|
|
sentstatus = ct->ct_flags & CT_SENDSTATUS;
|
2000-01-03 23:55:30 +00:00
|
|
|
ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK;
|
2001-04-04 21:58:29 +00:00
|
|
|
/*
|
|
|
|
* We *ought* to be able to get back to the original ATIO
|
|
|
|
* here, but for some reason this gets lost. It's just as
|
|
|
|
* well because it's squirrelled away as part of periph
|
|
|
|
* private data.
|
|
|
|
*
|
|
|
|
* We can live without it as long as we continue to use
|
|
|
|
* the auto-replenish feature for CTIOs.
|
|
|
|
*/
|
|
|
|
notify_cam = ct->ct_header.rqs_seqno & 0x1;
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ct->ct_status == (CT_HBA_RESET & 0xff)) {
|
|
|
|
ok = 0;
|
|
|
|
notify_cam = 1;
|
|
|
|
sentstatus = 1;
|
|
|
|
ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR;
|
|
|
|
} else if (!ok) {
|
|
|
|
ccb->ccb_h.status |= CAM_REQ_CMP_ERR;
|
|
|
|
} else if (ct->ct_status & QLTM_SVALID) {
|
2001-04-04 21:58:29 +00:00
|
|
|
char *sp = (char *)ct;
|
|
|
|
sp += CTIO_SENSE_OFFSET;
|
2009-08-01 01:04:26 +00:00
|
|
|
ccb->csio.sense_len = min(sizeof (ccb->csio.sense_data), QLTM_SENSELEN);
|
|
|
|
ISP_MEMCPY(&ccb->csio.sense_data, sp, ccb->csio.sense_len);
|
2001-04-04 21:58:29 +00:00
|
|
|
ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
|
|
|
|
}
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) {
|
2001-04-04 21:58:29 +00:00
|
|
|
resid = ct->ct_resid;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: CTIO[%x] tag %x S_ID 0x%x lun %d sts %x flg %x resid %d %s", __func__,
|
|
|
|
ct->ct_fwhandle, ct->ct_tag_val, ct->ct_iid, ct->ct_lun, ct->ct_status, ct->ct_flags, resid, sentstatus? "FIN" : "MID");
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
tval = ct->ct_fwhandle;
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2001-04-04 21:58:29 +00:00
|
|
|
ccb->csio.resid += resid;
|
2000-01-03 23:55:30 +00:00
|
|
|
|
|
|
|
/*
|
2001-04-04 21:58:29 +00:00
|
|
|
* 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.
|
2000-01-03 23:55:30 +00:00
|
|
|
*
|
|
|
|
* In any case, for this platform, the upper layers figure out
|
|
|
|
* what to do next, so all we do here is collect status and
|
2001-04-04 21:58:29 +00:00
|
|
|
* pass information along. Any DMA handles have already been
|
|
|
|
* freed.
|
2000-01-03 23:55:30 +00:00
|
|
|
*/
|
2000-07-18 06:58:28 +00:00
|
|
|
if (notify_cam == 0) {
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, " INTER CTIO[0x%x] done", tval);
|
2009-08-01 01:04:26 +00:00
|
|
|
return;
|
2000-07-18 06:58:28 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (tptr) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
}
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s CTIO[0x%x] done", (sentstatus)? " FINAL " : "MIDTERM ", tval);
|
2001-04-04 21:58:29 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (!ok && !IS_24XX(isp)) {
|
2001-04-04 21:58:29 +00:00
|
|
|
isp_target_putback_atio(ccb);
|
2000-07-18 06:58:28 +00:00
|
|
|
} else {
|
2001-04-04 21:58:29 +00:00
|
|
|
isp_complete_ctio(ccb);
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_handle_platform_notify_scsi(ispsoftc_t *isp, in_entry_t *inot)
|
|
|
|
{
|
|
|
|
(void) isp_notify_ack(isp, inot);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_handle_platform_notify_fc(ispsoftc_t *isp, in_fcentry_t *inp)
|
|
|
|
{
|
|
|
|
int needack = 1;
|
|
|
|
switch (inp->in_status) {
|
|
|
|
case IN_PORT_LOGOUT:
|
|
|
|
/*
|
|
|
|
* XXX: Need to delete this initiator's WWN from the database
|
|
|
|
* XXX: Need to send this LOGOUT upstream
|
|
|
|
*/
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "port logout of S_ID 0x%x", inp->in_iid);
|
|
|
|
break;
|
|
|
|
case IN_PORT_CHANGED:
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "port changed for S_ID 0x%x", inp->in_iid);
|
|
|
|
break;
|
|
|
|
case IN_GLOBAL_LOGO:
|
|
|
|
isp_del_all_wwn_entries(isp, 0);
|
|
|
|
isp_prt(isp, ISP_LOGINFO, "all ports logged out");
|
|
|
|
break;
|
|
|
|
case IN_ABORT_TASK:
|
|
|
|
{
|
|
|
|
tstate_t *tptr;
|
|
|
|
uint16_t lun;
|
|
|
|
uint32_t loopid;
|
|
|
|
uint64_t wwn;
|
|
|
|
atio_private_data_t *atp;
|
|
|
|
fcportdb_t *lp;
|
|
|
|
struct ccb_immediate_notify *inot = NULL;
|
|
|
|
|
|
|
|
if (ISP_CAP_SCCFW(isp)) {
|
|
|
|
lun = inp->in_scclun;
|
|
|
|
} else {
|
|
|
|
lun = inp->in_lun;
|
|
|
|
}
|
|
|
|
if (ISP_CAP_2KLOGIN(isp)) {
|
|
|
|
loopid = ((in_fcentry_e_t *)inot)->in_iid;
|
|
|
|
} else {
|
|
|
|
loopid = inp->in_iid;
|
|
|
|
}
|
|
|
|
if (isp_find_pdb_by_loopid(isp, 0, loopid, &lp)) {
|
|
|
|
wwn = lp->port_wwn;
|
|
|
|
} else {
|
|
|
|
wwn = INI_ANY;
|
|
|
|
}
|
|
|
|
tptr = get_lun_statep(isp, 0, lun);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "ABORT TASK for lun %u- but no tstate", lun);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
atp = isp_get_atpd(isp, tptr, inp->in_seqid);
|
|
|
|
|
|
|
|
if (atp) {
|
|
|
|
inot = (struct ccb_immediate_notify *) SLIST_FIRST(&tptr->inots);
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "ABORT TASK RX_ID %x WWN 0x%016llx state %d", inp->in_seqid, (unsigned long long) wwn, atp->state);
|
|
|
|
if (inot) {
|
|
|
|
tptr->inot_count--;
|
|
|
|
SLIST_REMOVE_HEAD(&tptr->inots, sim_links.sle);
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: Take FREE INOT count now %d\n", __func__, tptr->inot_count);
|
|
|
|
} else {
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "out of INOT structures\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGWARN, tptr->owner, "abort task RX_ID %x from wwn 0x%016llx, state unknown\n", inp->in_seqid, wwn);
|
|
|
|
}
|
|
|
|
if (inot) {
|
|
|
|
isp_notify_t tmp, *nt = &tmp;
|
|
|
|
ISP_MEMZERO(nt, sizeof (isp_notify_t));
|
|
|
|
nt->nt_hba = isp;
|
|
|
|
nt->nt_tgt = FCPARAM(isp, 0)->isp_wwpn;
|
|
|
|
nt->nt_wwn = wwn;
|
|
|
|
nt->nt_nphdl = loopid;
|
|
|
|
nt->nt_sid = PORT_ANY;
|
|
|
|
nt->nt_did = PORT_ANY;
|
|
|
|
nt->nt_lun = lun;
|
|
|
|
nt->nt_need_ack = 1;
|
|
|
|
nt->nt_channel = 0;
|
|
|
|
nt->nt_ncode = NT_ABORT_TASK;
|
|
|
|
nt->nt_lreserved = inot;
|
|
|
|
isp_handle_platform_target_tmf(isp, nt);
|
|
|
|
needack = 0;
|
|
|
|
}
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (needack) {
|
|
|
|
(void) isp_notify_ack(isp, inp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_handle_platform_notify_24xx(ispsoftc_t *isp, in_fcentry_24xx_t *inot)
|
|
|
|
{
|
|
|
|
uint16_t nphdl;
|
|
|
|
uint32_t portid;
|
|
|
|
fcportdb_t *lp;
|
|
|
|
uint8_t *ptr = NULL;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (inot->in_status) {
|
|
|
|
case IN24XX_ELS_RCVD:
|
|
|
|
{
|
|
|
|
char buf[16], *msg;
|
|
|
|
int chan = ISP_GET_VPIDX(isp, inot->in_vpidx);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that we're just getting notification that an ELS was received
|
|
|
|
* (possibly with some associcated information sent upstream). This is
|
|
|
|
* *not* the same as being given the ELS frame to accept or reject.
|
|
|
|
*/
|
|
|
|
switch (inot->in_status_subcode) {
|
|
|
|
case LOGO:
|
|
|
|
msg = "LOGO";
|
|
|
|
if (ISP_FW_NEWER_THAN(isp, 4, 0, 25)) {
|
|
|
|
ptr = (uint8_t *)inot; /* 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, chan, wwn, nphdl, portid);
|
|
|
|
break;
|
|
|
|
case PRLO:
|
|
|
|
msg = "PRLO";
|
|
|
|
break;
|
|
|
|
case PLOGI:
|
|
|
|
msg = "PLOGI";
|
|
|
|
if (ISP_FW_NEWER_THAN(isp, 4, 0, 25)) {
|
|
|
|
ptr = (uint8_t *)inot; /* 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, chan, wwn, nphdl, portid);
|
|
|
|
break;
|
|
|
|
case PRLI:
|
|
|
|
msg = "PRLI";
|
|
|
|
break;
|
|
|
|
case PDISC:
|
|
|
|
msg = "PDISC";
|
|
|
|
break;
|
|
|
|
case ADISC:
|
|
|
|
msg = "ADISC";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ISP_SNPRINTF(buf, sizeof (buf), "ELS 0x%x", inot->in_status_subcode);
|
|
|
|
msg = buf;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (inot->in_flags & IN24XX_FLAG_PUREX_IOCB) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s Chan %d ELS N-port handle %x PortID 0x%06x marked as needing a PUREX response", msg, chan, nphdl, portid);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s Chan %d ELS N-port handle %x PortID 0x%06x RX_ID 0x%x OX_ID 0x%x", msg, chan, nphdl, portid,
|
|
|
|
inot->in_rxid, inot->in_oxid);
|
|
|
|
(void) isp_notify_ack(isp, inot);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IN24XX_PORT_LOGOUT:
|
|
|
|
ptr = "PORT LOGOUT";
|
|
|
|
if (isp_find_pdb_by_loopid(isp, ISP_GET_VPIDX(isp, inot->in_vpidx), nphdl, &lp)) {
|
|
|
|
isp_del_wwn_entry(isp, ISP_GET_VPIDX(isp, inot->in_vpidx), lp->port_wwn, nphdl, lp->portid);
|
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case IN24XX_PORT_CHANGED:
|
|
|
|
if (ptr == NULL) {
|
|
|
|
ptr = "PORT CHANGED";
|
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case IN24XX_LIP_RESET:
|
|
|
|
if (ptr == NULL) {
|
|
|
|
ptr = "LIP RESET";
|
|
|
|
}
|
|
|
|
isp_prt(isp, ISP_LOGINFO, "Chan %d %s (sub-status 0x%x) for N-port handle 0x%x", ISP_GET_VPIDX(isp, inot->in_vpidx), ptr, inot->in_status_subcode, nphdl);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All subcodes here are irrelevant. What is relevant
|
|
|
|
* is that we need to terminate all active commands from
|
|
|
|
* this initiator (known by N-port handle).
|
|
|
|
*/
|
|
|
|
/* XXX IMPLEMENT XXX */
|
|
|
|
(void) isp_notify_ack(isp, inot);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IN24XX_LINK_RESET:
|
|
|
|
case IN24XX_LINK_FAILED:
|
|
|
|
case IN24XX_SRR_RCVD:
|
|
|
|
default:
|
|
|
|
(void) isp_notify_ack(isp, inot);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
isp_handle_platform_target_notify_ack(ispsoftc_t *isp, isp_notify_t *mp)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (isp->isp_state != ISP_RUNSTATE) {
|
|
|
|
isp_prt(isp, ISP_LOGTINFO, "Notify Code 0x%x (qevalid=%d) acked- h/w not ready (dropping)", mp->nt_ncode, mp->nt_lreserved != NULL);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This case is for a Task Management Function, which shows up as an ATIO7 entry.
|
|
|
|
*/
|
|
|
|
if (IS_24XX(isp) && mp->nt_lreserved && ((isphdr_t *)mp->nt_lreserved)->rqs_entry_type == RQSTYPE_ATIO) {
|
|
|
|
ct7_entry_t local, *cto = &local;
|
|
|
|
at7_entry_t *aep = (at7_entry_t *)mp->nt_lreserved;
|
|
|
|
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, mp->nt_channel, sid, &lp)) {
|
|
|
|
nphdl = lp->handle;
|
|
|
|
} else {
|
|
|
|
nphdl = NIL_HANDLE;
|
|
|
|
}
|
|
|
|
ISP_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_vpidx = mp->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;
|
|
|
|
return (isp_target_put_entry(isp, &local));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This case is for a responding to an ABTS frame
|
|
|
|
*/
|
|
|
|
if (IS_24XX(isp) && mp->nt_lreserved && ((isphdr_t *)mp->nt_lreserved)->rqs_entry_type == RQSTYPE_ABTS_RCVD) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Overload nt_need_ack here to mark whether we've terminated the associated command.
|
|
|
|
*/
|
|
|
|
if (mp->nt_need_ack) {
|
|
|
|
uint8_t storage[QENTRY_LEN];
|
|
|
|
ct7_entry_t *cto = (ct7_entry_t *) storage;
|
|
|
|
abts_t *abts = (abts_t *)mp->nt_lreserved;
|
|
|
|
|
|
|
|
ISP_MEMZERO(cto, sizeof (ct7_entry_t));
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: [%x] terminating after ABTS received", __func__, abts->abts_rxid_task);
|
|
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7;
|
|
|
|
cto->ct_header.rqs_entry_count = 1;
|
|
|
|
cto->ct_nphdl = mp->nt_nphdl;
|
|
|
|
cto->ct_rxid = abts->abts_rxid_task;
|
|
|
|
cto->ct_iid_lo = mp->nt_sid;
|
|
|
|
cto->ct_iid_hi = mp->nt_sid >> 16;
|
|
|
|
cto->ct_oxid = abts->abts_ox_id;
|
|
|
|
cto->ct_vpidx = mp->nt_channel;
|
|
|
|
cto->ct_flags = CT7_NOACK|CT7_TERMINATE;
|
|
|
|
if (isp_target_put_entry(isp, cto)) {
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
mp->nt_need_ack = 0;
|
|
|
|
}
|
|
|
|
if (isp_acknak_abts(isp, mp->nt_lreserved, 0) == ENOMEM) {
|
|
|
|
return (ENOMEM);
|
|
|
|
} else {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle logout cases here
|
|
|
|
*/
|
|
|
|
if (mp->nt_ncode == NT_GLOBAL_LOGOUT) {
|
|
|
|
isp_del_all_wwn_entries(isp, mp->nt_channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp->nt_ncode == NT_LOGOUT) {
|
|
|
|
if (!IS_2100(isp) && IS_FC(isp)) {
|
|
|
|
isp_del_wwn_entries(isp, mp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* General purpose acknowledgement
|
|
|
|
*/
|
|
|
|
if (mp->nt_need_ack) {
|
|
|
|
isp_prt(isp, ISP_LOGTINFO, "Notify Code 0x%x (qevalid=%d) being acked", mp->nt_ncode, mp->nt_lreserved != NULL);
|
|
|
|
return (isp_notify_ack(isp, mp->nt_lreserved));
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle task managment functions.
|
|
|
|
*
|
|
|
|
* We show up here with a notify structure filled out.
|
|
|
|
*
|
|
|
|
* The nt_lreserved tag points to the original queue entry
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
isp_handle_platform_target_tmf(ispsoftc_t *isp, isp_notify_t *notify)
|
|
|
|
{
|
|
|
|
tstate_t *tptr;
|
|
|
|
fcportdb_t *lp;
|
|
|
|
struct ccb_immediate_notify *inot;
|
|
|
|
inot_private_data_t *ntp = NULL;
|
|
|
|
lun_id_t lun;
|
|
|
|
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: code 0x%x sid 0x%x tagval 0x%016llx chan %d lun 0x%x", __func__, notify->nt_ncode,
|
|
|
|
notify->nt_sid, (unsigned long long) notify->nt_tagval, notify->nt_channel, notify->nt_lun);
|
|
|
|
/*
|
|
|
|
* NB: This assignment is necessary because of tricky type conversion.
|
|
|
|
* XXX: This is tricky and I need to check this. If the lun isn't known
|
|
|
|
* XXX: for the task management function, it does not of necessity follow
|
|
|
|
* XXX: that it should go up stream to the wildcard listener.
|
|
|
|
*/
|
|
|
|
if (notify->nt_lun == LUN_ANY) {
|
|
|
|
lun = CAM_LUN_WILDCARD;
|
|
|
|
} else {
|
|
|
|
lun = notify->nt_lun;
|
|
|
|
}
|
|
|
|
tptr = get_lun_statep(isp, notify->nt_channel, lun);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
tptr = get_lun_statep(isp, notify->nt_channel, CAM_LUN_WILDCARD);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s: no state pointer found for chan %d lun 0x%x", __func__, notify->nt_channel, lun);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inot = (struct ccb_immediate_notify *) SLIST_FIRST(&tptr->inots);
|
|
|
|
if (inot == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s: out of immediate notify structures for chan %d lun 0x%x", __func__, notify->nt_channel, lun);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isp_find_pdb_by_sid(isp, notify->nt_channel, notify->nt_sid, &lp) == 0) {
|
|
|
|
inot->initiator_id = CAM_TARGET_WILDCARD;
|
|
|
|
} else {
|
|
|
|
inot->initiator_id = lp->handle;
|
|
|
|
}
|
|
|
|
inot->seq_id = notify->nt_tagval;
|
|
|
|
inot->tag_id = notify->nt_tagval >> 32;
|
|
|
|
|
|
|
|
switch (notify->nt_ncode) {
|
|
|
|
case NT_ABORT_TASK:
|
|
|
|
isp_target_mark_aborted_early(isp, tptr, inot->tag_id);
|
|
|
|
inot->arg = MSG_ABORT_TASK;
|
|
|
|
break;
|
|
|
|
case NT_ABORT_TASK_SET:
|
|
|
|
isp_target_mark_aborted_early(isp, tptr, TAG_ANY);
|
|
|
|
inot->arg = MSG_ABORT_TASK_SET;
|
|
|
|
break;
|
|
|
|
case NT_CLEAR_ACA:
|
|
|
|
inot->arg = MSG_CLEAR_ACA;
|
|
|
|
break;
|
|
|
|
case NT_CLEAR_TASK_SET:
|
|
|
|
inot->arg = MSG_CLEAR_TASK_SET;
|
|
|
|
break;
|
|
|
|
case NT_LUN_RESET:
|
|
|
|
inot->arg = MSG_LOGICAL_UNIT_RESET;
|
|
|
|
break;
|
|
|
|
case NT_TARGET_RESET:
|
|
|
|
inot->arg = MSG_TARGET_RESET;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s: unknown TMF code 0x%x for chan %d lun 0x%x", __func__, notify->nt_ncode, notify->nt_channel, lun);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
ntp = isp_get_ntpd(isp, tptr);
|
|
|
|
if (ntp == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s: out of inotify private structures", __func__);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
ISP_MEMCPY(&ntp->rd.nt, notify, sizeof (isp_notify_t));
|
|
|
|
if (notify->nt_lreserved) {
|
|
|
|
ISP_MEMCPY(&ntp->rd.data, notify->nt_lreserved, QENTRY_LEN);
|
|
|
|
ntp->rd.nt.nt_lreserved = &ntp->rd.data;
|
|
|
|
}
|
|
|
|
ntp->rd.seq_id = notify->nt_tagval;
|
|
|
|
ntp->rd.tag_id = notify->nt_tagval >> 32;
|
|
|
|
|
|
|
|
tptr->inot_count--;
|
|
|
|
SLIST_REMOVE_HEAD(&tptr->inots, sim_links.sle);
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: Take FREE INOT count now %d\n", __func__, tptr->inot_count);
|
|
|
|
inot->ccb_h.status = CAM_MESSAGE_RECV;
|
|
|
|
xpt_done((union ccb *)inot);
|
|
|
|
return;
|
|
|
|
bad:
|
|
|
|
if (tptr) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
}
|
|
|
|
if (notify->nt_need_ack && notify->nt_lreserved) {
|
|
|
|
if (((isphdr_t *)notify->nt_lreserved)->rqs_entry_type == RQSTYPE_ABTS_RCVD) {
|
|
|
|
(void) isp_acknak_abts(isp, notify->nt_lreserved, ENOMEM);
|
|
|
|
} else {
|
|
|
|
(void) isp_notify_ack(isp, notify->nt_lreserved);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the associated private data and makr it as dead so
|
|
|
|
* we don't try to work on it any further.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
isp_target_mark_aborted(ispsoftc_t *isp, union ccb *ccb)
|
|
|
|
{
|
|
|
|
tstate_t *tptr;
|
|
|
|
atio_private_data_t *atp;
|
|
|
|
|
|
|
|
tptr = get_lun_statep(isp, XS_CHANNEL(ccb), XS_LUN(ccb));
|
|
|
|
if (tptr == NULL) {
|
|
|
|
tptr = get_lun_statep(isp, XS_CHANNEL(ccb), CAM_LUN_WILDCARD);
|
|
|
|
if (tptr == NULL) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
atp = isp_get_atpd(isp, tptr, ccb->atio.tag_id);
|
|
|
|
if (atp == NULL) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
atp->dead = 1;
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_target_mark_aborted_early(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag_id)
|
|
|
|
{
|
|
|
|
atio_private_data_t *atp;
|
|
|
|
inot_private_data_t *restart_queue = tptr->restart_queue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First, clean any commands pending restart
|
|
|
|
*/
|
|
|
|
tptr->restart_queue = NULL;
|
|
|
|
while (restart_queue) {
|
|
|
|
uint32_t this_tag_id;
|
|
|
|
inot_private_data_t *ntp = restart_queue;
|
|
|
|
|
|
|
|
restart_queue = ntp->rd.nt.nt_hba;
|
|
|
|
|
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
this_tag_id = ((at7_entry_t *)ntp->rd.data)->at_rxid;
|
|
|
|
} else {
|
|
|
|
this_tag_id = ((at2_entry_t *)ntp->rd.data)->at_rxid;
|
|
|
|
}
|
|
|
|
if ((uint64_t)tag_id == TAG_ANY || tag_id == this_tag_id) {
|
|
|
|
isp_put_ntpd(isp, tptr, ntp);
|
|
|
|
} else {
|
|
|
|
ntp->rd.nt.nt_hba = tptr->restart_queue;
|
|
|
|
tptr->restart_queue = ntp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now mark other ones dead as well.
|
|
|
|
*/
|
|
|
|
for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) {
|
|
|
|
if ((uint64_t)tag_id == TAG_ANY || atp->tag == tag_id) {
|
|
|
|
atp->dead = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef ISP_INTERNAL_TARGET
|
|
|
|
// #define ISP_FORCE_TIMEOUT 1
|
2009-09-15 02:25:03 +00:00
|
|
|
// #define ISP_TEST_WWNS 1
|
|
|
|
// #define ISP_TEST_SEPARATE_STATUS 1
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
#define ccb_data_offset ppriv_field0
|
|
|
|
#define ccb_atio ppriv_ptr1
|
|
|
|
#define ccb_inot ppriv_ptr1
|
|
|
|
|
|
|
|
#define MAX_ISP_TARG_TRANSFER (2 << 20)
|
|
|
|
#define NISP_TARG_CMDS 1024
|
|
|
|
#define NISP_TARG_NOTIFIES 1024
|
|
|
|
#define DISK_SHIFT 9
|
|
|
|
#define JUNK_SIZE 256
|
|
|
|
|
|
|
|
#ifndef VERIFY_10
|
|
|
|
#define VERIFY_10 0x2f
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TAILQ_HEAD(ccb_queue, ccb_hdr);
|
|
|
|
extern u_int vm_kmem_size;
|
|
|
|
static int ca;
|
|
|
|
static uint32_t disk_size;
|
|
|
|
static uint8_t *disk_data = NULL;
|
|
|
|
static uint8_t *junk_data;
|
|
|
|
static MALLOC_DEFINE(M_ISPTARG, "ISPTARG", "ISP TARGET data");
|
|
|
|
struct isptarg_softc {
|
|
|
|
/* CCBs (CTIOs, ATIOs, INOTs) pending on the controller */
|
|
|
|
struct ccb_queue work_queue;
|
|
|
|
struct ccb_queue rework_queue;
|
|
|
|
struct ccb_queue running_queue;
|
|
|
|
struct ccb_queue inot_queue;
|
|
|
|
struct cam_periph *periph;
|
|
|
|
struct cam_path *path;
|
|
|
|
ispsoftc_t *isp;
|
|
|
|
};
|
|
|
|
static periph_ctor_t isptargctor;
|
|
|
|
static periph_dtor_t isptargdtor;
|
|
|
|
static periph_start_t isptargstart;
|
|
|
|
static periph_init_t isptarginit;
|
|
|
|
static void isptarg_done(struct cam_periph *, union ccb *);
|
|
|
|
static void isptargasync(void *, u_int32_t, struct cam_path *, void *);
|
|
|
|
|
|
|
|
|
|
|
|
static int isptarg_rwparm(uint8_t *, uint8_t *, uint64_t, uint32_t, uint8_t **, uint32_t *, int *);
|
|
|
|
|
|
|
|
static struct periph_driver isptargdriver =
|
|
|
|
{
|
|
|
|
isptarginit, "isptarg", TAILQ_HEAD_INITIALIZER(isptargdriver.units), /* generation */ 0
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
isptarginit(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isptargnotify(ispsoftc_t *isp, union ccb *iccb, struct ccb_immediate_notify *inot)
|
|
|
|
{
|
|
|
|
struct ccb_notify_acknowledge *ack = &iccb->cna2;
|
|
|
|
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: [0x%x] immediate notify for 0x%x from 0x%x status 0x%x arg 0x%x\n", __func__,
|
|
|
|
inot->tag_id, inot->initiator_id, inot->seq_id, inot->ccb_h.status, inot->arg);
|
|
|
|
ack->ccb_h.func_code = XPT_NOTIFY_ACKNOWLEDGE;
|
|
|
|
ack->ccb_h.flags = 0;
|
|
|
|
ack->ccb_h.retry_count = 0;
|
|
|
|
ack->ccb_h.cbfcnp = isptarg_done;
|
|
|
|
ack->ccb_h.timeout = 0;
|
|
|
|
ack->ccb_h.ccb_inot = inot;
|
|
|
|
ack->tag_id = inot->tag_id;
|
|
|
|
ack->seq_id = inot->seq_id;
|
|
|
|
ack->initiator_id = inot->initiator_id;
|
|
|
|
xpt_action(iccb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isptargstart(struct cam_periph *periph, union ccb *iccb)
|
|
|
|
{
|
|
|
|
const uint8_t niliqd[SHORT_INQUIRY_LENGTH] = { 0x7f };
|
|
|
|
const uint8_t iqd[SHORT_INQUIRY_LENGTH] = {
|
|
|
|
0, 0x0, 0x2, 0x2, 32, 0, 0, 0x32,
|
|
|
|
'F', 'R', 'E', 'E', 'B', 'S', 'D', ' ',
|
|
|
|
'S', 'C', 'S', 'I', ' ', 'M', 'E', 'M',
|
|
|
|
'O', 'R', 'Y', ' ', 'D', 'I', 'S', 'K',
|
|
|
|
'0', '0', '0', '1'
|
|
|
|
};
|
|
|
|
int i, more = 0, last;
|
|
|
|
struct isptarg_softc *softc = periph->softc;
|
|
|
|
struct ccb_scsiio *csio;
|
|
|
|
lun_id_t return_lun;
|
|
|
|
struct ccb_accept_tio *atio;
|
|
|
|
uint8_t *cdb, *ptr, status;
|
|
|
|
uint8_t *data_ptr;
|
|
|
|
uint32_t data_len, flags;
|
|
|
|
struct ccb_hdr *ccbh;
|
|
|
|
|
|
|
|
mtx_assert(periph->sim->mtx, MA_OWNED);
|
|
|
|
ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, iccb->ccb_h.path, "%s: function code 0x%x INOTQ=%c WORKQ=%c REWORKQ=%c\n", __func__, iccb->ccb_h.func_code,
|
|
|
|
TAILQ_FIRST(&softc->inot_queue)? 'y' : 'n', TAILQ_FIRST(&softc->work_queue)? 'y' : 'n', TAILQ_FIRST(&softc->rework_queue)? 'y' : 'n');
|
|
|
|
/*
|
|
|
|
* Check for immediate notifies first
|
|
|
|
*/
|
|
|
|
ccbh = TAILQ_FIRST(&softc->inot_queue);
|
|
|
|
if (ccbh) {
|
|
|
|
TAILQ_REMOVE(&softc->inot_queue, ccbh, periph_links.tqe);
|
|
|
|
if (TAILQ_FIRST(&softc->inot_queue) || TAILQ_FIRST(&softc->work_queue) || TAILQ_FIRST(&softc->rework_queue)) {
|
|
|
|
xpt_schedule(periph, 1);
|
|
|
|
}
|
|
|
|
isptargnotify(softc->isp, iccb, (struct ccb_immediate_notify *)ccbh);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the rework (continuation) work queue first.
|
|
|
|
*/
|
|
|
|
ccbh = TAILQ_FIRST(&softc->rework_queue);
|
|
|
|
if (ccbh) {
|
|
|
|
atio = (struct ccb_accept_tio *)ccbh;
|
|
|
|
TAILQ_REMOVE(&softc->rework_queue, ccbh, periph_links.tqe);
|
|
|
|
more = TAILQ_FIRST(&softc->work_queue) || TAILQ_FIRST(&softc->rework_queue);
|
|
|
|
} else {
|
|
|
|
ccbh = TAILQ_FIRST(&softc->work_queue);
|
|
|
|
if (ccbh == NULL) {
|
|
|
|
ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, iccb->ccb_h.path, "%s: woken up but no work?\n", __func__);
|
|
|
|
xpt_release_ccb(iccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
atio = (struct ccb_accept_tio *)ccbh;
|
|
|
|
TAILQ_REMOVE(&softc->work_queue, ccbh, periph_links.tqe);
|
|
|
|
more = TAILQ_FIRST(&softc->work_queue) != NULL;
|
|
|
|
atio->ccb_h.ccb_data_offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (atio->tag_id == 0xffffffff || atio->ccb_h.func_code != XPT_ACCEPT_TARGET_IO) {
|
|
|
|
panic("BAD ATIO");
|
|
|
|
}
|
|
|
|
|
|
|
|
data_ptr = NULL;
|
|
|
|
data_len = 0;
|
|
|
|
csio = &iccb->csio;
|
|
|
|
status = SCSI_STATUS_OK;
|
|
|
|
flags = CAM_SEND_STATUS;
|
|
|
|
memset(&atio->sense_data, 0, sizeof (atio->sense_data));
|
|
|
|
cdb = atio->cdb_io.cdb_bytes;
|
|
|
|
ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, ccbh->path, "%s: [0x%x] processing ATIO from 0x%x CDB=0x%x data_offset=%u\n", __func__, atio->tag_id, atio->init_id,
|
|
|
|
cdb[0], atio->ccb_h.ccb_data_offset);
|
|
|
|
|
|
|
|
return_lun = XS_LUN(atio);
|
|
|
|
if (return_lun != 0) {
|
|
|
|
xpt_print(atio->ccb_h.path, "[0x%x] Non-Zero Lun %d: cdb0=0x%x\n", atio->tag_id, return_lun, cdb[0]);
|
|
|
|
if (cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) {
|
|
|
|
status = SCSI_STATUS_CHECK_COND;
|
|
|
|
atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_ILLEGAL_REQUEST;
|
|
|
|
atio->sense_data.add_sense_code = 0x25;
|
|
|
|
atio->sense_data.add_sense_code_qual = 0x0;
|
|
|
|
atio->sense_len = sizeof (atio->sense_data);
|
|
|
|
}
|
|
|
|
return_lun = CAM_LUN_WILDCARD;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cdb[0]) {
|
|
|
|
case REQUEST_SENSE:
|
|
|
|
flags |= CAM_DIR_IN;
|
|
|
|
data_len = sizeof (atio->sense_data);
|
|
|
|
junk_data[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_NO_SENSE;
|
|
|
|
memset(junk_data+1, 0, data_len-1);
|
|
|
|
if (data_len > cdb[4]) {
|
|
|
|
data_len = cdb[4];
|
|
|
|
}
|
|
|
|
if (data_len) {
|
|
|
|
data_ptr = junk_data;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case READ_6:
|
|
|
|
case READ_10:
|
|
|
|
case READ_12:
|
|
|
|
case READ_16:
|
|
|
|
if (isptarg_rwparm(cdb, disk_data, disk_size, atio->ccb_h.ccb_data_offset, &data_ptr, &data_len, &last)) {
|
|
|
|
status = SCSI_STATUS_CHECK_COND;
|
|
|
|
atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION;
|
|
|
|
atio->sense_data.add_sense_code = 0x5;
|
|
|
|
atio->sense_data.add_sense_code_qual = 0x24;
|
|
|
|
atio->sense_len = sizeof (atio->sense_data);
|
|
|
|
} else {
|
|
|
|
#ifdef ISP_FORCE_TIMEOUT
|
|
|
|
{
|
|
|
|
static int foo;
|
|
|
|
if (foo++ == 500) {
|
|
|
|
if (more) {
|
|
|
|
xpt_schedule(periph, 1);
|
|
|
|
}
|
|
|
|
foo = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef ISP_TEST_SEPARATE_STATUS
|
|
|
|
if (last && data_len) {
|
|
|
|
last = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (last == 0) {
|
|
|
|
flags &= ~CAM_SEND_STATUS;
|
|
|
|
}
|
|
|
|
if (data_len) {
|
|
|
|
atio->ccb_h.ccb_data_offset += data_len;
|
|
|
|
flags |= CAM_DIR_IN;
|
|
|
|
} else {
|
|
|
|
flags |= CAM_DIR_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WRITE_6:
|
|
|
|
case WRITE_10:
|
|
|
|
case WRITE_12:
|
|
|
|
case WRITE_16:
|
|
|
|
if (isptarg_rwparm(cdb, disk_data, disk_size, atio->ccb_h.ccb_data_offset, &data_ptr, &data_len, &last)) {
|
|
|
|
status = SCSI_STATUS_CHECK_COND;
|
|
|
|
atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION;
|
|
|
|
atio->sense_data.add_sense_code = 0x5;
|
|
|
|
atio->sense_data.add_sense_code_qual = 0x24;
|
|
|
|
atio->sense_len = sizeof (atio->sense_data);
|
|
|
|
} else {
|
|
|
|
#ifdef ISP_FORCE_TIMEOUT
|
|
|
|
{
|
|
|
|
static int foo;
|
|
|
|
if (foo++ == 500) {
|
|
|
|
if (more) {
|
|
|
|
xpt_schedule(periph, 1);
|
|
|
|
}
|
|
|
|
foo = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef ISP_TEST_SEPARATE_STATUS
|
|
|
|
if (last && data_len) {
|
|
|
|
last = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (last == 0) {
|
|
|
|
flags &= ~CAM_SEND_STATUS;
|
|
|
|
}
|
|
|
|
if (data_len) {
|
|
|
|
atio->ccb_h.ccb_data_offset += data_len;
|
|
|
|
flags |= CAM_DIR_OUT;
|
|
|
|
} else {
|
|
|
|
flags |= CAM_DIR_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case INQUIRY:
|
|
|
|
flags |= CAM_DIR_IN;
|
|
|
|
if (cdb[1] || cdb[2] || cdb[3]) {
|
|
|
|
status = SCSI_STATUS_CHECK_COND;
|
|
|
|
atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION;
|
|
|
|
atio->sense_data.add_sense_code = 0x5;
|
|
|
|
atio->sense_data.add_sense_code_qual = 0x20;
|
|
|
|
atio->sense_len = sizeof (atio->sense_data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
data_len = sizeof (iqd);
|
|
|
|
if (data_len > cdb[4]) {
|
|
|
|
data_len = cdb[4];
|
|
|
|
}
|
|
|
|
if (data_len) {
|
|
|
|
if (XS_LUN(iccb) != 0) {
|
|
|
|
memcpy(junk_data, niliqd, sizeof (iqd));
|
|
|
|
} else {
|
|
|
|
memcpy(junk_data, iqd, sizeof (iqd));
|
|
|
|
}
|
|
|
|
data_ptr = junk_data;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TEST_UNIT_READY:
|
|
|
|
flags |= CAM_DIR_NONE;
|
|
|
|
if (ca) {
|
|
|
|
ca = 0;
|
|
|
|
status = SCSI_STATUS_CHECK_COND;
|
|
|
|
atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION;
|
|
|
|
atio->sense_data.add_sense_code = 0x28;
|
|
|
|
atio->sense_data.add_sense_code_qual = 0x0;
|
|
|
|
atio->sense_len = sizeof (atio->sense_data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SYNCHRONIZE_CACHE:
|
|
|
|
case START_STOP:
|
|
|
|
case RESERVE:
|
|
|
|
case RELEASE:
|
|
|
|
case VERIFY_10:
|
|
|
|
flags |= CAM_DIR_NONE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case READ_CAPACITY:
|
|
|
|
flags |= CAM_DIR_IN;
|
|
|
|
if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) {
|
|
|
|
status = SCSI_STATUS_CHECK_COND;
|
|
|
|
atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION;
|
|
|
|
atio->sense_data.add_sense_code = 0x5;
|
|
|
|
atio->sense_data.add_sense_code_qual = 0x24;
|
|
|
|
atio->sense_len = sizeof (atio->sense_data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cdb[8] & 0x1) { /* PMI */
|
|
|
|
junk_data[0] = 0xff;
|
|
|
|
junk_data[1] = 0xff;
|
|
|
|
junk_data[2] = 0xff;
|
|
|
|
junk_data[3] = 0xff;
|
|
|
|
} else {
|
|
|
|
uint64_t last_blk = (disk_size >> DISK_SHIFT) - 1;
|
|
|
|
if (last_blk < 0xffffffffULL) {
|
|
|
|
junk_data[0] = (last_blk >> 24) & 0xff;
|
|
|
|
junk_data[1] = (last_blk >> 16) & 0xff;
|
|
|
|
junk_data[2] = (last_blk >> 8) & 0xff;
|
|
|
|
junk_data[3] = (last_blk) & 0xff;
|
|
|
|
} else {
|
|
|
|
junk_data[0] = 0xff;
|
|
|
|
junk_data[1] = 0xff;
|
|
|
|
junk_data[2] = 0xff;
|
|
|
|
junk_data[3] = 0xff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
junk_data[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
|
|
|
|
junk_data[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
|
|
|
|
junk_data[6] = ((1 << DISK_SHIFT) >> 8) & 0xff;
|
|
|
|
junk_data[7] = ((1 << DISK_SHIFT)) & 0xff;
|
|
|
|
data_ptr = junk_data;
|
|
|
|
data_len = 8;
|
|
|
|
break;
|
|
|
|
case REPORT_LUNS:
|
|
|
|
flags |= CAM_DIR_IN;
|
|
|
|
memset(junk_data, 0, JUNK_SIZE);
|
|
|
|
junk_data[0] = (1 << 3) >> 24;
|
|
|
|
junk_data[1] = (1 << 3) >> 16;
|
|
|
|
junk_data[2] = (1 << 3) >> 8;
|
|
|
|
junk_data[3] = (1 << 3);
|
|
|
|
ptr = NULL;
|
|
|
|
for (i = 0; i < 1; i++) {
|
|
|
|
ptr = &junk_data[8 + (1 << 3)];
|
|
|
|
if (i >= 256) {
|
|
|
|
ptr[0] = 0x40 | ((i >> 8) & 0x3f);
|
|
|
|
}
|
|
|
|
ptr[1] = i;
|
|
|
|
}
|
|
|
|
data_ptr = junk_data;
|
|
|
|
data_len = (ptr + 8) - junk_data;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
flags |= CAM_DIR_NONE;
|
|
|
|
status = SCSI_STATUS_CHECK_COND;
|
|
|
|
atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION;
|
|
|
|
atio->sense_data.add_sense_code = 0x5;
|
|
|
|
atio->sense_data.add_sense_code_qual = 0x20;
|
|
|
|
atio->sense_len = sizeof (atio->sense_data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are done with the transaction, tell the
|
|
|
|
* controller to send status and perform a CMD_CMPLT.
|
|
|
|
* If we have associated sense data, see if we can
|
|
|
|
* send that too.
|
|
|
|
*/
|
|
|
|
if (status == SCSI_STATUS_CHECK_COND) {
|
|
|
|
flags |= CAM_SEND_SENSE;
|
|
|
|
csio->sense_len = atio->sense_len;
|
|
|
|
csio->sense_data = atio->sense_data;
|
|
|
|
flags &= ~CAM_DIR_MASK;
|
|
|
|
data_len = 0;
|
|
|
|
data_ptr = NULL;
|
|
|
|
}
|
|
|
|
cam_fill_ctio(csio, 0, isptarg_done, flags, MSG_SIMPLE_Q_TAG, atio->tag_id, atio->init_id, status, data_ptr, data_len, 0);
|
|
|
|
iccb->ccb_h.target_id = atio->ccb_h.target_id;
|
|
|
|
iccb->ccb_h.target_lun = return_lun;
|
|
|
|
iccb->ccb_h.ccb_atio = atio;
|
|
|
|
xpt_action(iccb);
|
|
|
|
|
|
|
|
if ((atio->ccb_h.status & CAM_DEV_QFRZN) != 0) {
|
|
|
|
cam_release_devq(periph->path, 0, 0, 0, 0);
|
|
|
|
atio->ccb_h.status &= ~CAM_DEV_QFRZN;
|
|
|
|
}
|
|
|
|
if (more) {
|
|
|
|
xpt_schedule(periph, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static cam_status
|
|
|
|
isptargctor(struct cam_periph *periph, void *arg)
|
|
|
|
{
|
|
|
|
struct isptarg_softc *softc;
|
|
|
|
|
|
|
|
softc = (struct isptarg_softc *)arg;
|
|
|
|
periph->softc = softc;
|
|
|
|
softc->periph = periph;
|
|
|
|
softc->path = periph->path;
|
|
|
|
ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, periph->path, "%s called\n", __func__);
|
|
|
|
return (CAM_REQ_CMP);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isptargdtor(struct cam_periph *periph)
|
|
|
|
{
|
|
|
|
struct isptarg_softc *softc;
|
|
|
|
softc = (struct isptarg_softc *)periph->softc;
|
|
|
|
ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, periph->path, "%s called\n", __func__);
|
|
|
|
softc->periph = NULL;
|
|
|
|
softc->path = NULL;
|
|
|
|
periph->softc = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isptarg_done(struct cam_periph *periph, union ccb *ccb)
|
|
|
|
{
|
|
|
|
struct isptarg_softc *softc;
|
|
|
|
ispsoftc_t *isp;
|
|
|
|
struct ccb_accept_tio *atio;
|
|
|
|
struct ccb_immediate_notify *inot;
|
|
|
|
cam_status status;
|
|
|
|
|
|
|
|
softc = (struct isptarg_softc *)periph->softc;
|
|
|
|
isp = softc->isp;
|
|
|
|
status = ccb->ccb_h.status & CAM_STATUS_MASK;
|
|
|
|
|
|
|
|
switch (ccb->ccb_h.func_code) {
|
|
|
|
case XPT_ACCEPT_TARGET_IO:
|
|
|
|
atio = (struct ccb_accept_tio *) ccb;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] ATIO seen in %s\n", atio->tag_id, __func__);
|
|
|
|
TAILQ_INSERT_TAIL(&softc->work_queue, &ccb->ccb_h, periph_links.tqe);
|
|
|
|
xpt_schedule(periph, 1);
|
|
|
|
break;
|
|
|
|
case XPT_IMMEDIATE_NOTIFY:
|
|
|
|
inot = (struct ccb_immediate_notify *) ccb;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] INOT for 0x%x seen in %s\n", inot->tag_id, inot->seq_id, __func__);
|
|
|
|
TAILQ_INSERT_TAIL(&softc->inot_queue, &ccb->ccb_h, periph_links.tqe);
|
|
|
|
xpt_schedule(periph, 1);
|
|
|
|
break;
|
|
|
|
case XPT_CONT_TARGET_IO:
|
|
|
|
if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
|
|
|
|
cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);
|
|
|
|
ccb->ccb_h.status &= ~CAM_DEV_QFRZN;
|
|
|
|
}
|
|
|
|
atio = ccb->ccb_h.ccb_atio;
|
|
|
|
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
|
|
|
cam_error_print(ccb, CAM_ESF_ALL, CAM_EPF_ALL);
|
|
|
|
xpt_action((union ccb *)atio);
|
|
|
|
} else if ((ccb->ccb_h.flags & CAM_SEND_STATUS) == 0) {
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] MID CTIO seen in %s\n", atio->tag_id, __func__);
|
|
|
|
TAILQ_INSERT_TAIL(&softc->rework_queue, &atio->ccb_h, periph_links.tqe);
|
|
|
|
xpt_schedule(periph, 1);
|
|
|
|
} else {
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] FINAL CTIO seen in %s\n", atio->tag_id, __func__);
|
|
|
|
xpt_action((union ccb *)atio);
|
|
|
|
}
|
|
|
|
xpt_release_ccb(ccb);
|
|
|
|
break;
|
|
|
|
case XPT_NOTIFY_ACKNOWLEDGE:
|
|
|
|
if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
|
|
|
|
cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);
|
|
|
|
ccb->ccb_h.status &= ~CAM_DEV_QFRZN;
|
|
|
|
}
|
|
|
|
inot = ccb->ccb_h.ccb_inot;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "[0x%x] recycle notify for tag 0x%x\n", inot->tag_id, inot->seq_id);
|
|
|
|
xpt_release_ccb(ccb);
|
|
|
|
xpt_action((union ccb *)inot);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
xpt_print(ccb->ccb_h.path, "unexpected code 0x%x\n", ccb->ccb_h.func_code);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isptargasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
|
|
|
|
{
|
|
|
|
struct ac_contract *acp = arg;
|
|
|
|
struct ac_device_changed *fc = (struct ac_device_changed *) acp->contract_data;
|
|
|
|
|
|
|
|
if (code != AC_CONTRACT) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
xpt_print(path, "0x%016llx Port ID 0x%06x %s\n", (unsigned long long) fc->wwpn, fc->port, fc->arrived? "arrived" : "departed");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_target_thread(ispsoftc_t *isp, int chan)
|
|
|
|
{
|
|
|
|
union ccb *ccb = NULL;
|
|
|
|
int i;
|
|
|
|
void *wchan;
|
|
|
|
cam_status status;
|
|
|
|
struct isptarg_softc *softc = NULL;
|
|
|
|
struct cam_periph *periph = NULL, *wperiph = NULL;
|
|
|
|
struct cam_path *path, *wpath;
|
|
|
|
struct cam_sim *sim;
|
|
|
|
|
|
|
|
if (disk_data == NULL) {
|
|
|
|
disk_size = roundup2(vm_kmem_size >> 1, (1ULL << 20));
|
|
|
|
if (disk_size < (50 << 20)) {
|
|
|
|
disk_size = 50 << 20;
|
|
|
|
}
|
|
|
|
disk_data = malloc(disk_size, M_ISPTARG, M_WAITOK | M_ZERO);
|
|
|
|
if (disk_data == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: could not allocate disk data", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
isp_prt(isp, ISP_LOGINFO, "allocated a %ju MiB disk", (uintmax_t) (disk_size >> 20));
|
|
|
|
}
|
|
|
|
junk_data = malloc(JUNK_SIZE, M_ISPTARG, M_WAITOK | M_ZERO);
|
|
|
|
if (junk_data == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: could not allocate junk", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
softc = malloc(sizeof (*softc), M_ISPTARG, M_WAITOK | M_ZERO);
|
|
|
|
if (softc == NULL) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: could not allocate softc", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
TAILQ_INIT(&softc->work_queue);
|
|
|
|
TAILQ_INIT(&softc->rework_queue);
|
|
|
|
TAILQ_INIT(&softc->running_queue);
|
|
|
|
TAILQ_INIT(&softc->inot_queue);
|
|
|
|
softc->isp = isp;
|
|
|
|
|
|
|
|
periphdriver_register(&isptargdriver);
|
|
|
|
ISP_GET_PC(isp, chan, sim, sim);
|
|
|
|
ISP_GET_PC(isp, chan, path, path);
|
|
|
|
status = xpt_create_path_unlocked(&wpath, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
|
|
|
|
if (status != CAM_REQ_CMP) {
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: could not allocate wildcard path", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
status = xpt_create_path_unlocked(&path, NULL, cam_sim_path(sim), 0, 0);
|
|
|
|
if (status != CAM_REQ_CMP) {
|
|
|
|
xpt_free_path(wpath);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: could not allocate path", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ccb = xpt_alloc_ccb();
|
|
|
|
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
status = cam_periph_alloc(isptargctor, NULL, isptargdtor, isptargstart, "isptarg", CAM_PERIPH_BIO, wpath, NULL, 0, softc);
|
|
|
|
if (status != CAM_REQ_CMP) {
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: cam_periph_alloc for wildcard failed", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
wperiph = cam_periph_find(wpath, "isptarg");
|
|
|
|
if (wperiph == NULL) {
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: wildcard periph already allocated but doesn't exist", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = cam_periph_alloc(isptargctor, NULL, isptargdtor, isptargstart, "isptarg", CAM_PERIPH_BIO, path, NULL, 0, softc);
|
|
|
|
if (status != CAM_REQ_CMP) {
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: cam_periph_alloc failed", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
periph = cam_periph_find(path, "isptarg");
|
|
|
|
if (periph == NULL) {
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: periph already allocated but doesn't exist", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = xpt_register_async(AC_CONTRACT, isptargasync, isp, wpath);
|
|
|
|
if (status != CAM_REQ_CMP) {
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
isp_prt(isp, ISP_LOGERR, "%s: xpt_register_async failed", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
|
|
|
|
ccb = xpt_alloc_ccb();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure role is none.
|
|
|
|
*/
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, periph->path, 10);
|
|
|
|
ccb->ccb_h.func_code = XPT_SET_SIM_KNOB;
|
|
|
|
ccb->knob.xport_specific.fc.role = KNOB_ROLE_NONE;
|
|
|
|
#ifdef ISP_TEST_WWNS
|
|
|
|
ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE | KNOB_VALID_ADDRESS;
|
|
|
|
ccb->knob.xport_specific.fc.wwnn = 0x508004d000000000ULL | (device_get_unit(isp->isp_osinfo.dev) << 8) | (chan << 16);
|
|
|
|
ccb->knob.xport_specific.fc.wwpn = 0x508004d000000001ULL | (device_get_unit(isp->isp_osinfo.dev) << 8) | (chan << 16);
|
|
|
|
#else
|
|
|
|
ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_action(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now enable luns
|
|
|
|
*/
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, periph->path, 10);
|
|
|
|
ccb->ccb_h.func_code = XPT_EN_LUN;
|
|
|
|
ccb->cel.enable = 1;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_action(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
if (ccb->ccb_h.status != CAM_REQ_CMP) {
|
|
|
|
xpt_free_ccb(ccb);
|
|
|
|
xpt_print(periph->path, "failed to enable lun (0x%x)\n", ccb->ccb_h.status);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 10);
|
|
|
|
ccb->ccb_h.func_code = XPT_EN_LUN;
|
|
|
|
ccb->cel.enable = 1;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_action(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
if (ccb->ccb_h.status != CAM_REQ_CMP) {
|
|
|
|
xpt_free_ccb(ccb);
|
|
|
|
xpt_print(wperiph->path, "failed to enable lun (0x%x)\n", ccb->ccb_h.status);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
xpt_free_ccb(ccb);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add resources
|
|
|
|
*/
|
|
|
|
ISP_GET_PC_ADDR(isp, chan, target_proc, wchan);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
ccb = malloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO);
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 1);
|
|
|
|
ccb->ccb_h.func_code = XPT_ACCEPT_TARGET_IO;
|
|
|
|
ccb->ccb_h.cbfcnp = isptarg_done;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_action(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
}
|
|
|
|
for (i = 0; i < NISP_TARG_CMDS; i++) {
|
|
|
|
ccb = malloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO);
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, periph->path, 1);
|
|
|
|
ccb->ccb_h.func_code = XPT_ACCEPT_TARGET_IO;
|
|
|
|
ccb->ccb_h.cbfcnp = isptarg_done;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_action(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
}
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
ccb = malloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO);
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 1);
|
|
|
|
ccb->ccb_h.func_code = XPT_IMMEDIATE_NOTIFY;
|
|
|
|
ccb->ccb_h.cbfcnp = isptarg_done;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_action(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
}
|
|
|
|
for (i = 0; i < NISP_TARG_NOTIFIES; i++) {
|
|
|
|
ccb = malloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO);
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, periph->path, 1);
|
|
|
|
ccb->ccb_h.func_code = XPT_IMMEDIATE_NOTIFY;
|
|
|
|
ccb->ccb_h.cbfcnp = isptarg_done;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_action(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now turn it all back on
|
|
|
|
*/
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, periph->path, 10);
|
|
|
|
ccb->ccb_h.func_code = XPT_SET_SIM_KNOB;
|
|
|
|
ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE;
|
|
|
|
ccb->knob.xport_specific.fc.role = KNOB_ROLE_TARGET;
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
xpt_action(ccb);
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Okay, while things are still active, sleep...
|
|
|
|
*/
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
for (;;) {
|
|
|
|
ISP_GET_PC(isp, chan, proc_active, i);
|
|
|
|
if (i == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
msleep(wchan, &isp->isp_lock, PUSER, "tsnooze", 0);
|
|
|
|
}
|
|
|
|
ISP_UNLOCK(isp);
|
2000-01-03 23:55:30 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
out:
|
|
|
|
if (wperiph) {
|
|
|
|
cam_periph_invalidate(wperiph);
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (periph) {
|
|
|
|
cam_periph_invalidate(periph);
|
|
|
|
}
|
|
|
|
if (junk_data) {
|
|
|
|
free(junk_data, M_ISPTARG);
|
|
|
|
}
|
|
|
|
if (disk_data) {
|
|
|
|
free(disk_data, M_ISPTARG);
|
|
|
|
}
|
|
|
|
if (softc) {
|
|
|
|
free(softc, M_ISPTARG);
|
|
|
|
}
|
|
|
|
xpt_free_path(path);
|
|
|
|
xpt_free_path(wpath);
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
2002-06-16 05:08:02 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
|
|
|
isp_target_thread_pi(void *arg)
|
2002-06-16 05:08:02 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
struct isp_spi *pi = arg;
|
|
|
|
isp_target_thread(cam_sim_softc(pi->sim), cam_sim_bus(pi->sim));
|
2002-06-16 05:08:02 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
|
|
|
isp_target_thread_fc(void *arg)
|
2002-06-16 05:08:02 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
struct isp_fc *fc = arg;
|
|
|
|
isp_target_thread(cam_sim_softc(fc->sim), cam_sim_bus(fc->sim));
|
|
|
|
}
|
2002-06-16 05:08:02 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static int
|
|
|
|
isptarg_rwparm(uint8_t *cdb, uint8_t *dp, uint64_t dl, uint32_t offset, uint8_t **kp, uint32_t *tl, int *lp)
|
|
|
|
{
|
|
|
|
uint32_t cnt, curcnt;
|
|
|
|
uint64_t lba;
|
|
|
|
|
|
|
|
switch (cdb[0]) {
|
|
|
|
case WRITE_16:
|
|
|
|
case READ_16:
|
|
|
|
cnt = (((uint32_t)cdb[10]) << 24) |
|
|
|
|
(((uint32_t)cdb[11]) << 16) |
|
|
|
|
(((uint32_t)cdb[12]) << 8) |
|
|
|
|
((uint32_t)cdb[13]);
|
|
|
|
|
|
|
|
lba = (((uint64_t)cdb[2]) << 56) |
|
|
|
|
(((uint64_t)cdb[3]) << 48) |
|
|
|
|
(((uint64_t)cdb[4]) << 40) |
|
|
|
|
(((uint64_t)cdb[5]) << 32) |
|
|
|
|
(((uint64_t)cdb[6]) << 24) |
|
|
|
|
(((uint64_t)cdb[7]) << 16) |
|
|
|
|
(((uint64_t)cdb[8]) << 8) |
|
|
|
|
((uint64_t)cdb[9]);
|
2002-06-16 05:08:02 +00:00
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case WRITE_12:
|
|
|
|
case READ_12:
|
|
|
|
cnt = (((uint32_t)cdb[6]) << 16) |
|
|
|
|
(((uint32_t)cdb[7]) << 8) |
|
|
|
|
((u_int32_t)cdb[8]);
|
|
|
|
|
|
|
|
lba = (((uint32_t)cdb[2]) << 24) |
|
|
|
|
(((uint32_t)cdb[3]) << 16) |
|
|
|
|
(((uint32_t)cdb[4]) << 8) |
|
|
|
|
((uint32_t)cdb[5]);
|
2002-06-16 05:08:02 +00:00
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case WRITE_10:
|
|
|
|
case READ_10:
|
|
|
|
cnt = (((uint32_t)cdb[7]) << 8) |
|
|
|
|
((u_int32_t)cdb[8]);
|
|
|
|
|
|
|
|
lba = (((uint32_t)cdb[2]) << 24) |
|
|
|
|
(((uint32_t)cdb[3]) << 16) |
|
|
|
|
(((uint32_t)cdb[4]) << 8) |
|
|
|
|
((uint32_t)cdb[5]);
|
2002-06-16 05:08:02 +00:00
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case WRITE_6:
|
|
|
|
case READ_6:
|
|
|
|
cnt = cdb[4];
|
|
|
|
if (cnt == 0) {
|
|
|
|
cnt = 256;
|
|
|
|
}
|
|
|
|
lba = (((uint32_t)cdb[1] & 0x1f) << 16) |
|
|
|
|
(((uint32_t)cdb[2]) << 8) |
|
|
|
|
((uint32_t)cdb[3]);
|
2002-06-16 05:08:02 +00:00
|
|
|
break;
|
|
|
|
default:
|
2009-08-01 01:04:26 +00:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
cnt <<= DISK_SHIFT;
|
|
|
|
lba <<= DISK_SHIFT;
|
|
|
|
|
|
|
|
if (offset == cnt) {
|
|
|
|
*lp = 1;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lba + cnt > dl) {
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
curcnt = MAX_ISP_TARG_TRANSFER;
|
|
|
|
if (offset + curcnt >= cnt) {
|
|
|
|
curcnt = cnt - offset;
|
|
|
|
*lp = 1;
|
|
|
|
} else {
|
|
|
|
*lp = 0;
|
2002-06-16 05:08:02 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
*tl = curcnt;
|
|
|
|
*kp = &dp[lba + offset];
|
2002-06-16 05:08:02 +00:00
|
|
|
return (0);
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
#endif
|
2000-01-03 23:55:30 +00:00
|
|
|
#endif
|
|
|
|
|
1998-09-15 08:42:56 +00:00
|
|
|
static void
|
2006-02-15 00:31:48 +00:00
|
|
|
isp_cam_async(void *cbarg, uint32_t code, struct cam_path *path, void *arg)
|
1998-09-15 08:42:56 +00:00
|
|
|
{
|
|
|
|
struct cam_sim *sim;
|
2006-04-21 18:30:01 +00:00
|
|
|
ispsoftc_t *isp;
|
1998-09-15 08:42:56 +00:00
|
|
|
|
|
|
|
sim = (struct cam_sim *)cbarg;
|
2006-04-21 18:30:01 +00:00
|
|
|
isp = (ispsoftc_t *) cam_sim_softc(sim);
|
1998-09-15 08:42:56 +00:00
|
|
|
switch (code) {
|
|
|
|
case AC_LOST_DEVICE:
|
1999-10-17 18:50:47 +00:00
|
|
|
if (IS_SCSI(isp)) {
|
2006-02-15 00:31:48 +00:00
|
|
|
uint16_t oflags, nflags;
|
2009-08-01 01:04:26 +00:00
|
|
|
int bus = cam_sim_bus(sim);
|
|
|
|
sdparam *sdp = SDPARAM(isp, bus);
|
2001-04-04 21:58:29 +00:00
|
|
|
int tgt;
|
1998-09-15 08:42:56 +00:00
|
|
|
|
1999-12-20 01:35:04 +00:00
|
|
|
tgt = xpt_path_target_id(path);
|
2002-10-10 17:29:05 +00:00
|
|
|
if (tgt >= 0) {
|
|
|
|
nflags = sdp->isp_devparam[tgt].nvrm_flags;
|
2001-04-04 21:58:29 +00:00
|
|
|
#ifndef ISP_TARGET_MODE
|
2002-10-10 17:29:05 +00:00
|
|
|
nflags &= DPARM_SAFE_DFLT;
|
|
|
|
if (isp->isp_loaded_fw) {
|
|
|
|
nflags |= DPARM_NARROW | DPARM_ASYNC;
|
|
|
|
}
|
2001-04-04 21:58:29 +00:00
|
|
|
#else
|
2002-10-10 17:29:05 +00:00
|
|
|
nflags = DPARM_DEFAULT;
|
2001-04-04 21:58:29 +00:00
|
|
|
#endif
|
2002-10-10 17:29:05 +00:00
|
|
|
oflags = sdp->isp_devparam[tgt].goal_flags;
|
|
|
|
sdp->isp_devparam[tgt].goal_flags = nflags;
|
|
|
|
sdp->isp_devparam[tgt].dev_update = 1;
|
2009-08-01 01:04:26 +00:00
|
|
|
sdp->update = 1;
|
|
|
|
(void) isp_control(isp, ISPCTL_UPDATE_PARAMS, bus);
|
2002-10-10 17:29:05 +00:00
|
|
|
sdp->isp_devparam[tgt].goal_flags = oflags;
|
|
|
|
}
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2001-03-01 02:14:54 +00:00
|
|
|
isp_prt(isp, ISP_LOGWARN, "isp_cam_async: Code 0x%x", code);
|
1998-09-15 08:42:56 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1998-12-28 19:22:27 +00:00
|
|
|
isp_poll(struct cam_sim *sim)
|
1998-09-15 08:42:56 +00:00
|
|
|
{
|
2006-04-21 18:30:01 +00:00
|
|
|
ispsoftc_t *isp = cam_sim_softc(sim);
|
2006-11-02 03:21:32 +00:00
|
|
|
uint32_t isr;
|
|
|
|
uint16_t sema, mbox;
|
2001-08-31 21:39:04 +00:00
|
|
|
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) {
|
2001-08-31 21:39:04 +00:00
|
|
|
isp_intr(isp, isr, sema, mbox);
|
I don't know what I was thinking- if I have two separate busses on on
SIM (as is true for the 1280 and the 12160), then I have to have separate
flags && status for *both* busses. *Whap*.
Implement condition variables for coordination with some target mode
events. It's nice to use these and not panic in obscure little places
in the kernel like 'propagate_priority' just because we went to sleep
holding a mutex, or some other absurd thing.
Remove some bogus ISP_UNLOCK calls. *Whap*.
No longer require that somebody do a lun enable on the wildcard device
to enable target mode. They are, in fact, orthogonal. A wildcard open
is a statement that somebody upstream is willing to accept commands which
are otherwise unrouteable. Now, for QLogic regular SCSI target mode, this
won't matter for a damn because we'll never see ATIOs for luns we haven't
enabled (are listening for, if you will). But for SCCLUN fibre channel
SCSI, we get all kinds of ATIOs. We can either reflect them back here
with minimal info (which is isp_target.c:isp_endcmd() is for), or the
wildcard device (nominally targbh) can handle them.
Do further checking against firmware attributes to see whether we can,
in fact, support target mode in Fibre Channel. For now, require SCCLUN
f/w to supoprt FC target mode.
This is an awful lot of change, but target mode *still* isn't quite right.
MFC after: 4 weeks
2001-09-04 21:53:12 +00:00
|
|
|
}
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
|
|
|
|
1999-10-17 18:50:47 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
static void
|
|
|
|
isp_watchdog(void *arg)
|
2000-05-09 01:08:21 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
struct ccb_scsiio *xs = arg;
|
|
|
|
ispsoftc_t *isp;
|
2006-02-15 00:31:48 +00:00
|
|
|
uint32_t handle;
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
isp = XS_ISP(xs);
|
|
|
|
|
2000-05-09 01:08:21 +00:00
|
|
|
handle = isp_find_handle(isp, xs);
|
|
|
|
if (handle) {
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* Make sure the command is *really* dead before we
|
|
|
|
* release the handle (and DMA resources) for reuse.
|
|
|
|
*/
|
|
|
|
(void) isp_control(isp, ISPCTL_ABORT_CMD, xs);
|
2000-07-05 06:44:17 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* After this point, the comamnd is really dead.
|
|
|
|
*/
|
|
|
|
if (XS_XFRLEN(xs)) {
|
|
|
|
ISP_DMAFREE(isp, xs, handle);
|
|
|
|
}
|
|
|
|
isp_destroy_handle(isp, handle);
|
|
|
|
xpt_print(xs->ccb_h.path, "watchdog timeout for handle 0x%x\n", handle);
|
|
|
|
XS_SETERR(xs, CAM_CMD_TIMEOUT);
|
|
|
|
isp_done(xs);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_make_here(ispsoftc_t *isp, int chan, int tgt)
|
2006-11-14 08:45:48 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
union ccb *ccb;
|
|
|
|
struct isp_fc *fc = ISP_FC_PC(isp, chan);
|
2007-05-05 20:17:23 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (isp_autoconfig == 0) {
|
|
|
|
return;
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
|
|
|
|
2007-02-23 05:51:57 +00:00
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* Allocate a CCB, create a wildcard path for this bus/target and schedule a rescan.
|
2007-02-23 05:51:57 +00:00
|
|
|
*/
|
2007-04-18 04:58:53 +00:00
|
|
|
ccb = xpt_alloc_ccb_nowait();
|
2007-02-23 05:51:57 +00:00
|
|
|
if (ccb == NULL) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGWARN, "Chan %d unable to alloc CCB for rescan", chan);
|
2007-02-23 05:51:57 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(fc->sim), tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
|
2007-02-23 05:51:57 +00:00
|
|
|
isp_prt(isp, ISP_LOGWARN, "unable to create path for rescan");
|
|
|
|
xpt_free_ccb(ccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
xpt_rescan(ccb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_make_gone(ispsoftc_t *isp, int chan, int tgt)
|
2007-02-23 05:51:57 +00:00
|
|
|
{
|
|
|
|
struct cam_path *tp;
|
2009-08-01 01:04:26 +00:00
|
|
|
struct isp_fc *fc = ISP_FC_PC(isp, chan);
|
|
|
|
|
|
|
|
if (isp_autoconfig == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (xpt_create_path(&tp, NULL, cam_sim_path(fc->sim), tgt, CAM_LUN_WILDCARD) == CAM_REQ_CMP) {
|
2007-02-23 05:51:57 +00:00
|
|
|
xpt_async(AC_LOST_DEVICE, tp, NULL);
|
|
|
|
xpt_free_path(tp);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Gone Device Timer Function- when we have decided that a device has gone
|
|
|
|
* away, we wait a specific period of time prior to telling the OS it has
|
|
|
|
* gone away.
|
|
|
|
*
|
|
|
|
* This timer function fires once a second and then scans the port database
|
|
|
|
* for devices that are marked dead but still have a virtual target assigned.
|
|
|
|
* We decrement a counter for that port database entry, and when it hits zero,
|
|
|
|
* we tell the OS the device has gone away.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
isp_gdt(void *arg)
|
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
struct isp_fc *fc = arg;
|
|
|
|
ispsoftc_t *isp = fc->isp;
|
|
|
|
int chan = fc - isp->isp_osinfo.pc.fc;
|
2006-11-14 08:45:48 +00:00
|
|
|
fcportdb_t *lp;
|
|
|
|
int dbidx, tgt, more_to_do = 0;
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "Chan %d GDT timer expired", chan);
|
2006-11-14 08:45:48 +00:00
|
|
|
for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) {
|
2009-08-01 01:04:26 +00:00
|
|
|
lp = &FCPARAM(isp, chan)->portdb[dbidx];
|
2006-11-14 08:45:48 +00:00
|
|
|
|
|
|
|
if (lp->state != FC_PORTDB_STATE_ZOMBIE) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (lp->dev_map_idx == 0 || lp->target_mode) {
|
2006-11-14 08:45:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (lp->new_reserved == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
lp->new_reserved -= 1;
|
|
|
|
if (lp->new_reserved != 0) {
|
|
|
|
more_to_do++;
|
|
|
|
continue;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
tgt = lp->dev_map_idx - 1;
|
|
|
|
FCPARAM(isp, chan)->isp_dev_map[tgt] = 0;
|
|
|
|
lp->dev_map_idx = 0;
|
2006-11-14 08:45:48 +00:00
|
|
|
lp->state = FC_PORTDB_STATE_NIL;
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom3, chan, lp->portid, tgt, "Gone Device Timeout");
|
|
|
|
isp_make_gone(isp, chan, tgt);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
|
|
|
if (more_to_do) {
|
2009-08-01 01:04:26 +00:00
|
|
|
fc->gdt_running = 1;
|
|
|
|
callout_reset(&fc->gdt, hz, isp_gdt, fc);
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d stopping Gone Device Timer", chan);
|
|
|
|
fc->gdt_running = 0;
|
2000-05-09 01:08:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-14 08:45:48 +00:00
|
|
|
/*
|
|
|
|
* Loop Down Timer Function- when loop goes down, a timer is started and
|
|
|
|
* and after it expires we come here and take all probational devices that
|
|
|
|
* the OS knows about and the tell the OS that they've gone away.
|
|
|
|
*
|
|
|
|
* We don't clear the devices out of our port database because, when loop
|
|
|
|
* come back up, we have to do some actual cleanup with the chip at that
|
|
|
|
* point (implicit PLOGO, e.g., to get the chip's port database state right).
|
|
|
|
*/
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
static void
|
2006-11-14 08:45:48 +00:00
|
|
|
isp_ldt(void *arg)
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
struct isp_fc *fc = arg;
|
|
|
|
ispsoftc_t *isp = fc->isp;
|
|
|
|
int chan = fc - isp->isp_osinfo.pc.fc;
|
2006-11-14 08:45:48 +00:00
|
|
|
fcportdb_t *lp;
|
|
|
|
int dbidx, tgt;
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d Loop Down Timer expired @ %lu", chan, (unsigned long) time_uptime);
|
2007-05-05 20:17:23 +00:00
|
|
|
|
2006-11-14 08:45:48 +00:00
|
|
|
/*
|
|
|
|
* Notify to the OS all targets who we now consider have departed.
|
|
|
|
*/
|
|
|
|
for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) {
|
2009-08-01 01:04:26 +00:00
|
|
|
lp = &FCPARAM(isp, chan)->portdb[dbidx];
|
2006-11-14 08:45:48 +00:00
|
|
|
|
|
|
|
if (lp->state != FC_PORTDB_STATE_PROBATIONAL) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (lp->dev_map_idx == 0 || lp->target_mode) {
|
2006-11-14 08:45:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: CLEAN UP AND COMPLETE ANY PENDING COMMANDS FIRST!
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark that we've announced that this device is gone....
|
|
|
|
*/
|
|
|
|
lp->reserved = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* but *don't* change the state of the entry. Just clear
|
|
|
|
* any target id stuff and announce to CAM that the
|
|
|
|
* device is gone. This way any necessary PLOGO stuff
|
|
|
|
* will happen when loop comes back up.
|
|
|
|
*/
|
2006-04-21 18:30:01 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
tgt = lp->dev_map_idx - 1;
|
|
|
|
FCPARAM(isp, chan)->isp_dev_map[tgt] = 0;
|
|
|
|
lp->dev_map_idx = 0;
|
|
|
|
lp->state = FC_PORTDB_STATE_NIL;
|
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom3, chan, lp->portid, tgt, "Loop Down Timeout");
|
|
|
|
isp_make_gone(isp, chan, tgt);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
2006-04-21 18:30:01 +00:00
|
|
|
|
2006-11-14 08:45:48 +00:00
|
|
|
/*
|
|
|
|
* The loop down timer has expired. Wake up the kthread
|
|
|
|
* to notice that fact (or make it false).
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
fc->loop_dead = 1;
|
|
|
|
fc->loop_down_time = fc->loop_down_limit+1;
|
|
|
|
wakeup(fc);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_kthread(void *arg)
|
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
struct isp_fc *fc = arg;
|
|
|
|
ispsoftc_t *isp = fc->isp;
|
|
|
|
int chan = fc - isp->isp_osinfo.pc.fc;
|
2006-11-14 08:45:48 +00:00
|
|
|
int slp = 0;
|
2007-05-05 20:17:23 +00:00
|
|
|
mtx_lock(&isp->isp_osinfo.lock);
|
2009-08-01 01:04:26 +00:00
|
|
|
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
for (;;) {
|
2006-11-14 08:45:48 +00:00
|
|
|
int wasfrozen, lb, lim;
|
2002-07-08 17:42:47 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d checking FC state", __func__, chan);
|
|
|
|
lb = isp_fc_runstate(isp, chan, 250000);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Our action is different based upon whether we're supporting
|
|
|
|
* Initiator mode or not. If we are, we might freeze the simq
|
|
|
|
* when loop is down and set all sorts of different delays to
|
|
|
|
* check again.
|
|
|
|
*
|
|
|
|
* If not, we simply just wait for loop to come up.
|
|
|
|
*/
|
|
|
|
if (lb && (fc->role & ISP_ROLE_INITIATOR)) {
|
2006-11-14 08:45:48 +00:00
|
|
|
/*
|
|
|
|
* Increment loop down time by the last sleep interval
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
fc->loop_down_time += slp;
|
2006-11-02 03:21:32 +00:00
|
|
|
|
|
|
|
if (lb < 0) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d FC loop not up (down count %d)", __func__, chan, fc->loop_down_time);
|
2006-11-02 03:21:32 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d FC got to %d (down count %d)", __func__, chan, lb, fc->loop_down_time);
|
2001-07-25 04:23:52 +00:00
|
|
|
}
|
2006-11-02 03:21:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we've never seen loop up and we've waited longer
|
2006-11-14 08:45:48 +00:00
|
|
|
* than quickboot time, or we've seen loop up but we've
|
|
|
|
* waited longer than loop_down_limit, give up and go
|
|
|
|
* to sleep until loop comes up.
|
2006-11-02 03:21:32 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if (FCPARAM(isp, chan)->loop_seen_once == 0) {
|
2006-11-14 08:45:48 +00:00
|
|
|
lim = isp_quickboot_time;
|
2006-11-02 03:21:32 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
lim = fc->loop_down_limit;
|
2006-11-02 03:21:32 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (fc->loop_down_time >= lim) {
|
|
|
|
isp_freeze_loopdown(isp, chan, "loop limit hit");
|
2006-11-14 08:45:48 +00:00
|
|
|
slp = 0;
|
2009-08-01 01:04:26 +00:00
|
|
|
} else if (fc->loop_down_time < 10) {
|
2006-11-14 08:45:48 +00:00
|
|
|
slp = 1;
|
2009-08-01 01:04:26 +00:00
|
|
|
} else if (fc->loop_down_time < 30) {
|
2006-11-14 08:45:48 +00:00
|
|
|
slp = 5;
|
2009-08-01 01:04:26 +00:00
|
|
|
} else if (fc->loop_down_time < 60) {
|
2006-11-14 08:45:48 +00:00
|
|
|
slp = 10;
|
2009-08-01 01:04:26 +00:00
|
|
|
} else if (fc->loop_down_time < 120) {
|
2006-11-14 08:45:48 +00:00
|
|
|
slp = 20;
|
2006-11-02 03:21:32 +00:00
|
|
|
} else {
|
2006-11-14 08:45:48 +00:00
|
|
|
slp = 30;
|
2006-11-02 03:21:32 +00:00
|
|
|
}
|
2006-11-14 08:45:48 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
} else if (lb) {
|
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d FC Loop Down", __func__, chan);
|
|
|
|
fc->loop_down_time += slp;
|
|
|
|
slp = 60;
|
2006-11-02 03:21:32 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d FC state OK", __func__, chan);
|
|
|
|
fc->loop_down_time = 0;
|
2006-11-02 03:21:32 +00:00
|
|
|
slp = 0;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
2002-07-08 17:42:47 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
|
2001-07-25 04:23:52 +00:00
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* If this is past the first loop up or the loop is dead and if we'd frozen the simq, unfreeze it
|
|
|
|
* now so that CAM can start sending us commands.
|
|
|
|
*
|
|
|
|
* If the FC state isn't okay yet, they'll hit that in isp_start which will freeze the queue again
|
|
|
|
* or kill the commands, as appropriate.
|
2001-07-25 04:23:52 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
if (FCPARAM(isp, chan)->loop_seen_once || fc->loop_dead) {
|
|
|
|
wasfrozen = fc->simqfrozen & SIMQFRZ_LOOPDOWN;
|
|
|
|
fc->simqfrozen &= ~SIMQFRZ_LOOPDOWN;
|
|
|
|
if (wasfrozen && fc->simqfrozen == 0) {
|
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d releasing simq", __func__, chan);
|
|
|
|
xpt_release_simq(fc->sim, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d sleep time %d", __func__, chan, slp);
|
|
|
|
|
|
|
|
msleep(fc, &isp->isp_osinfo.lock, PRIBIO, "ispf", slp * hz);
|
|
|
|
|
2006-11-02 03:21:32 +00:00
|
|
|
/*
|
|
|
|
* If slp is zero, we're waking up for the first time after
|
|
|
|
* things have been okay. In this case, we set a deferral state
|
|
|
|
* for all commands and delay hysteresis seconds before starting
|
|
|
|
* the FC state evaluation. This gives the loop/fabric a chance
|
|
|
|
* to settle.
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if (slp == 0 && fc->hysteresis) {
|
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d sleep hysteresis ticks %d", __func__, chan, fc->hysteresis * hz);
|
|
|
|
(void) msleep(&isp_fabric_hysteresis, &isp->isp_osinfo.lock, PRIBIO, "ispT", (fc->hysteresis * hz));
|
2006-11-02 03:21:32 +00:00
|
|
|
}
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
2007-05-05 20:17:23 +00:00
|
|
|
mtx_unlock(&isp->isp_osinfo.lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isp_action(struct cam_sim *sim, union ccb *ccb)
|
|
|
|
{
|
|
|
|
int bus, tgt, ts, error, lim;
|
2006-04-21 18:30:01 +00:00
|
|
|
ispsoftc_t *isp;
|
1998-10-15 23:46:33 +00:00
|
|
|
struct ccb_trans_settings *cts;
|
1998-09-15 08:42:56 +00:00
|
|
|
|
|
|
|
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("isp_action\n"));
|
|
|
|
|
2006-04-21 18:30:01 +00:00
|
|
|
isp = (ispsoftc_t *)cam_sim_softc(sim);
|
2009-08-01 01:04:26 +00:00
|
|
|
mtx_assert(&isp->isp_lock, MA_OWNED);
|
|
|
|
|
|
|
|
if (isp->isp_state != ISP_RUNSTATE && ccb->ccb_h.func_code == XPT_SCSI_IO) {
|
1999-03-17 05:04:39 +00:00
|
|
|
isp_init(isp);
|
|
|
|
if (isp->isp_state != ISP_INITSTATE) {
|
|
|
|
/*
|
|
|
|
* Lie. Say it was a selection timeout.
|
|
|
|
*/
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
ccb->ccb_h.status = CAM_SEL_TIMEOUT | CAM_DEV_QFRZN;
|
1999-08-16 20:11:45 +00:00
|
|
|
xpt_freeze_devq(ccb->ccb_h.path, 1);
|
1999-03-17 05:04:39 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
isp->isp_state = ISP_RUNSTATE;
|
|
|
|
}
|
2000-08-01 06:31:44 +00:00
|
|
|
isp_prt(isp, ISP_LOGDEBUG2, "isp_action code %x", ccb->ccb_h.func_code);
|
2007-05-05 20:17:23 +00:00
|
|
|
ISP_PCMD(ccb) = NULL;
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
|
1998-09-15 08:42:56 +00:00
|
|
|
switch (ccb->ccb_h.func_code) {
|
|
|
|
case XPT_SCSI_IO: /* Execute the requested I/O operation */
|
2009-08-01 01:04:26 +00:00
|
|
|
bus = XS_CHANNEL(ccb);
|
1998-09-15 08:42:56 +00:00
|
|
|
/*
|
|
|
|
* Do a couple of preliminary checks...
|
|
|
|
*/
|
|
|
|
if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) {
|
|
|
|
if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-08-16 20:11:45 +00:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (ccb->ccb_h.target_id > (ISP_MAX_TARGETS(isp) - 1)) {
|
2006-12-05 07:50:23 +00:00
|
|
|
xpt_print(ccb->ccb_h.path, "invalid target\n");
|
1999-08-16 20:11:45 +00:00
|
|
|
ccb->ccb_h.status = CAM_PATH_INVALID;
|
|
|
|
} else if (ccb->ccb_h.target_lun > (ISP_MAX_LUNS(isp) - 1)) {
|
2006-12-05 07:50:23 +00:00
|
|
|
xpt_print(ccb->ccb_h.path, "invalid lun\n");
|
1999-08-16 20:11:45 +00:00
|
|
|
ccb->ccb_h.status = CAM_PATH_INVALID;
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
|
|
|
if (ccb->ccb_h.status == CAM_PATH_INVALID) {
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
1999-08-16 20:11:45 +00:00
|
|
|
#endif
|
2007-05-05 20:17:23 +00:00
|
|
|
ccb->csio.scsi_status = SCSI_STATUS_OK;
|
|
|
|
if (isp_get_pcmd(isp, ccb)) {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "out of PCMDs");
|
|
|
|
cam_freeze_devq(ccb->ccb_h.path);
|
2009-08-01 01:04:26 +00:00
|
|
|
cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 250, 0);
|
2007-05-05 20:17:23 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
2000-08-01 06:31:44 +00:00
|
|
|
error = isp_start((XS_T *) ccb);
|
1999-08-16 20:11:45 +00:00
|
|
|
switch (error) {
|
1998-09-15 08:42:56 +00:00
|
|
|
case CMD_QUEUED:
|
2006-11-14 08:45:48 +00:00
|
|
|
XS_CMD_S_CLEAR(ccb);
|
1998-09-15 08:42:56 +00:00
|
|
|
ccb->ccb_h.status |= CAM_SIM_QUEUED;
|
2007-05-05 20:17:23 +00:00
|
|
|
if (ccb->ccb_h.timeout == CAM_TIME_INFINITY) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ts = ccb->ccb_h.timeout;
|
|
|
|
if (ts == CAM_TIME_DEFAULT) {
|
|
|
|
ts = 60*1000;
|
2000-05-09 01:08:21 +00:00
|
|
|
}
|
2007-05-05 20:17:23 +00:00
|
|
|
ts = isp_mstohz(ts);
|
2009-08-01 01:04:26 +00:00
|
|
|
callout_reset(&PISP_PCMD(ccb)->wdog, ts, isp_watchdog, ccb);
|
1998-09-15 08:42:56 +00:00
|
|
|
break;
|
1999-08-16 20:11:45 +00:00
|
|
|
case CMD_RQLATER:
|
2006-11-02 03:21:32 +00:00
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* We get this result for FC devices if the loop state isn't ready yet
|
|
|
|
* or if the device in question has gone zombie on us.
|
|
|
|
*
|
|
|
|
* If we've never seen Loop UP at all, we requeue this request and wait
|
|
|
|
* for the initial loop up delay to expire.
|
2006-11-02 03:21:32 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
lim = ISP_FC_PC(isp, bus)->loop_down_limit;
|
|
|
|
if (FCPARAM(isp, bus)->loop_seen_once == 0 || ISP_FC_PC(isp, bus)->loop_down_time >= lim) {
|
|
|
|
if (FCPARAM(isp, bus)->loop_seen_once == 0) {
|
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "%d.%d loop not seen yet @ %lu", XS_TGT(ccb), XS_LUN(ccb), (unsigned long) time_uptime);
|
|
|
|
} else {
|
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "%d.%d downtime (%d) > lim (%d)", XS_TGT(ccb), XS_LUN(ccb), ISP_FC_PC(isp, bus)->loop_down_time, lim);
|
|
|
|
}
|
|
|
|
ccb->ccb_h.status = CAM_SEL_TIMEOUT|CAM_DEV_QFRZN;
|
2006-11-14 08:45:48 +00:00
|
|
|
xpt_freeze_devq(ccb->ccb_h.path, 1);
|
2007-05-05 20:17:23 +00:00
|
|
|
isp_free_pcmd(isp, ccb);
|
2001-07-25 04:23:52 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "%d.%d retry later", XS_TGT(ccb), XS_LUN(ccb));
|
2006-11-14 08:45:48 +00:00
|
|
|
cam_freeze_devq(ccb->ccb_h.path);
|
2009-08-01 01:04:26 +00:00
|
|
|
cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 1000, 0);
|
2006-11-14 08:45:48 +00:00
|
|
|
XS_SETERR(ccb, CAM_REQUEUE_REQ);
|
2007-05-05 20:17:23 +00:00
|
|
|
isp_free_pcmd(isp, ccb);
|
1998-09-15 08:42:56 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
1999-08-16 20:11:45 +00:00
|
|
|
case CMD_EAGAIN:
|
2007-05-05 20:17:23 +00:00
|
|
|
isp_free_pcmd(isp, ccb);
|
2009-08-01 01:04:26 +00:00
|
|
|
cam_freeze_devq(ccb->ccb_h.path);
|
|
|
|
cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 100, 0);
|
|
|
|
XS_SETERR(ccb, CAM_REQUEUE_REQ);
|
1998-09-15 08:42:56 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
1999-08-16 20:11:45 +00:00
|
|
|
case CMD_COMPLETE:
|
|
|
|
isp_done((struct ccb_scsiio *) ccb);
|
|
|
|
break;
|
|
|
|
default:
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGERR, "What's this? 0x%x at %d in file %s", error, __LINE__, __FILE__);
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
XS_SETERR(ccb, CAM_REQ_CMP_ERR);
|
2007-05-05 20:17:23 +00:00
|
|
|
isp_free_pcmd(isp, ccb);
|
1999-08-16 20:11:45 +00:00
|
|
|
xpt_done(ccb);
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2000-01-03 23:55:30 +00:00
|
|
|
#ifdef ISP_TARGET_MODE
|
2009-08-01 01:04:26 +00:00
|
|
|
case XPT_EN_LUN: /* Enable/Disable LUN as a target */
|
|
|
|
if (ccb->cel.enable) {
|
|
|
|
isp_enable_lun(isp, ccb);
|
|
|
|
} else {
|
|
|
|
isp_disable_lun(isp, ccb);
|
2004-02-08 19:17:56 +00:00
|
|
|
}
|
1998-09-15 08:42:56 +00:00
|
|
|
break;
|
2009-09-15 02:25:03 +00:00
|
|
|
case XPT_IMMED_NOTIFY:
|
2009-08-01 01:04:26 +00:00
|
|
|
case XPT_IMMEDIATE_NOTIFY: /* Add Immediate Notify Resource */
|
2000-01-03 23:55:30 +00:00
|
|
|
case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */
|
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
tstate_t *tptr = get_lun_statep(isp, XS_CHANNEL(ccb), ccb->ccb_h.target_lun);
|
2000-01-03 23:55:30 +00:00
|
|
|
if (tptr == NULL) {
|
2009-08-01 01:04:26 +00:00
|
|
|
tptr = get_lun_statep(isp, XS_CHANNEL(ccb), CAM_LUN_WILDCARD);
|
|
|
|
}
|
|
|
|
if (tptr == NULL) {
|
|
|
|
const char *str;
|
|
|
|
uint32_t tag;
|
|
|
|
|
|
|
|
if (ccb->ccb_h.func_code == XPT_IMMEDIATE_NOTIFY) {
|
|
|
|
str = "XPT_IMMEDIATE_NOTIFY";
|
|
|
|
tag = ccb->cin1.seq_id;
|
|
|
|
} else {
|
|
|
|
tag = ccb->atio.tag_id;
|
|
|
|
str = "XPT_ACCEPT_TARGET_IO";
|
|
|
|
}
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGWARN, ccb->ccb_h.path, "%s: [0x%x] no state pointer found for %s\n", __func__, tag, str);
|
|
|
|
dump_tstates(isp, XS_CHANNEL(ccb));
|
|
|
|
ccb->ccb_h.status = CAM_DEV_NOT_THERE;
|
2000-01-03 23:55:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2000-07-18 06:58:28 +00:00
|
|
|
ccb->ccb_h.sim_priv.entries[0].field = 0;
|
|
|
|
ccb->ccb_h.sim_priv.entries[1].ptr = isp;
|
2002-06-16 05:08:02 +00:00
|
|
|
ccb->ccb_h.flags = 0;
|
|
|
|
|
2000-01-03 23:55:30 +00:00
|
|
|
if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) {
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ccb->atio.tag_id) {
|
|
|
|
atio_private_data_t *atp = isp_get_atpd(isp, tptr, ccb->atio.tag_id);
|
|
|
|
if (atp) {
|
|
|
|
isp_put_atpd(isp, tptr, atp);
|
|
|
|
}
|
|
|
|
}
|
2002-06-16 05:08:02 +00:00
|
|
|
tptr->atio_count++;
|
2009-08-01 01:04:26 +00:00
|
|
|
SLIST_INSERT_HEAD(&tptr->atios, &ccb->ccb_h, sim_links.sle);
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "Put FREE ATIO (tag id 0x%x), count now %d\n",
|
|
|
|
((struct ccb_accept_tio *)ccb)->tag_id, tptr->atio_count);
|
|
|
|
} else if (ccb->ccb_h.func_code == XPT_IMMEDIATE_NOTIFY) {
|
|
|
|
if (ccb->cin1.tag_id) {
|
|
|
|
inot_private_data_t *ntp = isp_find_ntpd(isp, tptr, ccb->cin1.tag_id, ccb->cin1.seq_id);
|
|
|
|
if (ntp) {
|
|
|
|
isp_put_ntpd(isp, tptr, ntp);
|
|
|
|
}
|
|
|
|
}
|
2004-02-07 03:47:33 +00:00
|
|
|
tptr->inot_count++;
|
2009-08-01 01:04:26 +00:00
|
|
|
SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h, sim_links.sle);
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "Put FREE INOT, (seq id 0x%x) count now %d\n",
|
|
|
|
((struct ccb_immediate_notify *)ccb)->seq_id, tptr->inot_count);
|
2009-09-15 02:25:03 +00:00
|
|
|
} else if (ccb->ccb_h.func_code == XPT_IMMED_NOTIFY) {
|
|
|
|
tptr->inot_count++;
|
|
|
|
SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h, sim_links.sle);
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "Put FREE INOT, (seq id 0x%x) count now %d\n",
|
|
|
|
((struct ccb_immediate_notify *)ccb)->seq_id, tptr->inot_count);
|
2000-01-03 23:55:30 +00:00
|
|
|
}
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INPROG;
|
|
|
|
break;
|
|
|
|
}
|
2009-09-15 02:25:03 +00:00
|
|
|
case XPT_NOTIFY_ACK:
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case XPT_NOTIFY_ACKNOWLEDGE: /* notify ack */
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
tstate_t *tptr;
|
|
|
|
inot_private_data_t *ntp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: Because we cannot guarantee that the path information in the notify acknowledge ccb
|
|
|
|
* XXX: matches that for the immediate notify, we have to *search* for the notify structure
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* All the relevant path information is in the associated immediate notify
|
|
|
|
*/
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: [0x%x] NOTIFY ACKNOWLEDGE for 0x%x seen\n", __func__, ccb->cna2.tag_id, ccb->cna2.seq_id);
|
|
|
|
ntp = get_ntp_from_tagdata(isp, ccb->cna2.tag_id, ccb->cna2.seq_id, &tptr);
|
|
|
|
if (ntp == NULL) {
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGWARN, ccb->ccb_h.path, "%s: [0x%x] XPT_NOTIFY_ACKNOWLEDGE of 0x%x cannot find ntp private data\n", __func__,
|
|
|
|
ccb->cna2.tag_id, ccb->cna2.seq_id);
|
|
|
|
ccb->ccb_h.status = CAM_DEV_NOT_THERE;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (isp_handle_platform_target_notify_ack(isp, &ntp->rd.nt)) {
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
cam_freeze_devq(ccb->ccb_h.path);
|
|
|
|
cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 1000, 0);
|
|
|
|
XS_SETERR(ccb, CAM_REQUEUE_REQ);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
isp_put_ntpd(isp, tptr, ntp);
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: [0x%x] calling xpt_done for tag 0x%x\n", __func__, ccb->cna2.tag_id, ccb->cna2.seq_id);
|
|
|
|
xpt_done(ccb);
|
2000-01-03 23:55:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
case XPT_CONT_TARGET_IO:
|
|
|
|
isp_target_start_ctio(isp, ccb);
|
|
|
|
break;
|
2000-01-03 23:55:30 +00:00
|
|
|
#endif
|
1998-09-15 08:42:56 +00:00
|
|
|
case XPT_RESET_DEV: /* BDR the specified SCSI device */
|
2000-01-03 23:55:30 +00:00
|
|
|
|
|
|
|
bus = cam_sim_bus(xpt_path_sim(ccb->ccb_h.path));
|
|
|
|
tgt = ccb->ccb_h.target_id;
|
|
|
|
tgt |= (bus << 16);
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
error = isp_control(isp, ISPCTL_RESET_DEV, bus, tgt);
|
1998-09-15 08:42:56 +00:00
|
|
|
if (error) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
|
|
|
} else {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
}
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case XPT_ABORT: /* Abort the specified CCB */
|
|
|
|
{
|
|
|
|
union ccb *accb = ccb->cab.abort_ccb;
|
|
|
|
switch (accb->ccb_h.func_code) {
|
|
|
|
#ifdef ISP_TARGET_MODE
|
|
|
|
case XPT_ACCEPT_TARGET_IO:
|
|
|
|
isp_target_mark_aborted(isp, accb);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case XPT_SCSI_IO:
|
|
|
|
error = isp_control(isp, ISPCTL_ABORT_CMD, ccb);
|
|
|
|
if (error) {
|
|
|
|
ccb->ccb_h.status = CAM_UA_ABORT;
|
2006-12-18 23:50:30 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
2006-12-18 23:50:30 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#define IS_CURRENT_SETTINGS(c) (c->type == CTS_TYPE_CURRENT_SETTINGS)
|
|
|
|
case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */
|
|
|
|
cts = &ccb->cts;
|
|
|
|
if (!IS_CURRENT_SETTINGS(cts)) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tgt = cts->ccb_h.target_id;
|
|
|
|
bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path));
|
|
|
|
if (IS_SCSI(isp)) {
|
|
|
|
struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi;
|
|
|
|
struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi;
|
|
|
|
sdparam *sdp = SDPARAM(isp, bus);
|
2006-02-15 00:31:48 +00:00
|
|
|
uint16_t *dptr;
|
2001-07-04 18:54:29 +00:00
|
|
|
|
2006-12-03 07:22:15 +00:00
|
|
|
if (spi->valid == 0 && scsi->valid == 0) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
|
2001-07-04 18:54:29 +00:00
|
|
|
/*
|
2001-07-30 01:00:21 +00:00
|
|
|
* We always update (internally) from goal_flags
|
2001-07-04 18:54:29 +00:00
|
|
|
* so any request to change settings just gets
|
|
|
|
* vectored to that location.
|
|
|
|
*/
|
2001-07-30 01:00:21 +00:00
|
|
|
dptr = &sdp->isp_devparam[tgt].goal_flags;
|
2001-07-04 18:54:29 +00:00
|
|
|
|
|
|
|
if ((spi->valid & CTS_SPI_VALID_DISC) != 0) {
|
|
|
|
if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0)
|
|
|
|
*dptr |= DPARM_DISC;
|
|
|
|
else
|
|
|
|
*dptr &= ~DPARM_DISC;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) {
|
|
|
|
if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0)
|
|
|
|
*dptr |= DPARM_TQING;
|
|
|
|
else
|
|
|
|
*dptr &= ~DPARM_TQING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) {
|
|
|
|
if (spi->bus_width == MSG_EXT_WDTR_BUS_16_BIT)
|
|
|
|
*dptr |= DPARM_WIDE;
|
|
|
|
else
|
|
|
|
*dptr &= ~DPARM_WIDE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: FIX ME
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) && (spi->valid & CTS_SPI_VALID_SYNC_RATE) && (spi->sync_period && spi->sync_offset)) {
|
2001-07-04 18:54:29 +00:00
|
|
|
*dptr |= DPARM_SYNC;
|
2001-07-30 01:00:21 +00:00
|
|
|
/*
|
|
|
|
* XXX: CHECK FOR LEGALITY
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
sdp->isp_devparam[tgt].goal_period = spi->sync_period;
|
|
|
|
sdp->isp_devparam[tgt].goal_offset = spi->sync_offset;
|
2001-07-04 18:54:29 +00:00
|
|
|
} else {
|
|
|
|
*dptr &= ~DPARM_SYNC;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "SET (%d.%d.%d) to flags %x off %x per %x", bus, tgt, cts->ccb_h.target_lun, sdp->isp_devparam[tgt].goal_flags,
|
|
|
|
sdp->isp_devparam[tgt].goal_offset, sdp->isp_devparam[tgt].goal_period);
|
1998-09-15 08:42:56 +00:00
|
|
|
sdp->isp_devparam[tgt].dev_update = 1;
|
2009-08-01 01:04:26 +00:00
|
|
|
sdp->update = 1;
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
case XPT_GET_TRAN_SETTINGS:
|
|
|
|
cts = &ccb->cts;
|
|
|
|
tgt = cts->ccb_h.target_id;
|
2009-08-01 01:04:26 +00:00
|
|
|
bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path));
|
1999-10-17 18:50:47 +00:00
|
|
|
if (IS_FC(isp)) {
|
2009-08-01 01:04:26 +00:00
|
|
|
fcparam *fcp = FCPARAM(isp, bus);
|
|
|
|
struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi;
|
|
|
|
struct ccb_trans_settings_fc *fc = &cts->xport_specific.fc;
|
2001-07-04 18:54:29 +00:00
|
|
|
|
|
|
|
cts->protocol = PROTO_SCSI;
|
|
|
|
cts->protocol_version = SCSI_REV_2;
|
|
|
|
cts->transport = XPORT_FC;
|
|
|
|
cts->transport_version = 0;
|
|
|
|
|
2006-12-09 01:30:05 +00:00
|
|
|
scsi->valid = CTS_SCSI_VALID_TQ;
|
|
|
|
scsi->flags = CTS_SCSI_FLAGS_TAG_ENB;
|
2001-07-04 18:54:29 +00:00
|
|
|
fc->valid = CTS_FC_VALID_SPEED;
|
2007-08-23 15:57:13 +00:00
|
|
|
fc->bitrate = 100000;
|
2009-08-01 01:04:26 +00:00
|
|
|
fc->bitrate *= fcp->isp_gbspeed;
|
2001-07-04 18:54:29 +00:00
|
|
|
if (tgt > 0 && tgt < MAX_FC_TARG) {
|
2006-11-02 03:21:32 +00:00
|
|
|
fcportdb_t *lp = &fcp->portdb[tgt];
|
2001-07-04 18:54:29 +00:00
|
|
|
fc->wwnn = lp->node_wwn;
|
|
|
|
fc->wwpn = lp->port_wwn;
|
|
|
|
fc->port = lp->portid;
|
2009-08-01 01:04:26 +00:00
|
|
|
fc->valid |= CTS_FC_VALID_WWNN | CTS_FC_VALID_WWPN | CTS_FC_VALID_PORT;
|
2001-07-04 18:54:29 +00:00
|
|
|
}
|
1998-09-15 08:42:56 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi;
|
|
|
|
struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi;
|
|
|
|
sdparam *sdp = SDPARAM(isp, bus);
|
2006-02-15 00:31:48 +00:00
|
|
|
uint16_t dval, pval, oval;
|
1998-09-15 08:42:56 +00:00
|
|
|
|
2001-07-04 18:54:29 +00:00
|
|
|
if (IS_CURRENT_SETTINGS(cts)) {
|
1999-05-12 19:00:16 +00:00
|
|
|
sdp->isp_devparam[tgt].dev_refresh = 1;
|
2009-08-01 01:04:26 +00:00
|
|
|
sdp->update = 1;
|
|
|
|
(void) isp_control(isp, ISPCTL_UPDATE_PARAMS, bus);
|
2001-07-30 01:00:21 +00:00
|
|
|
dval = sdp->isp_devparam[tgt].actv_flags;
|
|
|
|
oval = sdp->isp_devparam[tgt].actv_offset;
|
|
|
|
pval = sdp->isp_devparam[tgt].actv_period;
|
1999-03-25 22:52:45 +00:00
|
|
|
} else {
|
2001-07-30 01:00:21 +00:00
|
|
|
dval = sdp->isp_devparam[tgt].nvrm_flags;
|
|
|
|
oval = sdp->isp_devparam[tgt].nvrm_offset;
|
|
|
|
pval = sdp->isp_devparam[tgt].nvrm_period;
|
1999-03-25 22:52:45 +00:00
|
|
|
}
|
1998-09-15 08:42:56 +00:00
|
|
|
|
2001-07-04 18:54:29 +00:00
|
|
|
cts->protocol = PROTO_SCSI;
|
|
|
|
cts->protocol_version = SCSI_REV_2;
|
|
|
|
cts->transport = XPORT_SPI;
|
|
|
|
cts->transport_version = 2;
|
|
|
|
|
2006-12-03 07:22:15 +00:00
|
|
|
spi->valid = 0;
|
|
|
|
scsi->valid = 0;
|
|
|
|
spi->flags = 0;
|
|
|
|
scsi->flags = 0;
|
2001-07-04 18:54:29 +00:00
|
|
|
if (dval & DPARM_DISC) {
|
|
|
|
spi->flags |= CTS_SPI_FLAGS_DISC_ENB;
|
|
|
|
}
|
2001-07-30 01:00:21 +00:00
|
|
|
if ((dval & DPARM_SYNC) && oval && pval) {
|
2001-07-04 18:54:29 +00:00
|
|
|
spi->sync_offset = oval;
|
|
|
|
spi->sync_period = pval;
|
2006-12-03 07:22:15 +00:00
|
|
|
} else {
|
|
|
|
spi->sync_offset = 0;
|
|
|
|
spi->sync_period = 0;
|
2001-07-04 18:54:29 +00:00
|
|
|
}
|
2006-12-03 07:22:15 +00:00
|
|
|
spi->valid |= CTS_SPI_VALID_SYNC_OFFSET;
|
|
|
|
spi->valid |= CTS_SPI_VALID_SYNC_RATE;
|
2001-07-04 18:54:29 +00:00
|
|
|
spi->valid |= CTS_SPI_VALID_BUS_WIDTH;
|
|
|
|
if (dval & DPARM_WIDE) {
|
|
|
|
spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
|
|
|
|
} else {
|
|
|
|
spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
|
|
|
|
}
|
|
|
|
if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) {
|
|
|
|
scsi->valid = CTS_SCSI_VALID_TQ;
|
2006-12-03 07:22:15 +00:00
|
|
|
if (dval & DPARM_TQING) {
|
|
|
|
scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
|
|
|
|
}
|
2001-07-04 18:54:29 +00:00
|
|
|
spi->valid |= CTS_SPI_VALID_DISC;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "GET %s (%d.%d.%d) to flags %x off %x per %x", IS_CURRENT_SETTINGS(cts)? "ACTIVE" : "NVRAM",
|
2006-12-03 07:22:15 +00:00
|
|
|
bus, tgt, cts->ccb_h.target_lun, dval, oval, pval);
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case XPT_CALC_GEOMETRY:
|
2009-08-01 01:04:26 +00:00
|
|
|
cam_calc_geometry(&ccb->ccg, 1);
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
1998-09-15 08:42:56 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
case XPT_RESET_BUS: /* Reset the specified bus */
|
|
|
|
bus = cam_sim_bus(sim);
|
|
|
|
error = isp_control(isp, ISPCTL_RESET_BUS, bus);
|
|
|
|
if (error) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
1998-09-15 08:42:56 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (bootverbose) {
|
|
|
|
xpt_print(ccb->ccb_h.path, "reset bus on channel %d\n", bus);
|
|
|
|
}
|
|
|
|
if (IS_FC(isp)) {
|
|
|
|
xpt_async(AC_BUS_RESET, ISP_FC_PC(isp, bus)->path, 0);
|
2006-04-21 18:30:01 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
xpt_async(AC_BUS_RESET, ISP_SPI_PC(isp, bus)->path, 0);
|
2006-04-21 18:30:01 +00:00
|
|
|
}
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
case XPT_TERM_IO: /* Terminate the I/O process */
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
1998-09-15 08:42:56 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
case XPT_SET_SIM_KNOB: /* Set SIM knobs */
|
|
|
|
{
|
|
|
|
struct ccb_sim_knob *kp = &ccb->knob;
|
|
|
|
fcparam *fcp;
|
|
|
|
|
|
|
|
|
|
|
|
if (!IS_FC(isp)) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus = cam_sim_bus(xpt_path_sim(kp->ccb_h.path));
|
|
|
|
fcp = FCPARAM(isp, bus);
|
|
|
|
|
|
|
|
if (kp->xport_specific.fc.valid & KNOB_VALID_ADDRESS) {
|
|
|
|
fcp->isp_wwnn = ISP_FC_PC(isp, bus)->def_wwnn = kp->xport_specific.fc.wwnn;
|
|
|
|
fcp->isp_wwpn = ISP_FC_PC(isp, bus)->def_wwpn = kp->xport_specific.fc.wwpn;
|
|
|
|
isp_prt(isp, ISP_LOGALL, "Setting Channel %d wwns to 0x%jx 0x%jx", bus, fcp->isp_wwnn, fcp->isp_wwpn);
|
|
|
|
}
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
if (kp->xport_specific.fc.valid & KNOB_VALID_ROLE) {
|
|
|
|
int rchange = 0;
|
|
|
|
int newrole = 0;
|
|
|
|
|
|
|
|
switch (kp->xport_specific.fc.role) {
|
|
|
|
case KNOB_ROLE_NONE:
|
|
|
|
if (fcp->role != ISP_ROLE_NONE) {
|
|
|
|
rchange = 1;
|
|
|
|
newrole = ISP_ROLE_NONE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case KNOB_ROLE_TARGET:
|
|
|
|
if (fcp->role != ISP_ROLE_TARGET) {
|
|
|
|
rchange = 1;
|
|
|
|
newrole = ISP_ROLE_TARGET;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case KNOB_ROLE_INITIATOR:
|
|
|
|
if (fcp->role != ISP_ROLE_INITIATOR) {
|
|
|
|
rchange = 1;
|
|
|
|
newrole = ISP_ROLE_INITIATOR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case KNOB_ROLE_BOTH:
|
2009-09-15 02:25:03 +00:00
|
|
|
#if 0
|
2009-08-01 01:04:26 +00:00
|
|
|
if (fcp->role != ISP_ROLE_BOTH) {
|
|
|
|
rchange = 1;
|
|
|
|
newrole = ISP_ROLE_BOTH;
|
|
|
|
}
|
2009-09-15 02:25:03 +00:00
|
|
|
#else
|
|
|
|
/*
|
|
|
|
* We don't really support dual role at present on FC cards.
|
|
|
|
*
|
|
|
|
* We should, but a bunch of things are currently broken,
|
|
|
|
* so don't allow it.
|
|
|
|
*/
|
|
|
|
isp_prt(isp, ISP_LOGERR, "cannot support dual role at present");
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
#endif
|
2009-08-01 01:04:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rchange) {
|
|
|
|
if (isp_fc_change_role(isp, bus, newrole) != 0) {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
|
|
|
#ifdef ISP_TARGET_MODE
|
|
|
|
} else if (newrole == ISP_ROLE_TARGET || newrole == ISP_ROLE_BOTH) {
|
|
|
|
isp_enable_deferred_luns(isp, bus);
|
2006-04-21 18:30:01 +00:00
|
|
|
#endif
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
}
|
1999-02-09 01:08:38 +00:00
|
|
|
}
|
1998-09-15 08:42:56 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
|
|
|
case XPT_GET_SIM_KNOB: /* Set SIM knobs */
|
|
|
|
{
|
|
|
|
struct ccb_sim_knob *kp = &ccb->knob;
|
1998-09-15 08:42:56 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_FC(isp)) {
|
|
|
|
fcparam *fcp;
|
|
|
|
|
|
|
|
bus = cam_sim_bus(xpt_path_sim(kp->ccb_h.path));
|
|
|
|
fcp = FCPARAM(isp, bus);
|
|
|
|
|
|
|
|
kp->xport_specific.fc.wwnn = fcp->isp_wwnn;
|
|
|
|
kp->xport_specific.fc.wwpn = fcp->isp_wwpn;
|
|
|
|
switch (fcp->role) {
|
|
|
|
case ISP_ROLE_NONE:
|
|
|
|
kp->xport_specific.fc.role = KNOB_ROLE_NONE;
|
|
|
|
break;
|
|
|
|
case ISP_ROLE_TARGET:
|
|
|
|
kp->xport_specific.fc.role = KNOB_ROLE_TARGET;
|
|
|
|
break;
|
|
|
|
case ISP_ROLE_INITIATOR:
|
|
|
|
kp->xport_specific.fc.role = KNOB_ROLE_INITIATOR;
|
|
|
|
break;
|
|
|
|
case ISP_ROLE_BOTH:
|
|
|
|
kp->xport_specific.fc.role = KNOB_ROLE_BOTH;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
kp->xport_specific.fc.valid = KNOB_VALID_ADDRESS | KNOB_VALID_ROLE;
|
|
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
} else {
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
}
|
1998-09-15 08:42:56 +00:00
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
1998-09-15 08:42:56 +00:00
|
|
|
case XPT_PATH_INQ: /* Path routing inquiry */
|
|
|
|
{
|
|
|
|
struct ccb_pathinq *cpi = &ccb->cpi;
|
|
|
|
|
|
|
|
cpi->version_num = 1;
|
2000-01-03 23:55:30 +00:00
|
|
|
#ifdef ISP_TARGET_MODE
|
2001-04-04 21:58:29 +00:00
|
|
|
cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO;
|
2000-01-03 23:55:30 +00:00
|
|
|
#else
|
1998-09-15 08:42:56 +00:00
|
|
|
cpi->target_sprt = 0;
|
2000-01-03 23:55:30 +00:00
|
|
|
#endif
|
1998-09-15 08:42:56 +00:00
|
|
|
cpi->hba_eng_cnt = 0;
|
1999-08-16 20:11:45 +00:00
|
|
|
cpi->max_target = ISP_MAX_TARGETS(isp) - 1;
|
|
|
|
cpi->max_lun = ISP_MAX_LUNS(isp) - 1;
|
|
|
|
cpi->bus_id = cam_sim_bus(sim);
|
2009-08-01 01:04:26 +00:00
|
|
|
bus = cam_sim_bus(xpt_path_sim(cpi->ccb_h.path));
|
1999-03-25 22:52:45 +00:00
|
|
|
if (IS_FC(isp)) {
|
2009-08-01 01:04:26 +00:00
|
|
|
fcparam *fcp = FCPARAM(isp, bus);
|
|
|
|
|
1999-03-25 22:52:45 +00:00
|
|
|
cpi->hba_misc = PIM_NOBUSRESET;
|
2009-08-01 01:04:26 +00:00
|
|
|
|
1999-08-16 20:11:45 +00:00
|
|
|
/*
|
|
|
|
* Because our loop ID can shift from time to time,
|
|
|
|
* make our initiator ID out of range of our bus.
|
|
|
|
*/
|
|
|
|
cpi->initiator_id = cpi->max_target + 1;
|
|
|
|
|
Add a number of interrelated CAM feature enhancements and bug fixes.
NOTE: These changes will require recompilation of any userland
applications, like cdrecord, xmcd, etc., that use the CAM passthrough
interface. A make world is recommended.
camcontrol.[c8]:
- We now support two new commands, "tags" and "negotiate".
- The tags commands allows users to view the number of tagged
openings for a device as well as a number of other related
parameters, and it allows users to set tagged openings for
a device.
- The negotiate command allows users to enable and disable
disconnection and tagged queueing, set sync rates, offsets
and bus width. Note that not all of those features are
available for all controllers. Only the adv, ahc, and ncr
drivers fully support all of the features at this point.
Some cards do not allow the setting of sync rates, offsets and
the like, and some of the drivers don't have any facilities to
do so. Some drivers, like the adw driver, only support enabling
or disabling sync negotiation, but do not support setting sync
rates.
- new description in the camcontrol man page of how to format a disk
- cleanup of the camcontrol inquiry command
- add support in the 'devlist' command for skipping unconfigured devices if
-v was not specified on the command line.
- make use of the new base_transfer_speed in the path inquiry CCB.
- fix CCB bzero cases
cam_xpt.c, cam_sim.[ch], cam_ccb.h:
- new flags on many CCB function codes to designate whether they're
non-immediate, use a user-supplied CCB, and can only be passed from
userland programs via the xpt device. Use these flags in the transport
layer and pass driver to categorize CCBs.
- new flag in the transport layer device matching code for device nodes
that indicates whether a device is unconfigured
- bump the CAM version from 0x10 to 0x11
- Change the CAM ioctls to use the version as their group code, so we can
force users to recompile code even when the CCB size doesn't change.
- add + fill in a new value in the path inquiry CCB, base_transfer_speed.
Remove a corresponding field from the cam_sim structure, and add code to
every SIM to set this field to the proper value.
- Fix the set transfer settings code in the transport layer.
scsi_cd.c:
- make some variables volatile instead of just casting them in various
places
- fix a race condition in the changer code
- attach unless we get a "logical unit not supported" error. This should
fix all of the cases where people have devices that return weird errors
when they don't have media in the drive.
scsi_da.c:
- attach unless we get a "logical unit not supported" error
scsi_pass.c:
- for immediate CCBs, just malloc a CCB to send the user request in. This
gets rid of the 'held' count problem in camcontrol tags.
scsi_pass.h:
- change the CAM ioctls to use the CAM version as their group code.
adv driver:
- Allow changing the sync rate and offset separately.
adw driver
- Allow changing the sync rate and offset separately.
aha driver:
- Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs.
ahc driver:
- Allow setting offset and sync rate separately
bt driver:
- Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs.
NCR driver:
- Fix the ultra/ultra 2 negotiation bug
- allow setting both the sync rate and offset separately
Other HBA drivers:
- Put code in to set the base_transfer_speed field for
XPT_GET_TRAN_SETTINGS CCBs.
Reviewed by: gibbs, mjacob (isp), imp (aha)
1999-05-06 20:16:39 +00:00
|
|
|
/*
|
2009-08-01 01:04:26 +00:00
|
|
|
* Set base transfer capabilities for Fibre Channel, for this HBA.
|
Add a number of interrelated CAM feature enhancements and bug fixes.
NOTE: These changes will require recompilation of any userland
applications, like cdrecord, xmcd, etc., that use the CAM passthrough
interface. A make world is recommended.
camcontrol.[c8]:
- We now support two new commands, "tags" and "negotiate".
- The tags commands allows users to view the number of tagged
openings for a device as well as a number of other related
parameters, and it allows users to set tagged openings for
a device.
- The negotiate command allows users to enable and disable
disconnection and tagged queueing, set sync rates, offsets
and bus width. Note that not all of those features are
available for all controllers. Only the adv, ahc, and ncr
drivers fully support all of the features at this point.
Some cards do not allow the setting of sync rates, offsets and
the like, and some of the drivers don't have any facilities to
do so. Some drivers, like the adw driver, only support enabling
or disabling sync negotiation, but do not support setting sync
rates.
- new description in the camcontrol man page of how to format a disk
- cleanup of the camcontrol inquiry command
- add support in the 'devlist' command for skipping unconfigured devices if
-v was not specified on the command line.
- make use of the new base_transfer_speed in the path inquiry CCB.
- fix CCB bzero cases
cam_xpt.c, cam_sim.[ch], cam_ccb.h:
- new flags on many CCB function codes to designate whether they're
non-immediate, use a user-supplied CCB, and can only be passed from
userland programs via the xpt device. Use these flags in the transport
layer and pass driver to categorize CCBs.
- new flag in the transport layer device matching code for device nodes
that indicates whether a device is unconfigured
- bump the CAM version from 0x10 to 0x11
- Change the CAM ioctls to use the version as their group code, so we can
force users to recompile code even when the CCB size doesn't change.
- add + fill in a new value in the path inquiry CCB, base_transfer_speed.
Remove a corresponding field from the cam_sim structure, and add code to
every SIM to set this field to the proper value.
- Fix the set transfer settings code in the transport layer.
scsi_cd.c:
- make some variables volatile instead of just casting them in various
places
- fix a race condition in the changer code
- attach unless we get a "logical unit not supported" error. This should
fix all of the cases where people have devices that return weird errors
when they don't have media in the drive.
scsi_da.c:
- attach unless we get a "logical unit not supported" error
scsi_pass.c:
- for immediate CCBs, just malloc a CCB to send the user request in. This
gets rid of the 'held' count problem in camcontrol tags.
scsi_pass.h:
- change the CAM ioctls to use the CAM version as their group code.
adv driver:
- Allow changing the sync rate and offset separately.
adw driver
- Allow changing the sync rate and offset separately.
aha driver:
- Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs.
ahc driver:
- Allow setting offset and sync rate separately
bt driver:
- Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs.
NCR driver:
- Fix the ultra/ultra 2 negotiation bug
- allow setting both the sync rate and offset separately
Other HBA drivers:
- Put code in to set the base_transfer_speed field for
XPT_GET_TRAN_SETTINGS CCBs.
Reviewed by: gibbs, mjacob (isp), imp (aha)
1999-05-06 20:16:39 +00:00
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
cpi->base_transfer_speed = 4000000;
|
|
|
|
} else if (IS_23XX(isp)) {
|
|
|
|
cpi->base_transfer_speed = 2000000;
|
|
|
|
} else {
|
|
|
|
cpi->base_transfer_speed = 1000000;
|
|
|
|
}
|
1999-08-16 20:11:45 +00:00
|
|
|
cpi->hba_inquiry = PI_TAG_ABLE;
|
2001-07-04 18:54:29 +00:00
|
|
|
cpi->transport = XPORT_FC;
|
2006-12-18 23:50:30 +00:00
|
|
|
cpi->transport_version = 0;
|
2009-08-01 01:04:26 +00:00
|
|
|
cpi->xport_specific.fc.wwnn = fcp->isp_wwnn;
|
|
|
|
cpi->xport_specific.fc.wwpn = fcp->isp_wwpn;
|
|
|
|
cpi->xport_specific.fc.port = fcp->isp_portid;
|
|
|
|
cpi->xport_specific.fc.bitrate = fcp->isp_gbspeed * 1000;
|
1998-09-15 08:42:56 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
sdparam *sdp = SDPARAM(isp, bus);
|
1999-08-16 20:11:45 +00:00
|
|
|
cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
|
2000-04-21 19:18:06 +00:00
|
|
|
cpi->hba_misc = 0;
|
1999-05-11 05:10:06 +00:00
|
|
|
cpi->initiator_id = sdp->isp_initiator_id;
|
Add a number of interrelated CAM feature enhancements and bug fixes.
NOTE: These changes will require recompilation of any userland
applications, like cdrecord, xmcd, etc., that use the CAM passthrough
interface. A make world is recommended.
camcontrol.[c8]:
- We now support two new commands, "tags" and "negotiate".
- The tags commands allows users to view the number of tagged
openings for a device as well as a number of other related
parameters, and it allows users to set tagged openings for
a device.
- The negotiate command allows users to enable and disable
disconnection and tagged queueing, set sync rates, offsets
and bus width. Note that not all of those features are
available for all controllers. Only the adv, ahc, and ncr
drivers fully support all of the features at this point.
Some cards do not allow the setting of sync rates, offsets and
the like, and some of the drivers don't have any facilities to
do so. Some drivers, like the adw driver, only support enabling
or disabling sync negotiation, but do not support setting sync
rates.
- new description in the camcontrol man page of how to format a disk
- cleanup of the camcontrol inquiry command
- add support in the 'devlist' command for skipping unconfigured devices if
-v was not specified on the command line.
- make use of the new base_transfer_speed in the path inquiry CCB.
- fix CCB bzero cases
cam_xpt.c, cam_sim.[ch], cam_ccb.h:
- new flags on many CCB function codes to designate whether they're
non-immediate, use a user-supplied CCB, and can only be passed from
userland programs via the xpt device. Use these flags in the transport
layer and pass driver to categorize CCBs.
- new flag in the transport layer device matching code for device nodes
that indicates whether a device is unconfigured
- bump the CAM version from 0x10 to 0x11
- Change the CAM ioctls to use the version as their group code, so we can
force users to recompile code even when the CCB size doesn't change.
- add + fill in a new value in the path inquiry CCB, base_transfer_speed.
Remove a corresponding field from the cam_sim structure, and add code to
every SIM to set this field to the proper value.
- Fix the set transfer settings code in the transport layer.
scsi_cd.c:
- make some variables volatile instead of just casting them in various
places
- fix a race condition in the changer code
- attach unless we get a "logical unit not supported" error. This should
fix all of the cases where people have devices that return weird errors
when they don't have media in the drive.
scsi_da.c:
- attach unless we get a "logical unit not supported" error
scsi_pass.c:
- for immediate CCBs, just malloc a CCB to send the user request in. This
gets rid of the 'held' count problem in camcontrol tags.
scsi_pass.h:
- change the CAM ioctls to use the CAM version as their group code.
adv driver:
- Allow changing the sync rate and offset separately.
adw driver
- Allow changing the sync rate and offset separately.
aha driver:
- Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs.
ahc driver:
- Allow setting offset and sync rate separately
bt driver:
- Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs.
NCR driver:
- Fix the ultra/ultra 2 negotiation bug
- allow setting both the sync rate and offset separately
Other HBA drivers:
- Put code in to set the base_transfer_speed field for
XPT_GET_TRAN_SETTINGS CCBs.
Reviewed by: gibbs, mjacob (isp), imp (aha)
1999-05-06 20:16:39 +00:00
|
|
|
cpi->base_transfer_speed = 3300;
|
2001-07-04 18:54:29 +00:00
|
|
|
cpi->transport = XPORT_SPI;
|
2006-12-18 23:50:30 +00:00
|
|
|
cpi->transport_version = 2;
|
1998-09-15 08:42:56 +00:00
|
|
|
}
|
2001-07-04 18:54:29 +00:00
|
|
|
cpi->protocol = PROTO_SCSI;
|
|
|
|
cpi->protocol_version = SCSI_REV_2;
|
1998-09-15 08:42:56 +00:00
|
|
|
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
|
|
|
|
strncpy(cpi->hba_vid, "Qlogic", HBA_IDLEN);
|
|
|
|
strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
|
|
|
|
cpi->unit_number = cam_sim_unit(sim);
|
|
|
|
cpi->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
|
|
xpt_done(ccb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1998-12-05 01:31:21 +00:00
|
|
|
|
|
|
|
#define ISPDDB (CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB)
|
2006-11-14 08:45:48 +00:00
|
|
|
|
1998-12-05 01:31:21 +00:00
|
|
|
void
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_done(XS_T *sccb)
|
1998-12-05 01:31:21 +00:00
|
|
|
{
|
2006-04-21 18:30:01 +00:00
|
|
|
ispsoftc_t *isp = XS_ISP(sccb);
|
1998-12-05 01:31:21 +00:00
|
|
|
|
|
|
|
if (XS_NOERR(sccb))
|
|
|
|
XS_SETERR(sccb, CAM_REQ_CMP);
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if ((sccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (sccb->scsi_status != SCSI_STATUS_OK)) {
|
1998-12-05 01:31:21 +00:00
|
|
|
sccb->ccb_h.status &= ~CAM_STATUS_MASK;
|
2009-08-01 01:04:26 +00:00
|
|
|
if ((sccb->scsi_status == SCSI_STATUS_CHECK_COND) && (sccb->ccb_h.status & CAM_AUTOSNS_VALID) == 0) {
|
2000-01-04 03:45:49 +00:00
|
|
|
sccb->ccb_h.status |= CAM_AUTOSENSE_FAIL;
|
|
|
|
} else {
|
|
|
|
sccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
|
|
|
|
}
|
1998-12-05 01:31:21 +00:00
|
|
|
}
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
|
1999-08-16 20:11:45 +00:00
|
|
|
sccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
1998-12-05 01:31:21 +00:00
|
|
|
if ((sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGDEBUG0, "target %d lun %d CAM status 0x%x SCSI status 0x%x", XS_TGT(sccb), XS_LUN(sccb), sccb->ccb_h.status, sccb->scsi_status);
|
1998-12-05 01:31:21 +00:00
|
|
|
if ((sccb->ccb_h.status & CAM_DEV_QFRZN) == 0) {
|
|
|
|
sccb->ccb_h.status |= CAM_DEV_QFRZN;
|
1999-08-16 20:11:45 +00:00
|
|
|
xpt_freeze_devq(sccb->ccb_h.path, 1);
|
1998-12-05 01:31:21 +00:00
|
|
|
}
|
|
|
|
}
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
if ((CAM_DEBUGGED(sccb->ccb_h.path, ISPDDB)) && (sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
|
|
|
xpt_print(sccb->ccb_h.path, "cam completion status 0x%x\n", sccb->ccb_h.status);
|
1998-12-05 01:31:21 +00:00
|
|
|
}
|
Add in the enabling of interrupts (to isp_attach). Clean up a busted
comment. Check against firmware state- not loop state when enabling
target mode. Other changes have to do with no longer enabling/disabling
interrupts at will.
Rearchitect command watchdog timeouts-
First of all, set the timeout period for a command that has a
timeout (in isp_action) to the period of time requested *plus* two
seconds. We don't want the Qlogic firmware and the host system to
race each other to report a dead command (the watchdog is there to
catch dead and/or broken firmware).
Next, make sure that the command being watched isn't done yet. If
it's not done yet, check for INT_PENDING and call isp_intr- if that
said it serviced an interrupt, check to see whether the command is
now done (this is what the "IN WATCHDOG" private flag is for- if
isp_intr completes the command, it won't call xpt_done on it because
isp_watchdog is still looking at the command).
If no interrupt was pending, or the command wasn't completed, check
to see if we've set the private 'grace period' flag. If so, the
command really *is* dead, so report it as dead and complete it with
a CAM_CMD_TIMEOUT value.
If the grace period flag wasn't set, set it and issue a SYNCHRONIZE_ALL
Marker Request Queue entry and re-set the timeout for one second
from now (see Revision 1.45 isp.c notes for more on this) to give
the firmware a final chance to complete this command.
2000-06-27 19:31:02 +00:00
|
|
|
|
|
|
|
XS_CMD_S_DONE(sccb);
|
2009-08-01 01:04:26 +00:00
|
|
|
callout_stop(&PISP_PCMD(sccb)->wdog);
|
|
|
|
XS_CMD_S_CLEAR(sccb);
|
|
|
|
isp_free_pcmd(isp, (union ccb *) sccb);
|
|
|
|
xpt_done((union ccb *) sccb);
|
1998-12-05 01:31:21 +00:00
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
void
|
|
|
|
isp_async(ispsoftc_t *isp, ispasync_t cmd, ...)
|
|
|
|
{
|
|
|
|
int bus;
|
|
|
|
static const char prom[] = "Chan %d PortID 0x%06x handle 0x%x role %s %s WWPN 0x%08x%08x";
|
|
|
|
static const char prom2[] = "Chan %d PortID 0x%06x handle 0x%x role %s %s tgt %u WWPN 0x%08x%08x";
|
2006-11-14 08:45:48 +00:00
|
|
|
char *msg = NULL;
|
2006-11-02 03:21:32 +00:00
|
|
|
target_id_t tgt;
|
|
|
|
fcportdb_t *lp;
|
|
|
|
struct cam_path *tmppath;
|
2009-08-01 01:04:26 +00:00
|
|
|
va_list ap;
|
2006-11-02 03:21:32 +00:00
|
|
|
|
1999-01-30 07:29:00 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case ISPASYNC_NEW_TGT_PARAMS:
|
1999-08-16 20:11:45 +00:00
|
|
|
{
|
2001-07-04 18:54:29 +00:00
|
|
|
struct ccb_trans_settings_scsi *scsi;
|
|
|
|
struct ccb_trans_settings_spi *spi;
|
1999-08-16 20:11:45 +00:00
|
|
|
int flags, tgt;
|
2009-08-01 01:04:26 +00:00
|
|
|
sdparam *sdp;
|
2001-07-04 18:54:29 +00:00
|
|
|
struct ccb_trans_settings cts;
|
1999-08-16 20:11:45 +00:00
|
|
|
|
2006-05-22 06:49:49 +00:00
|
|
|
memset(&cts, 0, sizeof (struct ccb_trans_settings));
|
2001-07-04 18:54:29 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
tgt = va_arg(ap, int);
|
|
|
|
va_end(ap);
|
|
|
|
sdp = SDPARAM(isp, bus);
|
|
|
|
|
|
|
|
if (xpt_create_path(&tmppath, NULL, cam_sim_path(ISP_SPI_PC(isp, bus)->sim), tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "isp_async cannot make temp path for %d.%d", tgt, bus);
|
1999-08-16 20:11:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2001-07-30 01:00:21 +00:00
|
|
|
flags = sdp->isp_devparam[tgt].actv_flags;
|
2001-07-04 18:54:29 +00:00
|
|
|
cts.type = CTS_TYPE_CURRENT_SETTINGS;
|
|
|
|
cts.protocol = PROTO_SCSI;
|
|
|
|
cts.transport = XPORT_SPI;
|
|
|
|
|
|
|
|
scsi = &cts.proto_specific.scsi;
|
|
|
|
spi = &cts.xport_specific.spi;
|
|
|
|
|
|
|
|
if (flags & DPARM_TQING) {
|
|
|
|
scsi->valid |= CTS_SCSI_VALID_TQ;
|
|
|
|
scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
|
|
|
|
}
|
|
|
|
|
1999-08-16 20:11:45 +00:00
|
|
|
if (flags & DPARM_DISC) {
|
2001-07-04 18:54:29 +00:00
|
|
|
spi->valid |= CTS_SPI_VALID_DISC;
|
|
|
|
spi->flags |= CTS_SPI_FLAGS_DISC_ENB;
|
|
|
|
}
|
|
|
|
spi->flags |= CTS_SPI_VALID_BUS_WIDTH;
|
|
|
|
if (flags & DPARM_WIDE) {
|
|
|
|
spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
|
|
|
|
} else {
|
|
|
|
spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
|
|
|
|
}
|
|
|
|
if (flags & DPARM_SYNC) {
|
|
|
|
spi->valid |= CTS_SPI_VALID_SYNC_RATE;
|
|
|
|
spi->valid |= CTS_SPI_VALID_SYNC_OFFSET;
|
2001-07-30 01:00:21 +00:00
|
|
|
spi->sync_period = sdp->isp_devparam[tgt].actv_period;
|
|
|
|
spi->sync_offset = sdp->isp_devparam[tgt].actv_offset;
|
2001-07-04 18:54:29 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGDEBUG2, "NEW_TGT_PARAMS bus %d tgt %d period %x offset %x flags %x", bus, tgt, sdp->isp_devparam[tgt].actv_period, sdp->isp_devparam[tgt].actv_offset, flags);
|
2001-07-04 18:54:29 +00:00
|
|
|
xpt_setup_ccb(&cts.ccb_h, tmppath, 1);
|
|
|
|
xpt_async(AC_TRANSFER_NEG, tmppath, &cts);
|
1999-08-16 20:11:45 +00:00
|
|
|
xpt_free_path(tmppath);
|
1999-01-30 07:29:00 +00:00
|
|
|
break;
|
1999-08-16 20:11:45 +00:00
|
|
|
}
|
1999-03-17 05:04:39 +00:00
|
|
|
case ISPASYNC_BUS_RESET:
|
2009-08-01 01:04:26 +00:00
|
|
|
{
|
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
va_end(ap);
|
|
|
|
isp_prt(isp, ISP_LOGINFO, "SCSI bus reset on bus %d detected", bus);
|
|
|
|
if (IS_FC(isp)) {
|
|
|
|
xpt_async(AC_BUS_RESET, ISP_FC_PC(isp, bus)->path, NULL);
|
|
|
|
} else {
|
|
|
|
xpt_async(AC_BUS_RESET, ISP_SPI_PC(isp, bus)->path, NULL);
|
1999-03-17 05:04:39 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
case ISPASYNC_LIP:
|
2006-11-14 08:45:48 +00:00
|
|
|
if (msg == NULL) {
|
|
|
|
msg = "LIP Received";
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
2006-11-14 08:45:48 +00:00
|
|
|
/* FALLTHROUGH */
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
case ISPASYNC_LOOP_RESET:
|
2006-11-14 08:45:48 +00:00
|
|
|
if (msg == NULL) {
|
|
|
|
msg = "LOOP Reset";
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
}
|
2006-11-14 08:45:48 +00:00
|
|
|
/* FALLTHROUGH */
|
1999-03-17 05:04:39 +00:00
|
|
|
case ISPASYNC_LOOP_DOWN:
|
2009-08-01 01:04:26 +00:00
|
|
|
{
|
|
|
|
struct isp_fc *fc;
|
2006-11-14 08:45:48 +00:00
|
|
|
if (msg == NULL) {
|
|
|
|
msg = "LOOP Down";
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
FCPARAM(isp, bus)->link_active = 1;
|
|
|
|
|
|
|
|
fc = ISP_FC_PC(isp, bus);
|
|
|
|
/*
|
|
|
|
* We don't do any simq freezing if we are only in target mode
|
|
|
|
*/
|
|
|
|
if (fc->role & ISP_ROLE_INITIATOR) {
|
|
|
|
if (fc->path) {
|
|
|
|
isp_freeze_loopdown(isp, bus, msg);
|
|
|
|
}
|
|
|
|
if (fc->ldt_running == 0) {
|
|
|
|
fc->ldt_running = 1;
|
|
|
|
callout_reset(&fc->ldt, fc->loop_down_limit * hz, isp_ldt, fc);
|
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "starting Loop Down Timer @ %lu", (unsigned long) time_uptime);
|
|
|
|
}
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGINFO, "Chan %d: %s", bus, msg);
|
1999-03-17 05:04:39 +00:00
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
1999-03-17 05:04:39 +00:00
|
|
|
case ISPASYNC_LOOP_UP:
|
2009-08-01 01:04:26 +00:00
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
va_end(ap);
|
Spring MegaChange #1.
----
Make a device for each ISP- really usable only with devfs and add an ioctl
entry point (this can be used to (re)set debug levels, reset the HBA,
rescan the fabric, issue lips, etc).
----
Add in a kernel thread for Fibre Channel cards. The purpose of this
thread is to be woken up to clean up after Fibre Channel events
block things. Basically, any FC event that casts doubt on the
location or identify of FC devices blocks the queues. When, and
if, we get the PORT DATABASE CHANGED or NAME SERVER DATABASE CHANGED
async event, we activate the kthread which will then, in full thread
context, re-evaluate the local loop and/or the fabric. When it's
satisfied that things are stable, it can then release the blocked
queues and let commands flow again.
The prior mechanism was a lazy evaluation. That is, the next command
to come down the pipe after change events would pay the full price
for re-evaluation. And if this was done off of a softcall, it really
could hang up the system.
These changes brings the FreeBSD port more in line with the Solaris,
Linux and NetBSD ports. It also, more importantly, gets us being
more proactive about topology changes which could then be reflected
upwards to CAM so that the periph driver can be informed sooner
rather than later when things arrive or depart.
---
Add in the (correct) usage of locking macros- we now have lock transition
macros which allow us to transition from holding the CAM lock (Giant)
and grabbing the softc lock and vice versa. Switch over to having this
HBA do real locking. Some folks claim this won't be a win. They're right.
But you have to start somewhere, and this will begin to teach us how
to DTRT for HBAs, etc.
--
Start putting in prototype 2300 support. Add back in LIP
and Loop Reset as async events that each platform will handle.
Add in another int_bogus instrumentation point.
Do some more substantial target mode cleanups.
MFC after: 8 weeks
2001-05-28 21:20:43 +00:00
|
|
|
/*
|
|
|
|
* Now we just note that Loop has come up. We don't
|
|
|
|
* actually do anything because we're waiting for a
|
|
|
|
* Change Notify before activating the FC cleanup
|
|
|
|
* thread to look at the state of the loop again.
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
FCPARAM(isp, bus)->link_active = 1;
|
|
|
|
ISP_FC_PC(isp, bus)->loop_dead = 0;
|
|
|
|
ISP_FC_PC(isp, bus)->loop_down_time = 0;
|
|
|
|
isp_prt(isp, ISP_LOGINFO, "Chan %d Loop UP", bus);
|
1999-03-17 05:04:39 +00:00
|
|
|
break;
|
2006-11-02 03:21:32 +00:00
|
|
|
case ISPASYNC_DEV_ARRIVED:
|
2009-08-01 01:04:26 +00:00
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
lp = va_arg(ap, fcportdb_t *);
|
|
|
|
va_end(ap);
|
2006-11-14 08:45:48 +00:00
|
|
|
lp->reserved = 0;
|
2009-08-01 01:04:26 +00:00
|
|
|
if ((ISP_FC_PC(isp, bus)->role & ISP_ROLE_INITIATOR) && (lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT))) {
|
|
|
|
int dbidx = lp - FCPARAM(isp, bus)->portdb;
|
2006-11-14 08:45:48 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_FC_TARG; i++) {
|
|
|
|
if (i >= FL_ID && i <= SNS_ID) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
if (FCPARAM(isp, bus)->isp_dev_map[i] == 0) {
|
2006-11-14 08:45:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i < MAX_FC_TARG) {
|
2009-08-01 01:04:26 +00:00
|
|
|
FCPARAM(isp, bus)->isp_dev_map[i] = dbidx + 1;
|
|
|
|
lp->dev_map_idx = i + 1;
|
2006-11-14 08:45:48 +00:00
|
|
|
} else {
|
|
|
|
isp_prt(isp, ISP_LOGWARN, "out of target ids");
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_dump_portdb(isp, bus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (lp->dev_map_idx) {
|
|
|
|
tgt = lp->dev_map_idx - 1;
|
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, roles[lp->roles], "arrived at", tgt, (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
|
|
|
isp_make_here(isp, bus, tgt);
|
2006-11-02 03:21:32 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "arrived", (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
2006-11-02 03:21:32 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ISPASYNC_DEV_CHANGED:
|
2009-08-01 01:04:26 +00:00
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
lp = va_arg(ap, fcportdb_t *);
|
|
|
|
va_end(ap);
|
|
|
|
lp->reserved = 0;
|
2006-11-14 08:45:48 +00:00
|
|
|
if (isp_change_is_bad) {
|
|
|
|
lp->state = FC_PORTDB_STATE_NIL;
|
2009-08-01 01:04:26 +00:00
|
|
|
if (lp->dev_map_idx) {
|
|
|
|
tgt = lp->dev_map_idx - 1;
|
|
|
|
FCPARAM(isp, bus)->isp_dev_map[tgt] = 0;
|
|
|
|
lp->dev_map_idx = 0;
|
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom3, bus, lp->portid, tgt, "change is bad");
|
|
|
|
isp_make_gone(isp, bus, tgt);
|
2006-11-14 08:45:48 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "changed and departed",
|
|
|
|
(uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
2006-11-02 03:21:32 +00:00
|
|
|
} else {
|
2006-11-14 08:45:48 +00:00
|
|
|
lp->portid = lp->new_portid;
|
|
|
|
lp->roles = lp->new_roles;
|
2009-08-01 01:04:26 +00:00
|
|
|
if (lp->dev_map_idx) {
|
|
|
|
int t = lp->dev_map_idx - 1;
|
|
|
|
FCPARAM(isp, bus)->isp_dev_map[t] = (lp - FCPARAM(isp, bus)->portdb) + 1;
|
|
|
|
tgt = lp->dev_map_idx - 1;
|
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, roles[lp->roles], "changed at", tgt,
|
|
|
|
(uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
2006-11-14 08:45:48 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "changed", (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
2006-11-02 03:21:32 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ISPASYNC_DEV_STAYED:
|
2009-08-01 01:04:26 +00:00
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
lp = va_arg(ap, fcportdb_t *);
|
|
|
|
va_end(ap);
|
|
|
|
if (lp->dev_map_idx) {
|
|
|
|
tgt = lp->dev_map_idx - 1;
|
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, roles[lp->roles], "stayed at", tgt,
|
|
|
|
(uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
2006-11-02 03:21:32 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "stayed",
|
|
|
|
(uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
2006-11-02 03:21:32 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ISPASYNC_DEV_GONE:
|
2009-08-01 01:04:26 +00:00
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
lp = va_arg(ap, fcportdb_t *);
|
|
|
|
va_end(ap);
|
2006-11-14 08:45:48 +00:00
|
|
|
/*
|
|
|
|
* If this has a virtual target and we haven't marked it
|
|
|
|
* that we're going to have isp_gdt tell the OS it's gone,
|
|
|
|
* set the isp_gdt timer running on it.
|
|
|
|
*
|
|
|
|
* If it isn't marked that isp_gdt is going to get rid of it,
|
|
|
|
* announce that it's gone.
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if (lp->dev_map_idx && lp->reserved == 0) {
|
2006-11-14 08:45:48 +00:00
|
|
|
lp->reserved = 1;
|
2009-08-01 01:04:26 +00:00
|
|
|
lp->new_reserved = ISP_FC_PC(isp, bus)->gone_device_time;
|
2006-11-14 08:45:48 +00:00
|
|
|
lp->state = FC_PORTDB_STATE_ZOMBIE;
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_FC_PC(isp, bus)->gdt_running == 0) {
|
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d starting Gone Device Timer", bus);
|
|
|
|
ISP_FC_PC(isp, bus)->gdt_running = 1;
|
|
|
|
callout_reset(&ISP_FC_PC(isp, bus)->gdt, hz, isp_gdt, ISP_FC_PC(isp, bus));
|
|
|
|
}
|
|
|
|
tgt = lp->dev_map_idx - 1;
|
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom2, bus, lp->portid, lp->handle, roles[lp->roles], "gone zombie at", tgt, (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
2006-11-14 08:45:48 +00:00
|
|
|
} else if (lp->reserved == 0) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGCONFIG, prom, bus, lp->portid, lp->handle, roles[lp->roles], "departed", (uint32_t) (lp->port_wwn >> 32), (uint32_t) lp->port_wwn);
|
2006-11-02 03:21:32 +00:00
|
|
|
}
|
1999-03-17 05:04:39 +00:00
|
|
|
break;
|
|
|
|
case ISPASYNC_CHANGE_NOTIFY:
|
2006-11-02 03:21:32 +00:00
|
|
|
{
|
|
|
|
char *msg;
|
2009-08-01 01:04:26 +00:00
|
|
|
int evt, nphdl, nlstate, reason;
|
|
|
|
|
|
|
|
va_start(ap, cmd);
|
|
|
|
bus = va_arg(ap, int);
|
|
|
|
evt = va_arg(ap, int);
|
|
|
|
if (IS_24XX(isp) && evt == 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);
|
|
|
|
|
|
|
|
if (evt == ISPASYNC_CHANGE_PDB) {
|
|
|
|
msg = "Chan %d Port Database Changed";
|
|
|
|
} else if (evt == ISPASYNC_CHANGE_SNS) {
|
|
|
|
msg = "Chan %d Name Server Database Changed";
|
2006-11-02 03:21:32 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
msg = "Chan %d Other Change Notify";
|
2001-01-09 02:47:15 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
|
2006-11-14 08:45:48 +00:00
|
|
|
/*
|
|
|
|
* If the loop down timer is running, cancel it.
|
|
|
|
*/
|
2009-08-01 01:04:26 +00:00
|
|
|
if (ISP_FC_PC(isp, bus)->ldt_running) {
|
|
|
|
isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Stopping Loop Down Timer @ %lu", (unsigned long) time_uptime);
|
|
|
|
ISP_FC_PC(isp, bus)->ldt_running = 0;
|
|
|
|
callout_stop(&ISP_FC_PC(isp, bus)->ldt);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGINFO, msg, bus);
|
|
|
|
if (ISP_FC_PC(isp, bus)->role & ISP_ROLE_INITIATOR) {
|
|
|
|
isp_freeze_loopdown(isp, bus, msg);
|
|
|
|
}
|
|
|
|
wakeup(ISP_FC_PC(isp, bus));
|
1999-03-17 05:04:39 +00:00
|
|
|
break;
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
#ifdef ISP_TARGET_MODE
|
2006-01-23 06:23:37 +00:00
|
|
|
case ISPASYNC_TARGET_NOTIFY:
|
2000-01-03 23:55:30 +00:00
|
|
|
{
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_notify_t *notify;
|
|
|
|
va_start(ap, cmd);
|
|
|
|
notify = va_arg(ap, isp_notify_t *);
|
|
|
|
va_end(ap);
|
|
|
|
switch (notify->nt_ncode) {
|
|
|
|
case NT_ABORT_TASK:
|
|
|
|
case NT_ABORT_TASK_SET:
|
|
|
|
case NT_CLEAR_ACA:
|
|
|
|
case NT_CLEAR_TASK_SET:
|
|
|
|
case NT_LUN_RESET:
|
|
|
|
case NT_TARGET_RESET:
|
|
|
|
/*
|
|
|
|
* These are task management functions.
|
|
|
|
*/
|
|
|
|
isp_handle_platform_target_tmf(isp, notify);
|
|
|
|
break;
|
|
|
|
case NT_BUS_RESET:
|
|
|
|
case NT_LIP_RESET:
|
|
|
|
case NT_LINK_UP:
|
|
|
|
case NT_LINK_DOWN:
|
|
|
|
/*
|
|
|
|
* No action need be taken here.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
case NT_HBA_RESET:
|
|
|
|
isp_del_all_wwn_entries(isp, ISP_NOCHAN);
|
|
|
|
break;
|
|
|
|
case NT_LOGOUT:
|
|
|
|
/*
|
|
|
|
* This is device arrival/departure notification
|
|
|
|
*/
|
|
|
|
isp_handle_platform_target_notify_ack(isp, notify);
|
|
|
|
break;
|
|
|
|
case NT_ARRIVED:
|
|
|
|
{
|
|
|
|
struct ac_contract ac;
|
|
|
|
struct ac_device_changed *fc;
|
|
|
|
|
|
|
|
ac.contract_number = AC_CONTRACT_DEV_CHG;
|
|
|
|
fc = (struct ac_device_changed *) ac.contract_data;
|
|
|
|
fc->wwpn = notify->nt_wwn;
|
|
|
|
fc->port = notify->nt_sid;
|
|
|
|
fc->target = notify->nt_nphdl;
|
|
|
|
fc->arrived = 1;
|
|
|
|
xpt_async(AC_CONTRACT, ISP_FC_PC(isp, notify->nt_channel)->path, &ac);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NT_DEPARTED:
|
|
|
|
{
|
|
|
|
struct ac_contract ac;
|
|
|
|
struct ac_device_changed *fc;
|
|
|
|
|
|
|
|
ac.contract_number = AC_CONTRACT_DEV_CHG;
|
|
|
|
fc = (struct ac_device_changed *) ac.contract_data;
|
|
|
|
fc->wwpn = notify->nt_wwn;
|
|
|
|
fc->port = notify->nt_sid;
|
|
|
|
fc->target = notify->nt_nphdl;
|
|
|
|
fc->arrived = 0;
|
|
|
|
xpt_async(AC_CONTRACT, ISP_FC_PC(isp, notify->nt_channel)->path, &ac);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
isp_prt(isp, ISP_LOGALL, "target notify code 0x%x", notify->nt_ncode);
|
|
|
|
isp_handle_platform_target_notify_ack(isp, notify);
|
|
|
|
break;
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ISPASYNC_TARGET_ACTION:
|
2009-08-01 01:04:26 +00:00
|
|
|
{
|
|
|
|
isphdr_t *hp;
|
|
|
|
|
|
|
|
va_start(ap, cmd);
|
|
|
|
hp = va_arg(ap, isphdr_t *);
|
|
|
|
va_end(ap);
|
|
|
|
switch (hp->rqs_entry_type) {
|
2000-01-03 23:55:30 +00:00
|
|
|
default:
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s: unhandled target action 0x%x", __func__, hp->rqs_entry_type);
|
2000-01-03 23:55:30 +00:00
|
|
|
break;
|
2002-06-16 05:08:02 +00:00
|
|
|
case RQSTYPE_NOTIFY:
|
|
|
|
if (IS_SCSI(isp)) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_handle_platform_notify_scsi(isp, (in_entry_t *) hp);
|
|
|
|
} else if (IS_24XX(isp)) {
|
|
|
|
isp_handle_platform_notify_24xx(isp, (in_fcentry_24xx_t *) hp);
|
2002-06-16 05:08:02 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_handle_platform_notify_fc(isp, (in_fcentry_t *) hp);
|
2002-06-16 05:08:02 +00:00
|
|
|
}
|
|
|
|
break;
|
2000-01-03 23:55:30 +00:00
|
|
|
case RQSTYPE_ATIO:
|
2009-08-01 01:04:26 +00:00
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
isp_handle_platform_atio7(isp, (at7_entry_t *) hp);
|
|
|
|
} else {
|
|
|
|
isp_handle_platform_atio(isp, (at_entry_t *) hp);
|
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
break;
|
|
|
|
case RQSTYPE_ATIO2:
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_handle_platform_atio2(isp, (at2_entry_t *) hp);
|
2000-01-03 23:55:30 +00:00
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
case RQSTYPE_CTIO7:
|
2006-05-22 07:07:30 +00:00
|
|
|
case RQSTYPE_CTIO3:
|
2000-01-03 23:55:30 +00:00
|
|
|
case RQSTYPE_CTIO2:
|
|
|
|
case RQSTYPE_CTIO:
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_handle_platform_ctio(isp, hp);
|
|
|
|
break;
|
|
|
|
case RQSTYPE_ABTS_RCVD:
|
|
|
|
{
|
|
|
|
abts_t *abts = (abts_t *)hp;
|
|
|
|
isp_notify_t notify, *nt = ¬ify;
|
|
|
|
tstate_t *tptr;
|
|
|
|
fcportdb_t *lp;
|
|
|
|
uint16_t chan;
|
|
|
|
uint32_t sid, did;
|
|
|
|
|
|
|
|
did = (abts->abts_did_hi << 16) | abts->abts_did_lo;
|
|
|
|
sid = (abts->abts_sid_hi << 16) | abts->abts_sid_lo;
|
|
|
|
ISP_MEMZERO(nt, sizeof (isp_notify_t));
|
|
|
|
|
|
|
|
nt->nt_hba = isp;
|
|
|
|
nt->nt_did = did;
|
|
|
|
nt->nt_nphdl = abts->abts_nphdl;
|
|
|
|
nt->nt_sid = sid;
|
|
|
|
isp_find_chan_by_did(isp, did, &chan);
|
|
|
|
if (chan == ISP_NOCHAN) {
|
|
|
|
nt->nt_tgt = TGT_ANY;
|
|
|
|
} else {
|
|
|
|
nt->nt_tgt = FCPARAM(isp, chan)->isp_wwpn;
|
|
|
|
if (isp_find_pdb_by_loopid(isp, chan, abts->abts_nphdl, &lp)) {
|
|
|
|
nt->nt_wwn = lp->port_wwn;
|
|
|
|
} else {
|
|
|
|
nt->nt_wwn = INI_ANY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Try hard to find the lun for this command.
|
|
|
|
*/
|
|
|
|
tptr = get_lun_statep_from_tag(isp, chan, abts->abts_rxid_task);
|
|
|
|
if (tptr) {
|
|
|
|
nt->nt_lun = xpt_path_lun_id(tptr->owner);
|
|
|
|
rls_lun_statep(isp, tptr);
|
|
|
|
} else {
|
|
|
|
nt->nt_lun = LUN_ANY;
|
|
|
|
}
|
|
|
|
nt->nt_need_ack = 1;
|
|
|
|
nt->nt_tagval = abts->abts_rxid_task;
|
|
|
|
nt->nt_tagval |= (((uint64_t) abts->abts_rxid_abts) << 32);
|
|
|
|
if (abts->abts_rxid_task == ISP24XX_NO_TASK) {
|
|
|
|
isp_prt(isp, ISP_LOGTINFO, "[0x%x] ABTS from N-Port handle 0x%x Port 0x%06x has no task id (rx_id 0x%04x ox_id 0x%04x)",
|
|
|
|
abts->abts_rxid_abts, abts->abts_nphdl, sid, abts->abts_rx_id, abts->abts_ox_id);
|
|
|
|
} else {
|
|
|
|
isp_prt(isp, ISP_LOGTINFO, "[0x%x] ABTS from N-Port handle 0x%x Port 0x%06x for task 0x%x (rx_id 0x%04x ox_id 0x%04x)",
|
|
|
|
abts->abts_rxid_abts, abts->abts_nphdl, sid, abts->abts_rxid_task, abts->abts_rx_id, abts->abts_ox_id);
|
|
|
|
}
|
|
|
|
nt->nt_channel = chan;
|
|
|
|
nt->nt_ncode = NT_ABORT_TASK;
|
|
|
|
nt->nt_lreserved = hp;
|
|
|
|
isp_handle_platform_target_tmf(isp, nt);
|
2000-01-03 23:55:30 +00:00
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
2000-01-03 23:55:30 +00:00
|
|
|
case RQSTYPE_ENABLE_LUN:
|
|
|
|
case RQSTYPE_MODIFY_LUN:
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_ledone(isp, (lun_entry_t *) hp);
|
2000-01-03 23:55:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2009-08-01 01:04:26 +00:00
|
|
|
}
|
1999-07-02 23:12:58 +00:00
|
|
|
#endif
|
2001-07-04 18:54:29 +00:00
|
|
|
case ISPASYNC_FW_CRASH:
|
|
|
|
{
|
2006-02-15 00:31:48 +00:00
|
|
|
uint16_t mbox1, mbox6;
|
2001-07-04 18:54:29 +00:00
|
|
|
mbox1 = ISP_READ(isp, OUTMAILBOX1);
|
|
|
|
if (IS_DUALBUS(isp)) {
|
|
|
|
mbox6 = ISP_READ(isp, OUTMAILBOX6);
|
|
|
|
} else {
|
|
|
|
mbox6 = 0;
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGERR, "Internal Firmware Error on bus %d @ RISC Address 0x%x", mbox6, mbox1);
|
2007-05-05 20:17:23 +00:00
|
|
|
mbox1 = isp->isp_osinfo.mbox_sleep_ok;
|
|
|
|
isp->isp_osinfo.mbox_sleep_ok = 0;
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_reinit(isp, 1);
|
2007-05-05 20:17:23 +00:00
|
|
|
isp->isp_osinfo.mbox_sleep_ok = mbox1;
|
|
|
|
isp_async(isp, ISPASYNC_FW_RESTARTED, NULL);
|
2001-07-04 18:54:29 +00:00
|
|
|
break;
|
|
|
|
}
|
1999-01-30 07:29:00 +00:00
|
|
|
default:
|
2000-08-01 06:31:44 +00:00
|
|
|
isp_prt(isp, ISP_LOGERR, "unknown isp_async event %d", cmd);
|
1999-01-30 07:29:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1998-12-28 19:22:27 +00:00
|
|
|
|
1999-07-02 23:12:58 +00:00
|
|
|
|
1998-12-28 19:22:27 +00:00
|
|
|
/*
|
|
|
|
* Locks are held before coming here.
|
|
|
|
*/
|
|
|
|
void
|
2006-04-21 18:30:01 +00:00
|
|
|
isp_uninit(ispsoftc_t *isp)
|
1998-12-28 19:22:27 +00:00
|
|
|
{
|
2006-11-02 03:21:32 +00:00
|
|
|
if (IS_24XX(isp)) {
|
|
|
|
ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_RESET);
|
|
|
|
} else {
|
|
|
|
ISP_WRITE(isp, HCCR, HCCR_CMD_RESET);
|
|
|
|
}
|
|
|
|
ISP_DISABLE_INTS(isp);
|
1998-12-28 19:22:27 +00:00
|
|
|
}
|
2000-08-01 06:31:44 +00:00
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
isp_default_wwn(ispsoftc_t * isp, int chan, int isactive, int iswwnn)
|
|
|
|
{
|
|
|
|
uint64_t seed;
|
|
|
|
struct isp_fc *fc = ISP_FC_PC(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;
|
2009-08-13 01:17:26 +00:00
|
|
|
if (seed) {
|
|
|
|
return (seed);
|
|
|
|
}
|
|
|
|
return (0x400000007F000009ull);
|
2009-08-01 01:04:26 +00:00
|
|
|
} 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_FC_PC(isp, 0)->def_wwnn : ISP_FC_PC(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);
|
|
|
|
}
|
|
|
|
|
2000-08-01 06:31:44 +00:00
|
|
|
void
|
2006-04-21 18:30:01 +00:00
|
|
|
isp_prt(ispsoftc_t *isp, int level, const char *fmt, ...)
|
2000-08-01 06:31:44 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
if (level != ISP_LOGALL && (level & isp->isp_dblev) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2001-03-01 02:14:54 +00:00
|
|
|
printf("%s: ", device_get_nameunit(isp->isp_dev));
|
2000-08-01 06:31:44 +00:00
|
|
|
va_start(ap, fmt);
|
|
|
|
vprintf(fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
printf("\n");
|
|
|
|
}
|
2006-11-14 08:45:48 +00:00
|
|
|
|
|
|
|
uint64_t
|
|
|
|
isp_nanotime_sub(struct timespec *b, struct timespec *a)
|
|
|
|
{
|
|
|
|
uint64_t elapsed;
|
|
|
|
struct timespec x = *b;
|
|
|
|
timespecsub(&x, a);
|
|
|
|
elapsed = GET_NANOSEC(&x);
|
|
|
|
if (elapsed == 0)
|
|
|
|
elapsed++;
|
|
|
|
return (elapsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
isp_mbox_acquire(ispsoftc_t *isp)
|
|
|
|
{
|
|
|
|
if (isp->isp_osinfo.mboxbsy) {
|
|
|
|
return (1);
|
|
|
|
} else {
|
|
|
|
isp->isp_osinfo.mboxcmd_done = 0;
|
|
|
|
isp->isp_osinfo.mboxbsy = 1;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isp_mbox_wait_complete(ispsoftc_t *isp, mbreg_t *mbp)
|
|
|
|
{
|
2006-12-17 16:59:19 +00:00
|
|
|
unsigned int usecs = mbp->timeout;
|
|
|
|
unsigned int max, olim, ilim;
|
2006-11-14 08:45:48 +00:00
|
|
|
|
|
|
|
if (usecs == 0) {
|
|
|
|
usecs = MBCMD_DEFAULT_TIMEOUT;
|
|
|
|
}
|
2006-12-17 16:59:19 +00:00
|
|
|
max = isp->isp_mbxwrk0 + 1;
|
|
|
|
|
2006-11-14 08:45:48 +00:00
|
|
|
if (isp->isp_osinfo.mbox_sleep_ok) {
|
2006-12-17 16:59:19 +00:00
|
|
|
unsigned int ms = (usecs + 999) / 1000;
|
|
|
|
|
2006-11-14 08:45:48 +00:00
|
|
|
isp->isp_osinfo.mbox_sleep_ok = 0;
|
|
|
|
isp->isp_osinfo.mbox_sleeping = 1;
|
2006-12-17 16:59:19 +00:00
|
|
|
for (olim = 0; olim < max; olim++) {
|
2009-08-01 01:04:26 +00:00
|
|
|
msleep(&isp->isp_mbxworkp, &isp->isp_osinfo.lock, PRIBIO, "ispmbx_sleep", isp_mstohz(ms));
|
2006-11-14 08:45:48 +00:00
|
|
|
if (isp->isp_osinfo.mboxcmd_done) {
|
|
|
|
break;
|
|
|
|
}
|
2006-12-17 16:59:19 +00:00
|
|
|
}
|
|
|
|
isp->isp_osinfo.mbox_sleep_ok = 1;
|
|
|
|
isp->isp_osinfo.mbox_sleeping = 0;
|
|
|
|
} else {
|
|
|
|
for (olim = 0; olim < max; olim++) {
|
|
|
|
for (ilim = 0; ilim < usecs; ilim += 100) {
|
|
|
|
uint32_t isr;
|
|
|
|
uint16_t sema, mbox;
|
2006-11-14 08:45:48 +00:00
|
|
|
if (isp->isp_osinfo.mboxcmd_done) {
|
|
|
|
break;
|
|
|
|
}
|
2006-12-17 16:59:19 +00:00
|
|
|
if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) {
|
|
|
|
isp_intr(isp, isr, sema, mbox);
|
|
|
|
if (isp->isp_osinfo.mboxcmd_done) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
ISP_DELAY(100);
|
2006-12-17 16:59:19 +00:00
|
|
|
}
|
|
|
|
if (isp->isp_osinfo.mboxcmd_done) {
|
|
|
|
break;
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isp->isp_osinfo.mboxcmd_done == 0) {
|
2009-08-01 01:04:26 +00:00
|
|
|
isp_prt(isp, ISP_LOGWARN, "%s Mailbox Command (0x%x) Timeout (%uus) (started @ %s:%d)",
|
|
|
|
isp->isp_osinfo.mbox_sleep_ok? "Interrupting" : "Polled", isp->isp_lastmbxcmd, usecs, mbp->func, mbp->lineno);
|
2006-11-14 08:45:48 +00:00
|
|
|
mbp->param[0] = MBOX_TIMEOUT;
|
|
|
|
isp->isp_osinfo.mboxcmd_done = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isp_mbox_notify_done(ispsoftc_t *isp)
|
|
|
|
{
|
|
|
|
if (isp->isp_osinfo.mbox_sleeping) {
|
|
|
|
wakeup(&isp->isp_mbxworkp);
|
|
|
|
}
|
|
|
|
isp->isp_osinfo.mboxcmd_done = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isp_mbox_release(ispsoftc_t *isp)
|
|
|
|
{
|
|
|
|
isp->isp_osinfo.mboxbsy = 0;
|
|
|
|
}
|
|
|
|
|
2009-08-01 01:04:26 +00:00
|
|
|
int
|
|
|
|
isp_fc_scratch_acquire(ispsoftc_t *isp, int chan)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
if (isp->isp_osinfo.pc.fc[chan].fcbsy) {
|
|
|
|
ret = -1;
|
|
|
|
} else {
|
|
|
|
isp->isp_osinfo.pc.fc[chan].fcbsy = 1;
|
|
|
|
}
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2006-11-14 08:45:48 +00:00
|
|
|
int
|
|
|
|
isp_mstohz(int ms)
|
|
|
|
{
|
2006-12-17 16:59:19 +00:00
|
|
|
int hz;
|
2006-11-14 08:45:48 +00:00
|
|
|
struct timeval t;
|
|
|
|
t.tv_sec = ms / 1000;
|
|
|
|
t.tv_usec = (ms % 1000) * 1000;
|
2006-12-17 16:59:19 +00:00
|
|
|
hz = tvtohz(&t);
|
|
|
|
if (hz < 0) {
|
|
|
|
hz = 0x7fffffff;
|
|
|
|
}
|
|
|
|
if (hz == 0) {
|
|
|
|
hz = 1;
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
2006-12-17 16:59:19 +00:00
|
|
|
return (hz);
|
2006-11-14 08:45:48 +00:00
|
|
|
}
|
2007-05-05 20:17:23 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
isp_platform_intr(void *arg)
|
|
|
|
{
|
|
|
|
ispsoftc_t *isp = arg;
|
|
|
|
uint32_t isr;
|
|
|
|
uint16_t sema, mbox;
|
|
|
|
|
|
|
|
ISP_LOCK(isp);
|
|
|
|
isp->isp_intcnt++;
|
|
|
|
if (ISP_READ_ISR(isp, &isr, &sema, &mbox) == 0) {
|
|
|
|
isp->isp_intbogus++;
|
|
|
|
} else {
|
|
|
|
isp_intr(isp, isr, sema, mbox);
|
|
|
|
}
|
|
|
|
ISP_UNLOCK(isp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isp_common_dmateardown(ispsoftc_t *isp, struct ccb_scsiio *csio, uint32_t hdl)
|
|
|
|
{
|
|
|
|
if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
|
2009-08-01 01:04:26 +00:00
|
|
|
bus_dmamap_sync(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap, BUS_DMASYNC_POSTREAD);
|
2007-05-05 20:17:23 +00:00
|
|
|
} else {
|
2009-08-01 01:04:26 +00:00
|
|
|
bus_dmamap_sync(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap, BUS_DMASYNC_POSTWRITE);
|
2007-05-05 20:17:23 +00:00
|
|
|
}
|
|
|
|
bus_dmamap_unload(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap);
|
|
|
|
}
|
2009-08-01 01:04:26 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
isp_timer(void *arg)
|
|
|
|
{
|
|
|
|
ispsoftc_t *isp = arg;
|
|
|
|
#ifdef ISP_TARGET_MODE
|
|
|
|
isp_tmcmd_restart(isp);
|
|
|
|
#endif
|
|
|
|
callout_reset(&isp->isp_osinfo.tmo, hz, isp_timer, isp);
|
|
|
|
}
|