freebsd-dev/sys/dev/mps/mps_sas.c
Kenneth D. Merry 63feedfd46 MFp4 (//depot/projects/mps/...):
According to the MPT2 spec, task management commands are
serialized, and so no I/O should start while task management
commands are active.

So, to comply with that, freeze the SIM queue before we send any
task management commands (abort, target reset, etc.) down to the
IOC.  We unfreeze the queue once the task management command
completes.

It isn't clear from the spec whether multiple simultaneous task
management commands are supported.  Right now it is possible to
have multiple outstanding task management commands, especially in
the abort case.  Multiple outstanding aborts do complete
successfully, so it may be supported.

We also don't yet have any recovery mechanism (e.g. reset the IOC)
if the task management command fails.
2010-09-16 22:54:56 +00:00

1435 lines
38 KiB
C

/*-
* Copyright (c) 2009 Yahoo! Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/* Communications core for LSI MPT2 */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/selinfo.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/bio.h>
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_debug.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt_sim.h>
#include <cam/cam_xpt_periph.h>
#include <cam/cam_periph.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>
#include <dev/mps/mpi/mpi2_type.h>
#include <dev/mps/mpi/mpi2.h>
#include <dev/mps/mpi/mpi2_ioc.h>
#include <dev/mps/mpi/mpi2_sas.h>
#include <dev/mps/mpi/mpi2_cnfg.h>
#include <dev/mps/mpi/mpi2_init.h>
#include <dev/mps/mpsvar.h>
#include <dev/mps/mps_table.h>
struct mpssas_target {
uint16_t handle;
uint8_t linkrate;
uint64_t devname;
uint32_t devinfo;
uint16_t encl_handle;
uint16_t encl_slot;
int flags;
#define MPSSAS_TARGET_INABORT (1 << 0)
#define MPSSAS_TARGET_INRESET (1 << 1)
#define MPSSAS_TARGET_INCHIPRESET (1 << 2)
#define MPSSAS_TARGET_INRECOVERY 0x7
uint16_t tid;
};
struct mpssas_softc {
struct mps_softc *sc;
u_int flags;
#define MPSSAS_IN_DISCOVERY (1 << 0)
#define MPSSAS_IN_STARTUP (1 << 1)
#define MPSSAS_DISCOVERY_TIMEOUT_PENDING (1 << 2)
#define MPSSAS_QUEUE_FROZEN (1 << 3)
struct mpssas_target *targets;
struct cam_devq *devq;
struct cam_sim *sim;
struct cam_path *path;
struct intr_config_hook sas_ich;
struct callout discovery_callout;
u_int discovery_timeouts;
struct mps_event_handle *mpssas_eh;
};
struct mpssas_devprobe {
struct mps_config_params params;
u_int state;
#define MPSSAS_PROBE_DEV1 0x01
#define MPSSAS_PROBE_DEV2 0x02
#define MPSSAS_PROBE_PHY 0x03
#define MPSSAS_PROBE_EXP 0x04
#define MPSSAS_PROBE_PHY2 0x05
#define MPSSAS_PROBE_EXP2 0x06
struct mpssas_target target;
};
#define MPSSAS_DISCOVERY_TIMEOUT 20
#define MPSSAS_MAX_DISCOVERY_TIMEOUTS 10 /* 200 seconds */
MALLOC_DEFINE(M_MPSSAS, "MPSSAS", "MPS SAS memory");
static struct mpssas_target * mpssas_alloc_target(struct mpssas_softc *,
struct mpssas_target *);
static struct mpssas_target * mpssas_find_target(struct mpssas_softc *, int,
uint16_t);
static void mpssas_announce_device(struct mpssas_softc *,
struct mpssas_target *);
static void mpssas_startup(void *data);
static void mpssas_discovery_end(struct mpssas_softc *sassc);
static void mpssas_discovery_timeout(void *data);
static void mpssas_prepare_remove(struct mpssas_softc *,
MPI2_EVENT_SAS_TOPO_PHY_ENTRY *);
static void mpssas_remove_device(struct mps_softc *, struct mps_command *);
static void mpssas_remove_complete(struct mps_softc *, struct mps_command *);
static void mpssas_action(struct cam_sim *sim, union ccb *ccb);
static void mpssas_poll(struct cam_sim *sim);
static void mpssas_probe_device(struct mps_softc *sc, uint16_t handle);
static void mpssas_probe_device_complete(struct mps_softc *sc,
struct mps_config_params *params);
static void mpssas_scsiio_timeout(void *data);
static void mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm);
static void mpssas_recovery(struct mps_softc *, struct mps_command *);
static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *);
static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *);
static int mpssas_resetdev(struct mpssas_softc *, struct mps_command *);
static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *);
static void mpssas_freeze_device(struct mpssas_softc *, struct mpssas_target *);
static void mpssas_unfreeze_device(struct mpssas_softc *, struct mpssas_target *) __unused;
static struct mpssas_target *
mpssas_alloc_target(struct mpssas_softc *sassc, struct mpssas_target *probe)
{
struct mpssas_target *target;
int start;
mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
/*
* If it's not a sata or sas target, CAM won't be able to see it. Put
* it into a high-numbered slot so that it's accessible but not
* interrupting the target numbering sequence of real drives.
*/
if ((probe->devinfo & (MPI2_SAS_DEVICE_INFO_SSP_TARGET |
MPI2_SAS_DEVICE_INFO_STP_TARGET | MPI2_SAS_DEVICE_INFO_SATA_DEVICE))
== 0) {
start = 200;
} else {
/*
* Use the enclosure number and slot number as a hint for target
* numbering. If that doesn't produce a sane result, search the
* entire space.
*/
#if 0
start = probe->encl_handle * 16 + probe->encl_slot;
#else
start = probe->encl_slot;
#endif
if (start >= sassc->sc->facts->MaxTargets)
start = 0;
}
target = mpssas_find_target(sassc, start, 0);
/*
* Nothing found on the first pass, try a second pass that searches the
* entire space.
*/
if (target == NULL)
target = mpssas_find_target(sassc, 0, 0);
return (target);
}
static struct mpssas_target *
mpssas_find_target(struct mpssas_softc *sassc, int start, uint16_t handle)
{
struct mpssas_target *target;
int i;
for (i = start; i < sassc->sc->facts->MaxTargets; i++) {
target = &sassc->targets[i];
if (target->handle == handle)
return (target);
}
return (NULL);
}
/*
* Start the probe sequence for a given device handle. This will not
* block.
*/
static void
mpssas_probe_device(struct mps_softc *sc, uint16_t handle)
{
struct mpssas_devprobe *probe;
struct mps_config_params *params;
MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
int error;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
probe = malloc(sizeof(*probe), M_MPSSAS, M_NOWAIT | M_ZERO);
if (probe == NULL) {
mps_dprint(sc, MPS_FAULT, "Out of memory starting probe\n");
return;
}
params = &probe->params;
hdr = &params->hdr.Ext;
params->action = MPI2_CONFIG_ACTION_PAGE_HEADER;
params->page_address = MPI2_SAS_DEVICE_PGAD_FORM_HANDLE | handle;
hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
hdr->ExtPageLength = 0;
hdr->PageNumber = 0;
hdr->PageVersion = 0;
params->buffer = NULL;
params->length = 0;
params->callback = mpssas_probe_device_complete;
params->cbdata = probe;
probe->target.handle = handle;
probe->state = MPSSAS_PROBE_DEV1;
if ((error = mps_read_config_page(sc, params)) != 0) {
free(probe, M_MPSSAS);
mps_dprint(sc, MPS_FAULT, "Failure starting device probe\n");
return;
}
}
static void
mpssas_probe_device_complete(struct mps_softc *sc,
struct mps_config_params *params)
{
MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
struct mpssas_devprobe *probe;
int error;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
hdr = &params->hdr.Ext;
probe = params->cbdata;
switch (probe->state) {
case MPSSAS_PROBE_DEV1:
case MPSSAS_PROBE_PHY:
case MPSSAS_PROBE_EXP:
if (params->status != MPI2_IOCSTATUS_SUCCESS) {
mps_dprint(sc, MPS_FAULT,
"Probe Failure 0x%x state %d\n", params->status,
probe->state);
free(probe, M_MPSSAS);
return;
}
params->action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
params->length = hdr->ExtPageLength * 4;
params->buffer = malloc(params->length, M_MPSSAS,
M_ZERO|M_NOWAIT);
if (params->buffer == NULL) {
mps_dprint(sc, MPS_FAULT, "Out of memory at state "
"0x%x, size 0x%x\n", probe->state, params->length);
free(probe, M_MPSSAS);
return;
}
if (probe->state == MPSSAS_PROBE_DEV1)
probe->state = MPSSAS_PROBE_DEV2;
else if (probe->state == MPSSAS_PROBE_PHY)
probe->state = MPSSAS_PROBE_PHY2;
else if (probe->state == MPSSAS_PROBE_EXP)
probe->state = MPSSAS_PROBE_EXP2;
error = mps_read_config_page(sc, params);
break;
case MPSSAS_PROBE_DEV2:
{
MPI2_CONFIG_PAGE_SAS_DEV_0 *buf;
if (params->status != MPI2_IOCSTATUS_SUCCESS) {
mps_dprint(sc, MPS_FAULT,
"Probe Failure 0x%x state %d\n", params->status,
probe->state);
free(params->buffer, M_MPSSAS);
free(probe, M_MPSSAS);
return;
}
buf = params->buffer;
mps_print_sasdev0(sc, buf);
probe->target.devname = mps_to_u64(&buf->DeviceName);
probe->target.devinfo = buf->DeviceInfo;
probe->target.encl_handle = buf->EnclosureHandle;
probe->target.encl_slot = buf->Slot;
if (buf->DeviceInfo & MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH) {
params->page_address =
MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | buf->PhyNum;
hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
hdr->PageNumber = 0;
probe->state = MPSSAS_PROBE_PHY;
} else {
params->page_address =
MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
buf->ParentDevHandle | (buf->PhyNum << 16);
hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
hdr->PageNumber = 1;
probe->state = MPSSAS_PROBE_EXP;
}
params->action = MPI2_CONFIG_ACTION_PAGE_HEADER;
hdr->ExtPageLength = 0;
hdr->PageVersion = 0;
params->buffer = NULL;
params->length = 0;
free(buf, M_MPSSAS);
error = mps_read_config_page(sc, params);
break;
}
case MPSSAS_PROBE_PHY2:
case MPSSAS_PROBE_EXP2:
{
MPI2_CONFIG_PAGE_SAS_PHY_0 *phy;
MPI2_CONFIG_PAGE_EXPANDER_1 *exp;
struct mpssas_softc *sassc;
struct mpssas_target *targ;
char devstring[80];
uint16_t handle;
if (params->status != MPI2_IOCSTATUS_SUCCESS) {
mps_dprint(sc, MPS_FAULT,
"Probe Failure 0x%x state %d\n", params->status,
probe->state);
free(params->buffer, M_MPSSAS);
free(probe, M_MPSSAS);
return;
}
if (probe->state == MPSSAS_PROBE_PHY2) {
phy = params->buffer;
mps_print_sasphy0(sc, phy);
probe->target.linkrate = phy->NegotiatedLinkRate & 0xf;
} else {
exp = params->buffer;
mps_print_expander1(sc, exp);
probe->target.linkrate = exp->NegotiatedLinkRate & 0xf;
}
free(params->buffer, M_MPSSAS);
sassc = sc->sassc;
handle = probe->target.handle;
if ((targ = mpssas_find_target(sassc, 0, handle)) != NULL) {
mps_printf(sc, "Ignoring dup device handle 0x%04x\n",
handle);
free(probe, M_MPSSAS);
return;
}
if ((targ = mpssas_alloc_target(sassc, &probe->target)) == NULL) {
mps_printf(sc, "Target table overflow, handle 0x%04x\n",
handle);
free(probe, M_MPSSAS);
return;
}
*targ = probe->target; /* Copy the attributes */
targ->tid = targ - sassc->targets;
mps_describe_devinfo(targ->devinfo, devstring, 80);
if (bootverbose)
mps_printf(sc, "Found device <%s> <%s> <0x%04x> "
"<%d/%d>\n", devstring,
mps_describe_table(mps_linkrate_names,
targ->linkrate), targ->handle, targ->encl_handle,
targ->encl_slot);
free(probe, M_MPSSAS);
mpssas_announce_device(sassc, targ);
break;
}
default:
printf("what?\n");
}
}
/*
* The MPT2 firmware performs debounce on the link to avoid transient link errors
* and false removals. When it does decide that link has been lost and a device
* need to go away, it expects that the host will perform a target reset and then
* an op remove. The reset has the side-effect of aborting any outstanding
* requests for the device, which is required for the op-remove to succeed. It's
* not clear if the host should check for the device coming back alive after the
* reset.
*/
static void
mpssas_prepare_remove(struct mpssas_softc *sassc, MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy)
{
MPI2_SCSI_TASK_MANAGE_REQUEST *req;
struct mps_softc *sc;
struct mps_command *cm;
struct mpssas_target *targ = NULL;
uint16_t handle;
mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
handle = phy->AttachedDevHandle;
targ = mpssas_find_target(sassc, 0, handle);
if (targ == NULL)
/* We don't know about this device? */
return;
sc = sassc->sc;
cm = mps_alloc_command(sc);
if (cm == NULL) {
mps_printf(sc, "comand alloc failure in mpssas_prepare_remove\n");
return;
}
req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
req->DevHandle = targ->handle;
req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
/* SAS Hard Link Reset / SATA Link Reset */
req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
cm->cm_data = NULL;
cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
cm->cm_complete = mpssas_remove_device;
cm->cm_targ = targ;
xpt_freeze_simq(sc->sassc->sim, 1);
mps_map_command(sc, cm);
}
static void
mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm)
{
MPI2_SCSI_TASK_MANAGE_REPLY *reply;
MPI2_SAS_IOUNIT_CONTROL_REQUEST *req;
struct mpssas_target *targ;
uint16_t handle;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
handle = cm->cm_targ->handle;
xpt_release_simq(sc->sassc->sim, 1);
if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) {
mps_printf(sc, "Failure 0x%x reseting device 0x%04x\n",
reply->IOCStatus, handle);
mps_free_command(sc, cm);
return;
}
mps_printf(sc, "Reset aborted %d commands\n", reply->TerminationCount);
mps_free_reply(sc, cm->cm_reply_data);
/* Reuse the existing command */
req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)cm->cm_req;
req->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
req->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
req->DevHandle = handle;
cm->cm_data = NULL;
cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
cm->cm_flags &= ~MPS_CM_FLAGS_COMPLETE;
cm->cm_complete = mpssas_remove_complete;
mps_map_command(sc, cm);
mps_dprint(sc, MPS_INFO, "clearing target handle 0x%04x\n", handle);
targ = mpssas_find_target(sc->sassc, 0, handle);
if (targ != NULL) {
targ->handle = 0x0;
mpssas_announce_device(sc->sassc, targ);
}
}
static void
mpssas_remove_complete(struct mps_softc *sc, struct mps_command *cm)
{
MPI2_SAS_IOUNIT_CONTROL_REPLY *reply;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
reply = (MPI2_SAS_IOUNIT_CONTROL_REPLY *)cm->cm_reply;
mps_printf(sc, "mpssas_remove_complete on target 0x%04x,"
" IOCStatus= 0x%x\n", cm->cm_targ->tid, reply->IOCStatus);
mps_free_command(sc, cm);
}
static void
mpssas_evt_handler(struct mps_softc *sc, uintptr_t data,
MPI2_EVENT_NOTIFICATION_REPLY *event)
{
struct mpssas_softc *sassc;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
sassc = sc->sassc;
mps_print_evt_sas(sc, event);
switch (event->Event) {
case MPI2_EVENT_SAS_DISCOVERY:
{
MPI2_EVENT_DATA_SAS_DISCOVERY *data;
data = (MPI2_EVENT_DATA_SAS_DISCOVERY *)&event->EventData;
if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_STARTED)
mps_dprint(sc, MPS_TRACE,"SAS discovery start event\n");
if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_COMPLETED) {
mps_dprint(sc, MPS_TRACE, "SAS discovery end event\n");
sassc->flags &= ~MPSSAS_IN_DISCOVERY;
mpssas_discovery_end(sassc);
}
break;
}
case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
{
MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *data;
MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy;
int i;
data = (MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *)
&event->EventData;
if (data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED) {
if (bootverbose)
printf("Expander found at enclosure %d\n",
data->EnclosureHandle);
mpssas_probe_device(sc, data->ExpanderDevHandle);
}
for (i = 0; i < data->NumEntries; i++) {
phy = &data->PHY[i];
switch (phy->PhyStatus & MPI2_EVENT_SAS_TOPO_RC_MASK) {
case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
mpssas_probe_device(sc, phy->AttachedDevHandle);
break;
case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
mpssas_prepare_remove(sassc, phy);
break;
case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
default:
break;
}
}
break;
}
case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
break;
default:
break;
}
mps_free_reply(sc, data);
}
static int
mpssas_register_events(struct mps_softc *sc)
{
uint8_t events[16];
bzero(events, 16);
setbit(events, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
setbit(events, MPI2_EVENT_SAS_DISCOVERY);
setbit(events, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE);
setbit(events, MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE);
setbit(events, MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW);
setbit(events, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
setbit(events, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
mps_register_events(sc, events, mpssas_evt_handler, NULL,
&sc->sassc->mpssas_eh);
return (0);
}
int
mps_attach_sas(struct mps_softc *sc)
{
struct mpssas_softc *sassc;
int error = 0;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
sassc = malloc(sizeof(struct mpssas_softc), M_MPT2, M_WAITOK|M_ZERO);
sassc->targets = malloc(sizeof(struct mpssas_target) *
sc->facts->MaxTargets, M_MPT2, M_WAITOK|M_ZERO);
sc->sassc = sassc;
sassc->sc = sc;
if ((sassc->devq = cam_simq_alloc(sc->num_reqs)) == NULL) {
mps_dprint(sc, MPS_FAULT, "Cannot allocate SIMQ\n");
error = ENOMEM;
goto out;
}
sassc->sim = cam_sim_alloc(mpssas_action, mpssas_poll, "mps", sassc,
device_get_unit(sc->mps_dev), &sc->mps_mtx, sc->num_reqs, sc->num_reqs,
sassc->devq);
if (sassc->sim == NULL) {
mps_dprint(sc, MPS_FAULT, "Cannot allocate SIM\n");
error = EINVAL;
goto out;
}
/*
* XXX There should be a bus for every port on the adapter, but since
* we're just going to fake the topology for now, we'll pretend that
* everything is just a target on a single bus.
*/
mps_lock(sc);
if ((error = xpt_bus_register(sassc->sim, sc->mps_dev, 0)) != 0) {
mps_dprint(sc, MPS_FAULT, "Error %d registering SCSI bus\n",
error);
mps_unlock(sc);
goto out;
}
/*
* Assume that discovery events will start right away. Freezing
* the simq will prevent the CAM boottime scanner from running
* before discovery is complete.
*/
sassc->flags = MPSSAS_IN_STARTUP | MPSSAS_IN_DISCOVERY;
xpt_freeze_simq(sassc->sim, 1);
mps_unlock(sc);
callout_init(&sassc->discovery_callout, 1 /*mpsafe*/);
sassc->discovery_timeouts = 0;
mpssas_register_events(sc);
out:
if (error)
mps_detach_sas(sc);
return (error);
}
int
mps_detach_sas(struct mps_softc *sc)
{
struct mpssas_softc *sassc;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
if (sc->sassc == NULL)
return (0);
sassc = sc->sassc;
/* Make sure CAM doesn't wedge if we had to bail out early. */
mps_lock(sc);
if (sassc->flags & MPSSAS_IN_STARTUP)
xpt_release_simq(sassc->sim, 1);
mps_unlock(sc);
if (sassc->mpssas_eh != NULL)
mps_deregister_events(sc, sassc->mpssas_eh);
mps_lock(sc);
if (sassc->sim != NULL) {
xpt_bus_deregister(cam_sim_path(sassc->sim));
cam_sim_free(sassc->sim, FALSE);
}
mps_unlock(sc);
if (sassc->devq != NULL)
cam_simq_free(sassc->devq);
free(sassc->targets, M_MPT2);
free(sassc, M_MPT2);
sc->sassc = NULL;
return (0);
}
static void
mpssas_discovery_end(struct mpssas_softc *sassc)
{
struct mps_softc *sc = sassc->sc;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
if (sassc->flags & MPSSAS_DISCOVERY_TIMEOUT_PENDING)
callout_stop(&sassc->discovery_callout);
if ((sassc->flags & MPSSAS_IN_STARTUP) != 0) {
mps_dprint(sc, MPS_INFO,
"mpssas_discovery_end: removing confighook\n");
sassc->flags &= ~MPSSAS_IN_STARTUP;
xpt_release_simq(sassc->sim, 1);
}
#if 0
mpssas_announce_device(sassc, NULL);
#endif
}
static void
mpssas_announce_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
{
union ccb *ccb;
int bus, tid, lun;
/*
* Force a rescan, a hackish way to announce devices.
* XXX Doing a scan on an individual device is hackish in that it
* won't scan the LUNs.
* XXX Does it matter if any of this fails?
*/
bus = cam_sim_path(sassc->sim);
if (targ != NULL) {
tid = targ->tid;
lun = 0;
} else {
tid = CAM_TARGET_WILDCARD;
lun = CAM_LUN_WILDCARD;
}
ccb = xpt_alloc_ccb_nowait();
if (ccb == NULL)
return;
if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, bus, tid,
CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
xpt_free_ccb(ccb);
return;
}
mps_dprint(sassc->sc, MPS_INFO, "Triggering rescan of %d:%d:-1\n",
bus, tid);
xpt_rescan(ccb);
}
static void
mpssas_startup(void *data)
{
struct mpssas_softc *sassc = data;
mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
mps_lock(sassc->sc);
if ((sassc->flags & MPSSAS_IN_DISCOVERY) == 0) {
mpssas_discovery_end(sassc);
} else {
if (sassc->discovery_timeouts < MPSSAS_MAX_DISCOVERY_TIMEOUTS) {
sassc->flags |= MPSSAS_DISCOVERY_TIMEOUT_PENDING;
callout_reset(&sassc->discovery_callout,
MPSSAS_DISCOVERY_TIMEOUT * hz,
mpssas_discovery_timeout, sassc);
sassc->discovery_timeouts++;
} else {
mps_dprint(sassc->sc, MPS_FAULT,
"Discovery timed out, continuing.\n");
sassc->flags &= ~MPSSAS_IN_DISCOVERY;
mpssas_discovery_end(sassc);
}
}
mps_unlock(sassc->sc);
return;
}
static void
mpssas_discovery_timeout(void *data)
{
struct mpssas_softc *sassc = data;
struct mps_softc *sc;
sc = sassc->sc;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
mps_lock(sc);
mps_printf(sc,
"Timeout waiting for discovery, interrupts may not be working!\n");
sassc->flags &= ~MPSSAS_DISCOVERY_TIMEOUT_PENDING;
/* Poll the hardware for events in case interrupts aren't working */
mps_intr_locked(sc);
mps_unlock(sc);
/* Check the status of discovery and re-arm the timeout if needed */
mpssas_startup(sassc);
}
static void
mpssas_action(struct cam_sim *sim, union ccb *ccb)
{
struct mpssas_softc *sassc;
sassc = cam_sim_softc(sim);
mps_dprint(sassc->sc, MPS_TRACE, "%s func 0x%x\n", __func__,
ccb->ccb_h.func_code);
switch (ccb->ccb_h.func_code) {
case XPT_PATH_INQ:
{
struct ccb_pathinq *cpi = &ccb->cpi;
cpi->version_num = 1;
cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
cpi->target_sprt = 0;
cpi->hba_misc = PIM_NOBUSRESET;
cpi->hba_eng_cnt = 0;
cpi->max_target = sassc->sc->facts->MaxTargets - 1;
cpi->max_lun = 0;
cpi->initiator_id = 255;
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
strncpy(cpi->hba_vid, "LSILogic", HBA_IDLEN);
strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
cpi->unit_number = cam_sim_unit(sim);
cpi->bus_id = cam_sim_bus(sim);
cpi->base_transfer_speed = 150000;
cpi->transport = XPORT_SAS;
cpi->transport_version = 0;
cpi->protocol = PROTO_SCSI;
cpi->protocol_version = SCSI_REV_SPC;
cpi->ccb_h.status = CAM_REQ_CMP;
break;
}
case XPT_GET_TRAN_SETTINGS:
{
struct ccb_trans_settings *cts;
struct ccb_trans_settings_sas *sas;
struct ccb_trans_settings_scsi *scsi;
struct mpssas_target *targ;
cts = &ccb->cts;
sas = &cts->xport_specific.sas;
scsi = &cts->proto_specific.scsi;
targ = &sassc->targets[cts->ccb_h.target_id];
if (targ->handle == 0x0) {
cts->ccb_h.status = CAM_TID_INVALID;
break;
}
cts->protocol_version = SCSI_REV_SPC2;
cts->transport = XPORT_SAS;
cts->transport_version = 0;
sas->valid = CTS_SAS_VALID_SPEED;
switch (targ->linkrate) {
case 0x08:
sas->bitrate = 150000;
break;
case 0x09:
sas->bitrate = 300000;
break;
case 0x0a:
sas->bitrate = 600000;
break;
default:
sas->valid = 0;
}
cts->protocol = PROTO_SCSI;
scsi->valid = CTS_SCSI_VALID_TQ;
scsi->flags = CTS_SCSI_FLAGS_TAG_ENB;
cts->ccb_h.status = CAM_REQ_CMP;
break;
}
case XPT_CALC_GEOMETRY:
cam_calc_geometry(&ccb->ccg, /*extended*/1);
ccb->ccb_h.status = CAM_REQ_CMP;
break;
case XPT_RESET_DEV:
mpssas_action_resetdev(sassc, ccb);
return;
case XPT_RESET_BUS:
case XPT_ABORT:
case XPT_TERM_IO:
ccb->ccb_h.status = CAM_REQ_CMP;
break;
case XPT_SCSI_IO:
mpssas_action_scsiio(sassc, ccb);
return;
default:
ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
break;
}
xpt_done(ccb);
}
#if 0
static void
mpssas_resettimeout_complete(struct mps_softc *sc, struct mps_command *cm)
{
MPI2_SCSI_TASK_MANAGE_REPLY *resp;
uint16_t code;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
code = resp->ResponseCode;
mps_free_command(sc, cm);
mpssas_unfreeze_device(sassc, targ);
if (code != MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) {
mps_reset_controller(sc);
}
return;
}
#endif
static void
mpssas_scsiio_timeout(void *data)
{
union ccb *ccb;
struct mps_softc *sc;
struct mps_command *cm;
struct mpssas_target *targ;
cm = (struct mps_command *)data;
sc = cm->cm_sc;
/*
* Run the interrupt handler to make sure it's not pending. This
* isn't perfect because the command could have already completed
* and been re-used, though this is unlikely.
*/
mps_lock(sc);
mps_intr_locked(sc);
if (cm->cm_state == MPS_CM_STATE_FREE) {
mps_unlock(sc);
return;
}
ccb = cm->cm_complete_data;
targ = cm->cm_targ;
if (targ == 0x00)
/* Driver bug */
targ = &sc->sassc->targets[ccb->ccb_h.target_id];
xpt_print(ccb->ccb_h.path, "SCSI command timeout on device handle "
"0x%04x SMID %d\n", targ->handle, cm->cm_desc.Default.SMID);
/* Inform CAM about the timeout and that recovery is starting. */
#if 0
if ((targ->flags & MPSSAS_TARGET_INRECOVERY) == 0) {
mpssas_freeze_device(sc->sassc, targ);
ccb->ccb_h.status = CAM_CMD_TIMEOUT;
xpt_done(ccb);
}
#endif
mpssas_freeze_device(sc->sassc, targ);
ccb->ccb_h.status = CAM_CMD_TIMEOUT;
/*
* recycle the command into recovery so that there's no risk of
* command allocation failure.
*/
cm->cm_state = MPS_CM_STATE_TIMEDOUT;
mpssas_recovery(sc, cm);
mps_unlock(sc);
}
static void
mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm)
{
MPI2_SCSI_TASK_MANAGE_REQUEST *req;
req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
mps_printf(sc, "%s: abort request on handle %#04x SMID %d "
"complete\n", __func__, req->DevHandle, req->TaskMID);
/*
* Release the SIM queue, we froze it when we sent the abort.
*/
xpt_release_simq(sc->sassc->sim, 1);
mps_free_command(sc, cm);
}
static void
mpssas_recovery(struct mps_softc *sc, struct mps_command *abort_cm)
{
struct mps_command *cm;
MPI2_SCSI_TASK_MANAGE_REQUEST *req, *orig_req;
int error;
cm = mps_alloc_command(sc);
if (cm == NULL) {
mps_printf(sc, "%s: command allocation failure\n", __func__);
return;
}
cm->cm_targ = abort_cm->cm_targ;
cm->cm_complete = mpssas_abort_complete;
req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
orig_req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)abort_cm->cm_req;
req->DevHandle = abort_cm->cm_targ->handle;
req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
memcpy(req->LUN, orig_req->LUN, sizeof(req->LUN));
req->TaskMID = abort_cm->cm_desc.Default.SMID;
cm->cm_data = NULL;
cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
/*
* Freeze the SIM queue while we issue the abort. According to the
* Fusion-MPT 2.0 spec, task management requests are serialized,
* and so the host should not send any I/O requests while task
* management requests are pending.
*/
xpt_freeze_simq(sc->sassc->sim, 1);
error = mps_map_command(sc, cm);
if (error != 0) {
mps_printf(sc, "%s: error mapping abort request!\n", __func__);
xpt_release_simq(sc->sassc->sim, 1);
}
#if 0
error = mpssas_reset(sc, targ, &resetcm);
if ((error != 0) && (error != EBUSY)) {
mps_printf(sc, "Error resetting device!\n");
mps_unlock(sc);
return;
}
targ->flags |= MPSSAS_TARGET_INRESET;
cm->cm_complete = mpssas_resettimeout_complete;
cm->cm_complete_data = cm;
mps_map_command(sassc->sc, cm);
#endif
}
static void
mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
{
MPI2_SCSI_IO_REQUEST *req;
struct ccb_scsiio *csio;
struct mps_softc *sc;
struct mpssas_target *targ;
struct mps_command *cm;
mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
sc = sassc->sc;
csio = &ccb->csio;
targ = &sassc->targets[csio->ccb_h.target_id];
if (targ->handle == 0x0) {
csio->ccb_h.status = CAM_SEL_TIMEOUT;
xpt_done(ccb);
return;
}
cm = mps_alloc_command(sc);
if (cm == NULL) {
if ((sassc->flags & MPSSAS_QUEUE_FROZEN) == 0) {
xpt_freeze_simq(sassc->sim, 1);
sassc->flags |= MPSSAS_QUEUE_FROZEN;
}
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
ccb->ccb_h.status |= CAM_REQUEUE_REQ;
xpt_done(ccb);
return;
}
req = (MPI2_SCSI_IO_REQUEST *)cm->cm_req;
req->DevHandle = targ->handle;
req->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
req->MsgFlags = 0;
req->SenseBufferLowAddress = cm->cm_sense_busaddr;
req->SenseBufferLength = MPS_SENSE_LEN;
req->SGLFlags = 0;
req->ChainOffset = 0;
req->SGLOffset0 = 24; /* 32bit word offset to the SGL */
req->SGLOffset1= 0;
req->SGLOffset2= 0;
req->SGLOffset3= 0;
req->SkipCount = 0;
req->DataLength = csio->dxfer_len;
req->BidirectionalDataLength = 0;
req->IoFlags = csio->cdb_len;
req->EEDPFlags = 0;
/* Note: BiDirectional transfers are not supported */
switch (csio->ccb_h.flags & CAM_DIR_MASK) {
case CAM_DIR_IN:
req->Control = MPI2_SCSIIO_CONTROL_READ;
cm->cm_flags |= MPS_CM_FLAGS_DATAIN;
break;
case CAM_DIR_OUT:
req->Control = MPI2_SCSIIO_CONTROL_WRITE;
cm->cm_flags |= MPS_CM_FLAGS_DATAOUT;
break;
case CAM_DIR_NONE:
default:
req->Control = MPI2_SCSIIO_CONTROL_NODATATRANSFER;
break;
}
/*
* It looks like the hardware doesn't require an explicit tag
* number for each transaction. SAM Task Management not supported
* at the moment.
*/
switch (csio->tag_action) {
case MSG_HEAD_OF_Q_TAG:
req->Control |= MPI2_SCSIIO_CONTROL_HEADOFQ;
break;
case MSG_ORDERED_Q_TAG:
req->Control |= MPI2_SCSIIO_CONTROL_ORDEREDQ;
break;
case MSG_ACA_TASK:
req->Control |= MPI2_SCSIIO_CONTROL_ACAQ;
break;
case CAM_TAG_ACTION_NONE:
case MSG_SIMPLE_Q_TAG:
default:
req->Control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
break;
}
/* XXX Need to handle multi-level LUNs */
if (csio->ccb_h.target_lun > 255) {
mps_free_command(sc, cm);
ccb->ccb_h.status = CAM_LUN_INVALID;
xpt_done(ccb);
return;
}
req->LUN[1] = csio->ccb_h.target_lun;
if (csio->ccb_h.flags & CAM_CDB_POINTER)
bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len);
else
bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len);
req->IoFlags = csio->cdb_len;
cm->cm_data = csio->data_ptr;
cm->cm_length = csio->dxfer_len;
cm->cm_sge = &req->SGL;
cm->cm_sglsize = (32 - 24) * 4;
cm->cm_desc.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
cm->cm_desc.SCSIIO.DevHandle = targ->handle;
cm->cm_complete = mpssas_scsiio_complete;
cm->cm_complete_data = ccb;
cm->cm_targ = targ;
callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000,
mpssas_scsiio_timeout, cm);
mps_map_command(sc, cm);
return;
}
static void
mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
{
MPI2_SCSI_IO_REPLY *rep;
union ccb *ccb;
struct mpssas_softc *sassc;
u_int sense_len;
int dir = 0;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
callout_stop(&cm->cm_callout);
sassc = sc->sassc;
ccb = cm->cm_complete_data;
rep = (MPI2_SCSI_IO_REPLY *)cm->cm_reply;
if (cm->cm_data != NULL) {
if (cm->cm_flags & MPS_CM_FLAGS_DATAIN)
dir = BUS_DMASYNC_POSTREAD;
else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT)
dir = BUS_DMASYNC_POSTWRITE;;
bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir);
bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
}
if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
ccb->ccb_h.flags |= CAM_RELEASE_SIMQ;
sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
}
/* Take the fast path to completion */
if (cm->cm_reply == NULL) {
ccb->ccb_h.status = CAM_REQ_CMP;
ccb->csio.scsi_status = SCSI_STATUS_OK;
mps_free_command(sc, cm);
xpt_done(ccb);
return;
}
mps_dprint(sc, MPS_INFO, "(%d:%d:%d) IOCStatus= 0x%x, "
"ScsiStatus= 0x%x, SCSIState= 0x%x TransferCount= 0x%x\n",
xpt_path_path_id(ccb->ccb_h.path),
xpt_path_target_id(ccb->ccb_h.path),
xpt_path_lun_id(ccb->ccb_h.path), rep->IOCStatus,
rep->SCSIStatus, rep->SCSIState, rep->TransferCount);
switch (rep->IOCStatus & MPI2_IOCSTATUS_MASK) {
case MPI2_IOCSTATUS_BUSY:
case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
/*
* The controller is overloaded, try waiting a bit for it
* to free up.
*/
ccb->ccb_h.status = CAM_BUSY;
break;
case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
ccb->csio.resid = cm->cm_length - rep->TransferCount;
/* FALLTHROUGH */
case MPI2_IOCSTATUS_SUCCESS:
case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
ccb->ccb_h.status = CAM_REQ_CMP;
break;
case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
/* resid is ignored for this condition */
ccb->csio.resid = 0;
ccb->ccb_h.status = CAM_DATA_RUN_ERR;
break;
case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
ccb->ccb_h.status = CAM_DEV_NOT_THERE;
break;
case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
/*
* This is one of the responses that comes back when an I/O
* has been aborted. If it is because of a timeout that we
* initiated, just set the status to CAM_CMD_TIMEOUT.
* Otherwise set it to CAM_REQ_ABORTED. The effect on the
* command is the same (it gets retried, subject to the
* retry counter), the only difference is what gets printed
* on the console.
*/
if (cm->cm_state == MPS_CM_STATE_TIMEDOUT)
ccb->ccb_h.status = CAM_CMD_TIMEOUT;
else
ccb->ccb_h.status = CAM_REQ_ABORTED;
break;
case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
ccb->ccb_h.status = CAM_REQ_ABORTED;
break;
case MPI2_IOCSTATUS_INVALID_SGL:
mps_print_scsiio_cmd(sc, cm);
ccb->ccb_h.status = CAM_UNREC_HBA_ERROR;
break;
case MPI2_IOCSTATUS_INVALID_FUNCTION:
case MPI2_IOCSTATUS_INTERNAL_ERROR:
case MPI2_IOCSTATUS_INVALID_VPID:
case MPI2_IOCSTATUS_INVALID_FIELD:
case MPI2_IOCSTATUS_INVALID_STATE:
case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED:
case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
default:
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
}
if ((rep->SCSIState & MPI2_SCSI_STATE_NO_SCSI_STATUS) == 0) {
ccb->csio.scsi_status = rep->SCSIStatus;
switch (rep->SCSIStatus) {
case MPI2_SCSI_STATUS_TASK_SET_FULL:
case MPI2_SCSI_STATUS_CHECK_CONDITION:
ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
break;
case MPI2_SCSI_STATUS_COMMAND_TERMINATED:
case MPI2_SCSI_STATUS_TASK_ABORTED:
ccb->ccb_h.status = CAM_REQ_ABORTED;
break;
case MPI2_SCSI_STATUS_GOOD:
default:
break;
}
}
if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
sense_len = MIN(rep->SenseCount,
sizeof(struct scsi_sense_data));
if (sense_len < rep->SenseCount)
ccb->csio.sense_resid = rep->SenseCount - sense_len;
bcopy(cm->cm_sense, &ccb->csio.sense_data, sense_len);
ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
}
if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_FAILED)
ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
if (rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
mps_free_command(sc, cm);
xpt_done(ccb);
}
static void
mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb)
{
struct mps_softc *sc;
struct mps_command *cm;
struct mpssas_target *targ;
int error;
sc = sassc->sc;
targ = &sassc->targets[ccb->ccb_h.target_id];
if (targ->flags & MPSSAS_TARGET_INRECOVERY) {
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_done(ccb);
return;
}
cm = mps_alloc_command(sc);
if (cm == NULL) {
mps_printf(sc, "mpssas_action_resetdev: cannot alloc command\n");
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_done(ccb);
return;
}
cm->cm_targ = targ;
cm->cm_complete = mpssas_resetdev_complete;
cm->cm_complete_data = ccb;
error = mpssas_resetdev(sassc, cm);
if (error) {
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_done(ccb);
return;
}
}
static int
mpssas_resetdev(struct mpssas_softc *sassc, struct mps_command *cm)
{
MPI2_SCSI_TASK_MANAGE_REQUEST *req;
struct mps_softc *sc;
int error;
mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
sc = sassc->sc;
req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
req->DevHandle = cm->cm_targ->handle;
req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
/* SAS Hard Link Reset / SATA Link Reset */
req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
cm->cm_data = NULL;
cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
xpt_freeze_simq(sassc->sim, 1);
error = mps_map_command(sassc->sc, cm);
if (error != 0)
xpt_release_simq(sassc->sim, 1);
return (error);
}
static void
mpssas_resetdev_complete(struct mps_softc *sc, struct mps_command *cm)
{
MPI2_SCSI_TASK_MANAGE_REPLY *resp;
union ccb *ccb;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
ccb = cm->cm_complete_data;
printf("resetdev complete IOCStatus= 0x%x ResponseCode= 0x%x\n",
resp->IOCStatus, resp->ResponseCode);
if (resp->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE)
ccb->ccb_h.status = CAM_REQ_CMP;
else
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
mps_free_command(sc, cm);
xpt_release_simq(sc->sassc->sim, 1);
xpt_done(ccb);
}
static void
mpssas_poll(struct cam_sim *sim)
{
struct mpssas_softc *sassc;
sassc = cam_sim_softc(sim);
mps_intr_locked(sassc->sc);
}
static void
mpssas_freeze_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
{
}
static void
mpssas_unfreeze_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
{
}