/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Implementation of "scsi_vhci_f_tpgs_tape" T10 standard based failover_ops.
*
* NOTE: for sequential devices only.
*/
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/scsi/scsi.h>
#include <sys/scsi/adapters/scsi_vhci.h>
#include <sys/scsi/adapters/scsi_vhci_tpgs.h>
/* Supported device table entries. */
char *tpgs_tape_dev_table[] = { NULL };
static void tpgs_tape_init(void);
static int tpgs_tape_device_probe(struct scsi_device *sd,
struct scsi_inquiry *inq, void **ctpriv);
/* Failover module plumbing. */
#ifdef lint
#define scsi_vhci_failover_ops scsi_vhci_failover_ops_f_tpgs_tape
#endif /* lint */
struct scsi_failover_ops scsi_vhci_failover_ops = {
SFO_REV,
"f_tpgs_tape",
tpgs_tape_dev_table,
tpgs_tape_init,
tpgs_tape_device_probe,
/* The rest of the implementation comes from SFO_NAME_TPGS import */
};
static struct modlmisc modlmisc = {
&mod_miscops, "f_tpgs_tape"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
/*
* External function definitions
*/
extern struct scsi_failover_ops *vhci_failover_ops_by_name(char *);
int
_init()
{
return (mod_install(&modlinkage));
}
int
_fini()
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/* ARGSUSED */
static int
tpgs_tape_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq,
void **ctpriv)
{
int mode;
int state;
int xlf;
int preferred = 0;
int support;
VHCI_DEBUG(6, (CE_NOTE, NULL, "tpgs_tape_device_probe: vidpid %s\n",
inq->inq_vid));
if (inq->inq_tpgs == TPGS_FAILOVER_NONE) {
VHCI_DEBUG(4, (CE_WARN, NULL,
"!tpgs_tape_device_probe: not a standard tpgs device"));
support = SFO_DEVICE_PROBE_PHCI;
} else if (inq->inq_dtype != DTYPE_SEQUENTIAL) {
VHCI_DEBUG(4, (CE_NOTE, NULL,
"!tpgs_tape_device_probe: Detected a "
"Standard Asymmetric device "
"not yet supported\n"));
support = SFO_DEVICE_PROBE_PHCI;
} else if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf,
&preferred)) {
VHCI_DEBUG(4, (CE_WARN, NULL, "!unable to fetch fo "
"mode: sd(%p)", (void *) sd));
support = SFO_DEVICE_PROBE_PHCI;
} else if (inq->inq_tpgs == TPGS_FAILOVER_IMPLICIT) {
VHCI_DEBUG(1, (CE_NOTE, NULL,
"!tpgs_tape_device_probe: Detected a "
"Standard Asymmetric device "
"with implicit failover\n"));
support = SFO_DEVICE_PROBE_VHCI;
} else if (inq->inq_tpgs == TPGS_FAILOVER_EXPLICIT) {
VHCI_DEBUG(1, (CE_NOTE, NULL,
"!tpgs_tape_device_probe: Detected a "
"Standard Asymmetric device "
"with explicit failover\n"));
support = SFO_DEVICE_PROBE_VHCI;
} else if (inq->inq_tpgs == TPGS_FAILOVER_BOTH) {
VHCI_DEBUG(1, (CE_NOTE, NULL,
"!tpgs_tape_device_probe: Detected a "
"Standard Asymmetric device "
"which supports both implicit and explicit failover\n"));
support = SFO_DEVICE_PROBE_VHCI;
} else {
VHCI_DEBUG(1, (CE_WARN, NULL,
"!tpgs_tape_device_probe: "
"Unknown tpgs_bits: %x", inq->inq_tpgs));
support = SFO_DEVICE_PROBE_PHCI;
}
if (support == SFO_DEVICE_PROBE_VHCI) {
/*
* Policy only applies to 'client' probe, not
* vhci_is_dev_supported() pHCI probe. Detect difference
* based on ctpriv.
*/
if (ctpriv &&
(mdi_set_lb_policy(sd->sd_dev, LOAD_BALANCE_NONE) !=
MDI_SUCCESS)) {
VHCI_DEBUG(6, (CE_NOTE, NULL, "!fail load balance none"
": %s\n", inq->inq_vid));
support = SFO_DEVICE_PROBE_PHCI;
}
}
return (support);
}
static void
tpgs_tape_init(void)
{
struct scsi_failover_ops *sfo, *ssfo, clone;
/* clone SFO_NAME_SYM implementation for most things */
ssfo = vhci_failover_ops_by_name(SFO_NAME_TPGS);
if (ssfo == NULL) {
VHCI_DEBUG(4, (CE_NOTE, NULL, "!tpgs_tape: "
"can't import " SFO_NAME_SYM "\n"));
return;
}
sfo = &scsi_vhci_failover_ops;
clone = *ssfo;
clone.sfo_rev = sfo->sfo_rev;
clone.sfo_name = sfo->sfo_name;
clone.sfo_devices = sfo->sfo_devices;
clone.sfo_init = sfo->sfo_init;
clone.sfo_device_probe = sfo->sfo_device_probe;
*sfo = clone;
}