mpapi_impl.c revision e9b793566614d39671404d615858fd0cd5ad9635
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* SNIA Multipath Management API implementation
*/
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/disp.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunmdi.h>
#include <sys/mdi_impldefs.h>
#include <sys/scsi/scsi.h>
#include <sys/scsi/impl/services.h>
#include <sys/scsi/impl/scsi_reset_notify.h>
#include <sys/scsi/adapters/scsi_vhci.h>
/* used to manually force a request sense */
int vhci_force_manual_sense = 0;
#define STD_ACTIVE_OPTIMIZED 0x0
#define STD_ACTIVE_NONOPTIMIZED 0x1
#define STD_STANDBY 0x2
#define STD_UNAVAILABLE 0x3
#define STD_TRANSITIONING 0xf
/*
* MP-API Prototypes
*/
int vhci_mpapi_init(struct scsi_vhci *);
void vhci_mpapi_add_dev_prod(struct scsi_vhci *, char *);
int vhci_mpapi_ctl(dev_t, int, intptr_t, int, cred_t *, int *);
void vhci_update_mpapi_data(struct scsi_vhci *,
scsi_vhci_lun_t *, mdi_pathinfo_t *);
void* vhci_get_mpapi_item(struct scsi_vhci *, mpapi_list_header_t *,
uint8_t, void*);
int vhci_mpapi_sync_init_port_list(dev_info_t *, void *);
int vhci_mpapi_get_vhci(dev_info_t *, void *);
void vhci_mpapi_set_path_state(dev_info_t *, mdi_pathinfo_t *, int);
void vhci_mpapi_synthesize_tpg_data(struct scsi_vhci *, scsi_vhci_lun_t *,
mdi_pathinfo_t *);
void vhci_mpapi_update_tpg_data(struct scsi_address *, char *);
int vhci_mpapi_update_tpg_acc_state_for_lu(struct scsi_vhci *,
scsi_vhci_lun_t *);
/* Static Functions */
static int vhci_get_driver_prop(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_dev_prod_list(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_dev_prod_prop(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_lu_list(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_lu_list_from_tpg(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_tpg_list_for_lu(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_lu_prop(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_path_list_for_mp_lu(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_path_list_for_init_port(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_path_list_for_target_port(struct scsi_vhci *,
mp_iocdata_t *, void *, void *, int);
static int vhci_get_path_prop(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_init_port_list(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_init_port_prop(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_target_port_prop(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_tpg_prop(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_target_port_list_for_tpg(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_set_tpg_access_state(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_prop_lb_list(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_get_prop_lb_prop(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_assign_lu_to_tpg(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_enable_auto_failback(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_disable_auto_failback(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_enable_path(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_disable_path(struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_send_uscsi_cmd(dev_t dev, struct scsi_vhci *, mp_iocdata_t *,
void *, void *, int);
static int vhci_mpapi_validate(void *, mp_iocdata_t *, int, cred_t *);
static uint64_t vhci_mpapi_create_oid(mpapi_priv_t *, uint8_t);
static int vhci_mpapi_ioctl(dev_t dev, struct scsi_vhci *, void *,
mp_iocdata_t *, int, cred_t *);
static int vhci_mpapi_add_to_list(mpapi_list_header_t *, mpapi_item_list_t *);
static mpapi_item_list_t *vhci_mpapi_create_item(struct scsi_vhci *,
uint8_t, void *);
static mpapi_item_list_t *vhci_mpapi_get_tpg_item(struct scsi_vhci *,
uint32_t, void *, char *, void *);
static mpapi_list_header_t *vhci_mpapi_create_list_head();
static int vhci_get_mpiocdata(const void *, mp_iocdata_t *, int);
static int vhci_is_model_type32(int);
static int vhci_mpapi_copyout_iocdata(void *, void *, int);
static int vhci_mpapi_chk_last_path(mdi_pathinfo_t *);
static int vhci_mpapi_sync_lu_oid_list(struct scsi_vhci *);
static mpapi_item_list_t *vhci_mpapi_get_tpg_for_lun(struct scsi_vhci *,
char *, void *, void *);
static int vhci_mpapi_check_tp_in_tpg(mpapi_tpg_data_t *tpgdata, void *tp);
static void vhci_mpapi_log_sysevent(dev_info_t *, uint64_t *, char *);
static mpapi_item_list_t *vhci_mpapi_match_pip(struct scsi_vhci *,
mpapi_item_list_t *, void *);
static mpapi_item_list_t *vhci_mpapi_match_lu(struct scsi_vhci *,
mpapi_item_list_t *, void *);
static void *vhci_mpapi_get_rel_tport_pair(struct scsi_vhci *vhci,
mpapi_list_header_t *list, void *tgt_port, uint32_t rel_tid);
/*
* Extern variables, structures and functions
*/
extern void *vhci_softstate;
extern char vhci_version_name[];
extern int (*tpgs_set_target_groups)(struct scsi_address *, int, int);
extern void mdi_vhci_walk_phcis(dev_info_t *,
int (*)(dev_info_t *, void *), void *);
extern void vhci_update_pathstates(void *);
extern int vhci_uscsi_iostart(struct buf *bp);
/*
* Routine for SCSI VHCI MPAPI IOCTL implementation.
*/
/* ARGSUSED */
int
vhci_mpapi_ctl(dev_t dev, int cm, intptr_t data, int mode,
cred_t *credp, int *rval)
{
struct scsi_vhci *vhci;
dev_info_t *vdip;
int retval = 0;
mp_iocdata_t mpio_blk;
mp_iocdata_t *mpioc = &mpio_blk;
/* Check for validity of vhci structure */
vhci = ddi_get_soft_state(vhci_softstate, MINOR2INST(getminor(dev)));
if (vhci == NULL) {
return (ENXIO);
}
mutex_enter(&vhci->vhci_mutex);
if ((vhci->vhci_state & VHCI_STATE_OPEN) == 0) {
mutex_exit(&vhci->vhci_mutex);
return (ENXIO);
}
mutex_exit(&vhci->vhci_mutex);
/* Get the vhci dip */
vdip = vhci->vhci_dip;
ASSERT(vdip != NULL);
/*
* Get IOCTL parameters from userland
*/
if (vhci_get_mpiocdata((const void *)data, mpioc, mode) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_mpapi_ctl: "
"vhci_get_mpiocdata() failed"));
}
if (mpioc->mp_cmd < MP_API_SUBCMD_MIN ||
mpioc->mp_cmd > MP_API_SUBCMD_MAX) {
return (ENXIO);
}
retval = vhci_mpapi_ioctl(dev, vhci, (void *)data, mpioc, mode, credp);
return (retval);
}
/* ARGSUSED */
static int
vhci_mpapi_validate(void *udata, mp_iocdata_t *mpioc, int mode, cred_t *credp)
{
int rval = 0, olen = 0;
int mode32 = 0;
if (vhci_is_model_type32(mode) == 1) {
mode32 = 1;
}
switch (mpioc->mp_cmd) {
case MP_GET_DEV_PROD_LIST:
case MP_GET_LU_LIST: /* XXX: This wont come; Plugin already has it */
case MP_GET_INIT_PORT_LIST: /* XXX: This call wont come either */
case MP_GET_TPG_LIST:
case MP_GET_PROPRIETARY_LOADBALANCE_LIST:
{
if ((mpioc->mp_olen == 0) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen == 0) {
/* We don't know alen yet, No point trying to set it */
mpioc->mp_errno = MP_MORE_DATA;
rval = MP_MORE_DATA;
}
}
break;
case MP_GET_DRIVER_PROP:
{
olen = sizeof (mp_driver_prop_t);
/* Adjust olen to account for the caddr_t in 32-bit mode */
if (mode32 == 1) {
olen -= 4;
}
if ((mpioc->mp_obuf == NULL) ||
(mpioc->mp_olen < olen) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen < olen) {
mpioc->mp_alen = olen;
mpioc->mp_errno = MP_MORE_DATA;
}
}
break;
case MP_GET_DEV_PROD_PROP:
{
olen = sizeof (mp_dev_prod_prop_t);
if ((mpioc->mp_olen < olen) ||
(mpioc->mp_ilen < sizeof (uint64_t)) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen < olen) {
mpioc->mp_alen = olen;
mpioc->mp_errno = MP_MORE_DATA;
}
}
break;
case MP_GET_LU_PROP:
{
olen = sizeof (mp_logical_unit_prop_t);
/* Adjust olen to account for the caddr_t in 32-bit mode */
if (mode32 == 1) {
olen -= 4;
}
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_olen < olen) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen < olen) {
mpioc->mp_alen = olen;
mpioc->mp_errno = MP_MORE_DATA;
}
}
break;
case MP_GET_PATH_PROP:
{
olen = sizeof (mp_path_prop_t);
/* Adjust olen to account for the caddr_t in 32-bit mode */
if (mode32 == 1) {
olen -= 4;
}
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_olen < olen) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen < olen) {
mpioc->mp_alen = olen;
mpioc->mp_errno = MP_MORE_DATA;
}
}
break;
case MP_GET_INIT_PORT_PROP:
{
olen = sizeof (mp_init_port_prop_t);
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_olen < olen) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen < olen) {
mpioc->mp_alen = olen;
mpioc->mp_errno = MP_MORE_DATA;
}
}
break;
case MP_GET_TARGET_PORT_PROP:
{
olen = sizeof (mp_target_port_prop_t);
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_olen < olen) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen < olen) {
mpioc->mp_alen = olen;
mpioc->mp_errno = MP_MORE_DATA;
}
}
break;
case MP_GET_TPG_PROP:
{
olen = sizeof (mp_tpg_prop_t);
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_olen < olen) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen < olen) {
mpioc->mp_alen = olen;
mpioc->mp_errno = MP_MORE_DATA;
}
}
break;
case MP_GET_PROPRIETARY_LOADBALANCE_PROP:
{
olen = sizeof (mp_proprietary_loadbalance_prop_t);
/* Adjust olen to account for the caddr_t in 32-bit mode */
if (mode32 == 1) {
olen -= 4;
}
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_olen < olen) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen < olen) {
mpioc->mp_alen = olen;
mpioc->mp_errno = MP_MORE_DATA;
}
}
break;
case MP_GET_PATH_LIST_FOR_MP_LU:
case MP_GET_PATH_LIST_FOR_INIT_PORT:
case MP_GET_PATH_LIST_FOR_TARGET_PORT:
case MP_GET_LU_LIST_FROM_TPG:
case MP_GET_TPG_LIST_FOR_LU:
case MP_GET_TARGET_PORT_LIST_FOR_TPG:
{
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_olen == 0) ||
(mpioc->mp_obuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_READ)) {
rval = EINVAL;
}
if (mpioc->mp_olen == 0) {
/* We don't know alen yet, No point trying to set it */
mpioc->mp_errno = MP_MORE_DATA;
rval = MP_MORE_DATA;
}
}
break;
case MP_SET_TPG_ACCESS_STATE:
{
if (drv_priv(credp) != 0) {
rval = EPERM;
break;
}
if ((mpioc->mp_ilen != sizeof (mp_set_tpg_state_req_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_WRITE)) {
rval = EINVAL;
}
}
break;
case MP_ENABLE_AUTO_FAILBACK:
case MP_DISABLE_AUTO_FAILBACK:
{
if (drv_priv(credp) != 0) {
rval = EPERM;
break;
}
if ((mpioc->mp_ibuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_WRITE)) {
rval = EINVAL;
}
}
break;
case MP_ENABLE_PATH:
case MP_DISABLE_PATH:
{
if (drv_priv(credp) != 0) {
rval = EPERM;
break;
}
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_WRITE)) {
rval = EINVAL;
}
}
break;
case MP_SEND_SCSI_CMD:
{
cred_t *cr;
int olen = 0;
cr = ddi_get_cred();
if (drv_priv(credp) != 0 && drv_priv(cr) != 0) {
rval = EPERM;
break;
}
if (mode32 == 1) {
olen = sizeof (struct uscsi_cmd32);
} else {
olen = sizeof (struct uscsi_cmd);
}
/* oid is in the ibuf and the uscsi cmd is in the obuf */
if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_olen != olen) ||
(mpioc->mp_obuf == NULL)) {
rval = EINVAL;
}
}
break;
case MP_ASSIGN_LU_TO_TPG:
{
if (drv_priv(credp) != 0) {
rval = EPERM;
break;
}
if ((mpioc->mp_ilen != sizeof (mp_lu_tpg_pair_t)) ||
(mpioc->mp_ibuf == NULL) ||
(mpioc->mp_xfer != MP_XFER_WRITE)) {
rval = EINVAL;
}
}
break;
default:
{
rval = EINVAL;
}
} /* Closing the main switch */
return (rval);
}
/* ARGSUSED */
static int
vhci_get_driver_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
mp_driver_prop_t *mpdp = (mp_driver_prop_t *)output_data;
if (output_data == NULL) {
return (EINVAL);
}
(void) strlcpy(mpdp->driverVersion, vhci_version_name,
sizeof (mpdp->driverVersion));
mpdp->supportedLoadBalanceTypes =
MP_DRVR_LOAD_BALANCE_TYPE_NONE |
MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN |
MP_DRVR_LOAD_BALANCE_TYPE_LBA_REGION;
mpdp->canSetTPGAccess = B_TRUE;
mpdp->canOverridePaths = B_FALSE;
mpdp->exposesPathDeviceFiles = B_FALSE;
(void) strlcpy(mpdp->deviceFileNamespace, "/devices/scsi_vhci",
sizeof (mpdp->deviceFileNamespace));
mpdp->onlySupportsSpecifiedProducts = 1;
mpdp->maximumWeight = 1;
mpdp->failbackPollingRateMax = 0;
mpdp->currentFailbackPollingRate = 0;
mpdp->autoFailbackSupport = MP_DRVR_AUTO_FAILBACK_SUPPORT;
mutex_enter(&vhci->vhci_mutex);
mpdp->autoFailbackEnabled =
((vhci->vhci_conf_flags & VHCI_CONF_FLAGS_AUTO_FAILBACK) ?
1 : 0);
mutex_exit(&vhci->vhci_mutex);
mpdp->defaultLoadBalanceType =
MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN;
mpdp->probingPollingRateMax = 0;
mpdp->currentProbingPollingRate = 0;
mpdp->autoProbingSupport = 0;
mpdp->autoProbingEnabled = 0;
if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
mpioc->mp_olen, mode) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_driver_prop: "
"ddi_copyout() for 64-bit failed"));
mpioc->mp_errno = EFAULT;
} else {
mpioc->mp_errno = 0;
mpioc->mp_alen = sizeof (mp_iocdata_t);
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_dev_prod_list(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
mpapi_item_list_t *ilist;
if (output_data == NULL) {
return (EINVAL);
}
/*
* XXX: Get the Plugin OID from the input_data and apply below
* Currently, we know we have only 1 plugin, so it ok to directly
* return this only plugin's device product list.
*/
ilist = vhci->mp_priv->
obj_hdr_list[MP_OBJECT_TYPE_DEVICE_PRODUCT]->head;
while (ilist != NULL) {
if (count < list_len) {
oid_list[count] = (uint64_t)ilist->item->oid.raw_oid;
} else {
rval = MP_MORE_DATA;
}
ilist = ilist->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_dev_prod_list: "
"ddi_copyout() failed"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
} else {
mpioc->mp_errno = 0;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_dev_prod_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t *oid = (uint64_t *)(input_data);
mp_dev_prod_prop_t *dev_prop = NULL;
mpapi_item_list_t *ilist;
if ((output_data == NULL) || (input_data == NULL)) {
return (EINVAL);
}
ilist = vhci->mp_priv->
obj_hdr_list[MP_OBJECT_TYPE_DEVICE_PRODUCT]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
ilist = ilist->next;
}
if (ilist != NULL) {
dev_prop = (mp_dev_prod_prop_t *)(ilist->item->idata);
if (dev_prop == NULL) {
return (EINVAL);
}
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_dev_prod_prop: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
if (ddi_copyout((void *)dev_prop, mpioc->mp_obuf,
sizeof (mp_dev_prod_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_lu_list(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
mpapi_item_list_t *ilist;
mpapi_lu_data_t *ld;
if (output_data == NULL) {
return (EINVAL);
}
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
while (ilist != NULL) {
if (count < list_len) {
oid_list[count] = (uint64_t)(ilist->item->oid.raw_oid);
} else {
rval = MP_MORE_DATA;
}
ld = ilist->item->idata;
if (ld->valid == 0) {
count--;
}
ilist = ilist->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list: "
"ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
} else {
mpioc->mp_errno = 0;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_lu_list_from_tpg(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
uint64_t *oid = (uint64_t *)(input_data);
mpapi_item_list_t *ilist, *tpg_lu_list = NULL;
mpapi_tpg_data_t *mptpglu;
mpapi_lu_data_t *ld;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT_GROUP]
->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
ilist = ilist->next;
if (ilist == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list_from_tpg: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
rval = EINVAL;
} else if (*oid == ilist->item->oid.raw_oid) {
mptpglu = (mpapi_tpg_data_t *)(ilist->item->idata);
if (mptpglu->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list_from_"
"tpg: OID NOT FOUND - TPG IS INVALID"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
tpg_lu_list = mptpglu->lu_list->head;
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list_from_tpg: "
"Unknown Error"));
}
while (tpg_lu_list != NULL) {
if (count < list_len) {
oid_list[count] = (uint64_t)tpg_lu_list->
item->oid.raw_oid;
} else {
rval = MP_MORE_DATA;
}
/*
* Get rid of the latest entry if item is invalid
*/
ld = tpg_lu_list->item->idata;
if (ld->valid == 0) {
count--;
}
tpg_lu_list = tpg_lu_list->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list_from_tpg: "
"ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_tpg_list_for_lu(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
uint64_t *oid = (uint64_t *)(input_data);
mpapi_item_list_t *ilist, *mplu_tpg_list = NULL;
mpapi_lu_data_t *mplutpg;
mpapi_tpg_data_t *tpgd;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
ilist = ilist->next;
if (ilist == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_list_for_lu: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
rval = EINVAL;
} else if (*oid == ilist->item->oid.raw_oid) {
mplutpg = (mpapi_lu_data_t *)(ilist->item->idata);
if (mplutpg->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_list_for_"
"lu: OID NOT FOUND - LU IS OFFLINE"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mplu_tpg_list = mplutpg->tpg_list->head;
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_list_for_lu: "
"Unknown Error"));
}
while (mplu_tpg_list != NULL) {
if (count < list_len) {
oid_list[count] =
(uint64_t)mplu_tpg_list->item->oid.raw_oid;
} else {
rval = MP_MORE_DATA;
}
tpgd = mplu_tpg_list->item->idata;
if (tpgd->valid == 0) {
count--;
}
mplu_tpg_list = mplu_tpg_list->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_list_for_lu: "
"ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_lu_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t *oid = (uint64_t *)(input_data);
mp_logical_unit_prop_t *mplup_prop;
mpapi_item_list_t *ilist;
mpapi_lu_data_t *mplup;
mplup_prop = (mp_logical_unit_prop_t *)output_data;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
ilist = ilist->next;
}
if (ilist != NULL) {
mplup = (mpapi_lu_data_t *)(ilist->item->idata);
if (mplup == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_prop: "
"idata in ilist is NULL"));
return (EINVAL);
} else if (mplup->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_prop: "
"OID NOT FOUND - LU GONE OFFLINE"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mplup_prop = (mp_logical_unit_prop_t *)(&mplup->prop);
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_prop: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
if (ddi_copyout((void *)mplup_prop, mpioc->mp_obuf,
sizeof (mp_logical_unit_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_path_list_for_mp_lu(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
uint64_t *oid = (uint64_t *)(input_data);
mpapi_item_list_t *ilist, *mplu_path_list = NULL;
mpapi_lu_data_t *mplup;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
ilist = ilist->next;
if (ilist == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_mp_lu: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
rval = EINVAL;
} else if (*oid == ilist->item->oid.raw_oid) {
mplup = (mpapi_lu_data_t *)(ilist->item->idata);
if (mplup->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_"
"mp_lu: MP_DRVR_PATH_STATE_LU_ERR - LU OFFLINE"));
mpioc->mp_errno = MP_DRVR_PATH_STATE_LU_ERR;
return (EINVAL);
}
mplu_path_list = mplup->path_list->head;
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_mp_lu: "
"Unknown Error"));
}
while (mplu_path_list != NULL) {
if (count < list_len) {
oid_list[count] = (uint64_t)mplu_path_list->
item->oid.raw_oid;
} else {
rval = MP_MORE_DATA;
}
mplu_path_list = mplu_path_list->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_mp_lu: "
"ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_path_list_for_init_port(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
uint64_t *oid = (uint64_t *)(input_data);
mpapi_item_list_t *ilist, *mpinit_path_list = NULL;
mpapi_initiator_data_t *mpinitp;
ilist = vhci->mp_priv->
obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
/*
* While walking the mpapi database for initiator ports invalidate all
* initiator ports. The succeeding call to walk the phci list through
* MDI walker will validate the currently existing pHCIS.
*/
while (ilist != NULL) {
mpinitp = ilist->item->idata;
mpinitp->valid = 0;
ilist = ilist->next;
}
mdi_vhci_walk_phcis(vhci->vhci_dip, vhci_mpapi_sync_init_port_list,
vhci);
ilist = vhci->mp_priv->
obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
ilist = ilist->next;
if (ilist == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_init_"
"port: OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
rval = EINVAL;
} else if (*oid == ilist->item->oid.raw_oid) {
mpinitp = (mpapi_initiator_data_t *)(ilist->item->idata);
if (mpinitp->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_"
"init_port: OID NOT FOUND - INIT PORT INVALID"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mpinit_path_list = mpinitp->path_list->head;
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_init_"
"port: Unknown Error"));
}
while (mpinit_path_list != NULL) {
if (count < list_len) {
oid_list[count] = (uint64_t)mpinit_path_list->
item->oid.raw_oid;
} else {
rval = MP_MORE_DATA;
}
mpinit_path_list = mpinit_path_list->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_init_"
"port: ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_path_list_for_target_port(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
uint64_t *oid = (uint64_t *)(input_data);
mpapi_item_list_t *ilist, *mptp_path_list = NULL;
mpapi_tport_data_t *mptpp;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
ilist = ilist->next;
if (ilist == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_target_"
"port: OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
rval = EINVAL;
} else if (*oid == ilist->item->oid.raw_oid) {
mptpp = (mpapi_tport_data_t *)(ilist->item->idata);
if (mptpp->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_"
"target_port: OID NOT FOUND - TGT PORT INVALID"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mptp_path_list = mptpp->path_list->head;
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_target_"
"port: Unknown Error"));
}
while (mptp_path_list != NULL) {
if (count < list_len) {
oid_list[count] =
(uint64_t)mptp_path_list->item->oid.raw_oid;
} else {
rval = MP_MORE_DATA;
}
mptp_path_list = mptp_path_list->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_target_"
"port: ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_path_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t oid;
mp_path_prop_t *mpp_prop = (mp_path_prop_t *)output_data;
mpapi_item_list_t *ilist;
mpapi_path_data_t *mpp;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_PATH_LU]->head;
rval = ddi_copyin(mpioc->mp_ibuf, &oid, mpioc->mp_ilen, mode);
while ((ilist != NULL) && (oid != ilist->item->oid.raw_oid))
ilist = ilist->next;
if (ilist != NULL) {
mpp = (mpapi_path_data_t *)(ilist->item->idata);
if (mpp == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_prop: "
"idata in ilist is NULL"));
return (EINVAL);
}
mpp_prop = (mp_path_prop_t *)(&mpp->prop);
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_prop: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
if (ddi_copyout((void *)mpp_prop, mpioc->mp_obuf,
sizeof (mp_path_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_init_port_list(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
mpapi_item_list_t *ilist;
mpapi_initiator_data_t *initd;
ilist = vhci->mp_priv->
obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
/*
* While walking the mpapi database for initiator ports invalidate all
* initiator ports. The succeeding call to walk the phci list through
* MDI walker will validate the currently existing pHCIS.
*/
while (ilist != NULL) {
initd = ilist->item->idata;
initd->valid = 0;
ilist = ilist->next;
}
mdi_vhci_walk_phcis(vhci->vhci_dip, vhci_mpapi_sync_init_port_list,
vhci);
ilist = vhci->mp_priv->
obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
while (ilist != NULL) {
if (count < list_len) {
oid_list[count] = (uint64_t)ilist->item->oid.raw_oid;
} else {
rval = MP_MORE_DATA;
}
/*
* Get rid of the latest entry if item is invalid
*/
initd = ilist->item->idata;
if (initd->valid == 0) {
count--;
}
ilist = ilist->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_init_port_list: "
"ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
} else {
mpioc->mp_errno = 0;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_init_port_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t *oid = (uint64_t *)(input_data);
mp_init_port_prop_t *mpip_prop = (mp_init_port_prop_t *)output_data;
mpapi_item_list_t *ilist;
mpapi_initiator_data_t *mpip;
ilist = vhci->mp_priv->
obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
/*
* While walking the mpapi database for initiator ports invalidate all
* initiator ports. The succeeding call to walk the phci list through
* MDI walker will validate the currently existing pHCIS.
*/
while (ilist != NULL) {
mpip = ilist->item->idata;
mpip->valid = 0;
ilist = ilist->next;
}
mdi_vhci_walk_phcis(vhci->vhci_dip, vhci_mpapi_sync_init_port_list,
vhci);
ilist = vhci->mp_priv->
obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
ilist = ilist->next;
}
if (ilist != NULL) {
mpip = (mpapi_initiator_data_t *)(ilist->item->idata);
if (mpip == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_init_port_prop:"
" idata in ilist is NULL"));
return (EINVAL);
} else if (mpip->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_init_port_prop"
": OID NOT FOUND - INIT PORT IS INVALID"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mpip_prop = (mp_init_port_prop_t *)(&mpip->prop);
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_init_port_prop: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
if (ddi_copyout((void *)mpip_prop, mpioc->mp_obuf,
sizeof (mp_init_port_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_target_port_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t *oid = (uint64_t *)(input_data);
mp_target_port_prop_t *mptp_prop;
mpapi_item_list_t *ilist;
mpapi_tport_data_t *mptp;
mptp_prop = (mp_target_port_prop_t *)output_data;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
ilist = ilist->next;
}
if (ilist != NULL) {
mptp = (mpapi_tport_data_t *)(ilist->item->idata);
if (mptp == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_"
"prop: idata in ilist is NULL"));
return (EINVAL);
} else if (mptp->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_"
"prop: OID NOT FOUND - TARGET PORT INVALID"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mptp_prop = (mp_target_port_prop_t *)(&mptp->prop);
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_prop: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
if (ddi_copyout((void *)mptp_prop, mpioc->mp_obuf,
sizeof (mp_target_port_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_tpg_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t *oid = (uint64_t *)(input_data);
mp_tpg_prop_t *mptpg_prop;
mpapi_item_list_t *ilist;
mpapi_tpg_data_t *mptpg;
mptpg_prop = (mp_tpg_prop_t *)output_data;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT_GROUP]->
head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
ilist = ilist->next;
}
if (ilist != NULL) {
mptpg = (mpapi_tpg_data_t *)(ilist->item->idata);
if (mptpg == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_prop: "
"idata in ilist is NULL"));
return (EINVAL);
} else if (mptpg->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_prop: "
"OID NOT FOUND - TPG INVALID"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mptpg_prop = (mp_tpg_prop_t *)(&mptpg->prop);
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_prop: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
if (ddi_copyout((void *)mptpg_prop, mpioc->mp_obuf,
sizeof (mp_tpg_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_target_port_list_for_tpg(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int count = 0, rval = 0;
int list_len = mpioc->mp_olen/sizeof (uint64_t);
uint64_t *oid_list = (uint64_t *)(output_data);
uint64_t *oid = (uint64_t *)(input_data);
mpapi_item_list_t *ilist, *tpg_tp_list = NULL;
mpapi_tpg_data_t *mptpgtp;
mpapi_tport_data_t *mptpp;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT_GROUP]
->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
ilist = ilist->next;
if (ilist == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_list_for_"
"tpg: OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
rval = EINVAL;
} else if (*oid == ilist->item->oid.raw_oid) {
mptpgtp = (mpapi_tpg_data_t *)(ilist->item->idata);
if (mptpgtp->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_"
"list_for_tpg: OID NOT FOUND - TPG INVALID"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
tpg_tp_list = mptpgtp->tport_list->head;
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_list_for_"
"tpg: Unknown Error"));
}
while (tpg_tp_list != NULL) {
if (count < list_len) {
oid_list[count] = (uint64_t)tpg_tp_list->
item->oid.raw_oid;
} else {
rval = MP_MORE_DATA;
}
mptpp = tpg_tp_list->item->idata;
if (mptpp->valid == 0) {
count--;
}
tpg_tp_list = tpg_tp_list->next;
count++;
}
mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
mpioc->mp_errno = MP_MORE_DATA;
return (EINVAL);
}
if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(count * sizeof (uint64_t)), mode) != 0)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_list_for_"
"tpg: ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_set_tpg_access_state(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0, retval = 0, held = 0;
uint32_t desired_state, t10_tpgid;
uint64_t lu_oid, tpg_oid;
mp_set_tpg_state_req_t mp_set_tpg;
mpapi_item_list_t *lu_list, *tpg_list;
mpapi_tpg_data_t *mptpgd;
scsi_vhci_lun_t *svl;
scsi_vhci_priv_t *svp;
mdi_pathinfo_t *pip;
struct scsi_address *ap = NULL;
lu_list = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]
->head;
tpg_list = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT_GROUP]
->head;
rval = ddi_copyin(mpioc->mp_ibuf, &mp_set_tpg, mpioc->mp_ilen, mode);
lu_oid = mp_set_tpg.luTpgPair.luId;
tpg_oid = mp_set_tpg.luTpgPair.tpgId;
desired_state = mp_set_tpg.desiredState;
VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_set_tpg_access_state: lu_oid: %lx,"
"tpg_oid: %lx, des_as: %x\n", (long)lu_oid, (long)tpg_oid,
desired_state));
while ((lu_list != NULL) && (lu_oid != lu_list->item->oid.raw_oid))
lu_list = lu_list->next;
while ((tpg_list != NULL) && (tpg_oid != tpg_list->item->oid.raw_oid))
tpg_list = tpg_list->next;
if ((lu_list == NULL) || (tpg_list == NULL)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_state: "
"OID NOT FOUND"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
if ((desired_state != MP_DRVR_ACCESS_STATE_ACTIVE) &&
(desired_state != MP_DRVR_ACCESS_STATE_ACTIVE_OPTIMIZED) &&
(desired_state != MP_DRVR_ACCESS_STATE_ACTIVE_NONOPTIMIZED) &&
(desired_state != MP_DRVR_ACCESS_STATE_STANDBY)) {
mpioc->mp_errno = MP_DRVR_ILLEGAL_ACCESS_STATE_REQUEST;
return (EINVAL);
}
mptpgd = (mpapi_tpg_data_t *)(tpg_list->item->idata);
if (desired_state == mptpgd->prop.accessState) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
"state: TPG already in desired State"));
return (EINVAL);
}
t10_tpgid = mptpgd->prop.tpgId;
/*
* All input seems to be ok, Go ahead & change state.
*/
svl = ((mpapi_lu_data_t *)(lu_list->item->idata))->resp;
if (!SCSI_FAILOVER_IS_TPGS(svl->svl_fops)) {
VHCI_HOLD_LUN(svl, VH_SLEEP, held);
/*
* retval specifically cares about failover
* status and not about this routine's success.
*/
retval = mdi_failover(vhci->vhci_dip, svl->svl_dip,
MDI_FAILOVER_SYNC);
if (retval != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
"state: FAILOVER FAILED: %x", retval));
VHCI_RELEASE_LUN(svl);
return (EIO);
} else {
/*
* Don't set TPG's accessState here. Let mdi_failover's
* call-back routine "vhci_failover()" call
* vhci_mpapi_update_tpg_acc_state_for_lu().
*/
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
"state: FAILOVER SUCCESS: %x", retval));
}
VHCI_RELEASE_LUN(svl);
} else {
/*
* Send SET_TARGET_PORT_GROUP SCSI Command. This is supported
* ONLY by devices which have TPGS EXPLICIT Failover support.
*/
retval = mdi_select_path(svl->svl_dip, NULL,
MDI_SELECT_ONLINE_PATH, NULL, &pip);
if ((rval != MDI_SUCCESS) || (pip == NULL)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
"state: Unable to find path: %x", retval));
return (EINVAL);
}
svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
if (svp == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
"state: Unable to find vhci private data"));
mdi_rele_path(pip);
return (EINVAL);
}
if (svp->svp_psd == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
"state: Unable to find scsi device"));
mdi_rele_path(pip);
return (EINVAL);
}
mdi_rele_path(pip);
ap = &svp->svp_psd->sd_address;
ASSERT(ap != NULL);
retval = (*tpgs_set_target_groups)
(ap, desired_state, t10_tpgid);
if (retval != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
"state:(ALUA) FAILOVER FAILED: %x", retval));
return (EIO);
} else {
/*
* Don't set accessState here.
* std_report_target_groups() call needs to sync up
* properly.
*/
VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_set_tpg_access_"
"state:(ALUA) FAILOVER SUCCESS: %x", retval));
VHCI_HOLD_LUN(svl, VH_NOSLEEP, held);
if (!held) {
return (TRAN_BUSY);
} else {
vhci_update_pathstates((void *)svl);
}
if (desired_state != mptpgd->prop.accessState) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_"
"access_state: TPGAccessState NOT Set: "
"des_state=%x, cur_state=%x", desired_state,
mptpgd->prop.accessState));
return (EIO);
}
}
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_prop_lb_list(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t *oid_list = (uint64_t *)(output_data);
oid_list[0] = NULL;
if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
(sizeof (uint64_t)), mode) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_prop_lb_list: "
"ddi_copyout() FAILED"));
mpioc->mp_errno = EFAULT;
rval = EINVAL;
} else {
mpioc->mp_errno = 0;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_get_prop_lb_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = EINVAL;
return (rval);
}
/*
* Operation not supported currently as we do not know
* support any devices that allow this in the first place.
*/
/* ARGSUSED */
static int
vhci_assign_lu_to_tpg(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = ENOTSUP;
return (rval);
}
/* ARGSUSED */
static int
vhci_enable_auto_failback(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
mpapi_item_list_t *ilist;
mpapi_lu_data_t *lud;
mutex_enter(&vhci->vhci_mutex);
vhci->vhci_conf_flags |= VHCI_CONF_FLAGS_AUTO_FAILBACK;
mutex_exit(&vhci->vhci_mutex);
/* Enable auto-failback for each lun in MPAPI database */
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
while (ilist != NULL) {
lud = ilist->item->idata;
lud->prop.autoFailbackEnabled = 1;
ilist = ilist->next;
}
return (rval);
}
/* ARGSUSED */
static int
vhci_disable_auto_failback(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
mpapi_item_list_t *ilist;
mpapi_lu_data_t *lud;
mutex_enter(&vhci->vhci_mutex);
vhci->vhci_conf_flags &= ~VHCI_CONF_FLAGS_AUTO_FAILBACK;
mutex_exit(&vhci->vhci_mutex);
/* Disable auto-failback for each lun in MPAPI database */
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
while (ilist != NULL) {
lud = ilist->item->idata;
lud->prop.autoFailbackEnabled = 0;
ilist = ilist->next;
}
return (rval);
}
/*
* Find the oid in the object type list. If found lock and return
* the item. If not found return NULL. The caller must unlock the item.
*/
void *
vhci_mpapi_hold_item(struct scsi_vhci *vhci, uint64_t *oid, uint8_t obj_type)
{
mpapi_item_list_t *ilist;
ilist = vhci->mp_priv->obj_hdr_list[obj_type]->head;
while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
ilist = ilist->next;
if (ilist == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_hold_item: "
"OID NOT FOUND. oid: %p", (void *)oid));
return (NULL);
}
if (*oid == ilist->item->oid.raw_oid) {
mutex_enter(&ilist->item->item_mutex);
return (ilist);
}
VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_mpapi_hold_item: "
"Unknown Error. oid: %p", (void *)oid));
return (NULL);
}
/*
* Check that the pip sent in by the user is still associated with
* the same oid. This is done through checking the path name.
*/
mdi_pathinfo_t *
vhci_mpapi_chk_path(struct scsi_vhci *vhci, mpapi_item_list_t *ilist)
{
mdi_pathinfo_t *pip;
mpapi_path_data_t *mpp;
mpp = (mpapi_path_data_t *)(ilist->item->idata);
if (mpp == NULL || mpp->valid == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_chk_path: "
"pathinfo is not valid: %p", (void *)mpp));
return (NULL);
}
pip = mpp->resp;
/* make sure it is the same pip by checking path */
if (vhci_mpapi_match_pip(vhci, ilist, pip) == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_chk_path: "
"Can not match pip: %p", (void *)pip));
return (NULL);
}
return (pip);
}
/*
* Get the pip from the oid passed in. the vhci_mpapi_chk_path
* will check the name with the passed in pip name. the mdi_select_path()
* path will lock the pip and this should get released by the caller
*/
mdi_pathinfo_t *
vhci_mpapi_hold_pip(struct scsi_vhci *vhci, mpapi_item_list_t *ilist, int flags)
{
mdi_pathinfo_t *pip, *opip, *npip;
scsi_vhci_lun_t *svl;
int rval;
mpapi_path_data_t *mpp;
mpp = (mpapi_path_data_t *)(ilist->item->idata);
pip = mpp->resp;
/* make sure it is the same pip by checking path */
if (vhci_mpapi_chk_path(vhci, ilist) == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_hold_pip: "
"Can not match pip: %p", (void *)pip));
return (NULL);
}
svl = mdi_client_get_vhci_private(mdi_pi_get_client(pip));
opip = npip = NULL;
/*
* use the select path to find the right pip since
* it does all the state checking and locks the pip
*/
rval = mdi_select_path(svl->svl_dip, NULL,
flags, NULL, &npip);
do {
if ((rval != MDI_SUCCESS) || (npip == NULL)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_hold_pip:"
" Unable to find path: %x.", rval));
return (NULL);
}
if (npip == pip) {
break;
}
opip = npip;
rval = mdi_select_path(svl->svl_dip, NULL,
flags, opip, &npip);
mdi_rele_path(opip);
} while ((npip != NULL) && (rval == MDI_SUCCESS));
return (npip);
}
/*
* Initialize the uscsi command. Lock the pip and the item in
* the item list.
*/
static mp_uscsi_cmd_t *
vhci_init_uscsi_cmd(struct scsi_vhci *vhci,
mp_iocdata_t *mpioc, uint64_t *oid, mpapi_item_list_t **list)
{
int arq_enabled;
mp_uscsi_cmd_t *mp_uscmdp;
scsi_vhci_priv_t *svp;
struct scsi_address *ap;
mdi_pathinfo_t *pip;
mpapi_item_list_t *ilist;
struct buf *bp;
VHCI_DEBUG(4, (CE_WARN, NULL,
"vhci_init_uscsi_cmd: enter"));
*list = NULL;
/* lock the item */
if ((ilist = (mpapi_item_list_t *)vhci_mpapi_hold_item(
vhci, oid, MP_OBJECT_TYPE_PATH_LU)) == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL,
"vhci_init_uscsi_cmd: exit EINVAL"));
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (NULL);
}
/* lock the pip */
if ((pip = vhci_mpapi_hold_pip(vhci, ilist,
(MDI_SELECT_STANDBY_PATH|MDI_SELECT_ONLINE_PATH))) == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL,
"vhci_init_uscsi_cmd: exit PATH_UNAVAIL"));
mpioc->mp_errno = MP_DRVR_PATH_UNAVAILABLE;
mutex_exit(&ilist->item->item_mutex);
return (NULL);
};
/* get the address of the pip */
svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
if (svp == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_init_uscsi_cmd:"
" Unable to find vhci private data"));
mpioc->mp_errno = MP_DRVR_PATH_UNAVAILABLE;
mdi_rele_path(pip);
mutex_exit(&ilist->item->item_mutex);
return (NULL);
}
if (svp->svp_psd == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_init_uscsi_cmd:"
" Unable to find scsi device"));
mpioc->mp_errno = MP_DRVR_PATH_UNAVAILABLE;
mdi_rele_path(pip);
mutex_exit(&ilist->item->item_mutex);
return (NULL);
}
ap = &svp->svp_psd->sd_address;
ASSERT(ap != NULL);
/* initialize the buffer */
bp = getrbuf(KM_SLEEP);
ASSERT(bp != NULL);
/* initialize the mp_uscsi_cmd */
mp_uscmdp = kmem_zalloc((size_t)sizeof (mp_uscsi_cmd_t), KM_SLEEP);
ASSERT(mp_uscmdp != NULL);
mp_uscmdp->ap = ap;
mp_uscmdp->pip = pip;
mp_uscmdp->cmdbp = bp;
mp_uscmdp->rqbp = NULL;
bp->b_private = mp_uscmdp;
/* used to debug a manual sense */
if (vhci_force_manual_sense) {
(void) scsi_ifsetcap(ap, "auto-rqsense", 0, 0);
} else {
if (scsi_ifgetcap(ap, "auto-rqsense", 1) != 1) {
(void) scsi_ifsetcap(ap, "auto-rqsense", 1, 1);
}
}
arq_enabled = scsi_ifgetcap(ap, "auto-rqsense", 1);
if (arq_enabled == 1) {
mp_uscmdp->arq_enabled = 1;
} else {
mp_uscmdp->arq_enabled = 0;
}
/* set the list pointer for the caller */
*list = ilist;
VHCI_DEBUG(4, (CE_WARN, NULL,
"vhci_init_uscsi_cmd: mp_uscmdp: %p ilist: %p mp_errno: %d "
"bp: %p arq: %d",
(void *)mp_uscmdp, (void *)*list, mpioc->mp_errno,
(void *)bp, arq_enabled));
return (mp_uscmdp);
}
/*
* Initialize the uscsi information and then issue the command.
*/
/* ARGSUSED */
static int
vhci_send_uscsi_cmd(dev_t dev, struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0, uioseg = 0;
struct uscsi_cmd *uscmdp;
uint64_t *oid = (uint64_t *)(input_data);
mp_uscsi_cmd_t *mp_uscmdp;
mpapi_item_list_t *ilist;
VHCI_DEBUG(4, (CE_WARN, NULL,
"vhci_send_uscsi_cmd: enter: mode: %x", mode));
mpioc->mp_errno = 0;
mp_uscmdp = vhci_init_uscsi_cmd(vhci, mpioc, oid, &ilist);
if (mp_uscmdp == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL,
"vhci_send_uscsi_cmd: exit INVALID_ID. rval: %d", rval));
return (EINVAL);
}
rval = scsi_uscsi_alloc_and_copyin((intptr_t)mpioc->mp_obuf,
mode, mp_uscmdp->ap, &uscmdp);
if (rval != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_send_uscsi_cmd: "
"scsi_uscsi_alloc_and_copyin failed. rval: %d", rval));
mpioc->mp_errno = EINVAL;
mdi_rele_path(mp_uscmdp->pip);
mutex_exit(&ilist->item->item_mutex);
if (mp_uscmdp->cmdbp)
freerbuf(mp_uscmdp->cmdbp);
kmem_free(mp_uscmdp, sizeof (mp_uscsi_cmd_t));
return (EINVAL);
}
/* initialize the mp_uscsi_cmd with the uscsi_cmd from uscsi_alloc */
mp_uscmdp->uscmdp = uscmdp;
uioseg = (mode & FKIOCTL) ? UIO_SYSSPACE : UIO_USERSPACE;
/* start the command sending the buffer as an argument */
rval = scsi_uscsi_handle_cmd(dev, uioseg,
uscmdp, vhci_uscsi_iostart, mp_uscmdp->cmdbp, mp_uscmdp);
if (rval != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_send_uscsi_cmd: "
"scsi_uscsi_handle_cmd failed. rval: %d", rval));
mpioc->mp_errno = EIO;
}
if (scsi_uscsi_copyout_and_free((intptr_t)mpioc->mp_obuf,
uscmdp) != 0 && rval == 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_send_uscsi_cmd: "
"scsi_uscsi_copyout_and_free failed. rval: %d", rval));
mpioc->mp_errno = EFAULT;
rval = EFAULT;
}
/* cleanup */
mdi_rele_path(mp_uscmdp->pip);
mutex_exit(&ilist->item->item_mutex);
if (mp_uscmdp->cmdbp)
freerbuf(mp_uscmdp->cmdbp);
kmem_free(mp_uscmdp, sizeof (mp_uscsi_cmd_t));
VHCI_DEBUG(4, (CE_WARN, NULL,
"vhci_send_uscsi_cmd: rval: %d mp_errno: %d",
rval, mpioc->mp_errno));
return (rval);
}
/* ARGSUSED */
static int
vhci_enable_path(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t *oid = (uint64_t *)(input_data);
mdi_pathinfo_t *pip;
mpapi_item_list_t *ilist;
mpapi_path_data_t *mpp;
if ((ilist = (mpapi_item_list_t *)vhci_mpapi_hold_item(vhci, oid,
MP_OBJECT_TYPE_PATH_LU)) == NULL) {
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mpp = (mpapi_path_data_t *)(ilist->item->idata);
pip = (mdi_pathinfo_t *)mpp->resp;
if (vhci_mpapi_chk_path(vhci, ilist) == NULL) {
mutex_exit(&ilist->item->item_mutex);
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
if (mdi_pi_enable_path(pip, USER_DISABLE) != 0) {
rval = EFAULT;
} else {
mpp->prop.disabled = 0;
vhci_mpapi_log_sysevent(vhci->vhci_dip,
&(((mpoid_t *)oid)->raw_oid), ESC_SUN_MP_PATH_CHANGE);
}
mutex_exit(&ilist->item->item_mutex);
return (rval);
}
/* ARGSUSED */
static int
vhci_disable_path(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
void *input_data, void *output_data, int mode)
{
int rval = 0;
uint64_t *oid = (uint64_t *)(input_data);
mdi_pathinfo_t *pip = NULL;
mpapi_item_list_t *ilist;
mpapi_path_data_t *mpp;
if ((ilist = (mpapi_item_list_t *)vhci_mpapi_hold_item(vhci, oid,
MP_OBJECT_TYPE_PATH_LU)) == NULL) {
mpioc->mp_errno = MP_DRVR_INVALID_ID;
return (EINVAL);
}
mpp = (mpapi_path_data_t *)(ilist->item->idata);
pip = (mdi_pathinfo_t *)mpp->resp;
if (vhci_mpapi_chk_path(vhci, ilist) == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_disable_path: Request "
"received to disable last path. Cant disable, Sorry!"));
mutex_exit(&ilist->item->item_mutex);
return (EINVAL);
}
if (vhci_mpapi_chk_last_path(pip) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_disable_path(1): Request "
"received to disable last path. Cant disable, Sorry!"));
mutex_exit(&ilist->item->item_mutex);
return (EINVAL);
}
if (mdi_pi_disable_path(pip, USER_DISABLE) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_disable_path(2): Request "
"received to disable last path. Cant disable, Sorry!"));
rval = EFAULT;
} else {
mpp->prop.disabled = 1;
vhci_mpapi_log_sysevent(vhci->vhci_dip,
&(((mpoid_t *)oid)->raw_oid), ESC_SUN_MP_PATH_CHANGE);
}
mutex_exit(&ilist->item->item_mutex);
return (rval);
}
/* ARGSUSED */
static int
vhci_mpapi_ioctl(dev_t dev, struct scsi_vhci *vhci, void *udata,
mp_iocdata_t *mpioc, int mode, cred_t *credp)
{
int rval = 0;
uint64_t oid;
void *input_data = NULL, *output_data = NULL;
/* validate mpioc */
rval = vhci_mpapi_validate(udata, mpioc, mode, credp);
if (rval == EINVAL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
" vhci_mpapi_validate() Returned %x: INVALID DATA", rval));
if (vhci_mpapi_copyout_iocdata(mpioc, udata, mode)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
"vhci_mpapi_copyout_iocdata FAILED in EINVAL"));
}
return (rval);
} else if (rval == EPERM) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
" vhci_mpapi_validate() Returned %x: NO CREDS", rval));
if (vhci_mpapi_copyout_iocdata(mpioc, udata, mode)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
"vhci_mpapi_copyout_iocdata FAILED in EPERM"));
}
return (rval);
/* Process good cases & also cases where we need to get correct alen */
} else if ((rval == 0) || (rval == MP_MORE_DATA)) {
/* allocate an input buffer */
if ((mpioc->mp_ibuf) && (mpioc->mp_ilen != 0)) {
input_data = kmem_zalloc(mpioc->mp_ilen,
KM_SLEEP);
ASSERT(input_data != NULL);
rval = ddi_copyin(mpioc->mp_ibuf,
input_data, mpioc->mp_ilen, mode);
oid = (uint64_t)(*((uint64_t *)input_data));
VHCI_DEBUG(7, (CE_NOTE, NULL, "Requesting op for "
"OID = %lx w/ mpioc = %p mp_cmd = %x\n",
(long)oid, (void *)mpioc, mpioc->mp_cmd));
}
if ((mpioc->mp_xfer == MP_XFER_READ) && (mpioc->mp_olen != 0)) {
output_data = kmem_zalloc(mpioc->mp_olen, KM_SLEEP);
ASSERT(output_data != NULL);
}
}
if (vhci_mpapi_sync_lu_oid_list(vhci) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_mpapi_ioctl: "
"vhci_mpapi_sync_lu_oid_list() failed"));
}
mdi_vhci_walk_phcis(vhci->vhci_dip,
vhci_mpapi_sync_init_port_list, vhci);
/* process ioctls */
switch (mpioc->mp_cmd) {
case MP_GET_DRIVER_PROP:
rval = vhci_get_driver_prop(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_DEV_PROD_LIST:
rval = vhci_get_dev_prod_list(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_DEV_PROD_PROP:
rval = vhci_get_dev_prod_prop(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_LU_LIST:
rval = vhci_get_lu_list(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_LU_LIST_FROM_TPG:
rval = vhci_get_lu_list_from_tpg(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_TPG_LIST_FOR_LU:
rval = vhci_get_tpg_list_for_lu(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_LU_PROP:
rval = vhci_get_lu_prop(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_PATH_LIST_FOR_MP_LU:
rval = vhci_get_path_list_for_mp_lu(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_PATH_LIST_FOR_INIT_PORT:
rval = vhci_get_path_list_for_init_port(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_PATH_LIST_FOR_TARGET_PORT:
rval = vhci_get_path_list_for_target_port(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_PATH_PROP:
rval = vhci_get_path_prop(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_INIT_PORT_LIST: /* Not Required */
rval = vhci_get_init_port_list(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_INIT_PORT_PROP:
rval = vhci_get_init_port_prop(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_TARGET_PORT_PROP:
rval = vhci_get_target_port_prop(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_TPG_LIST: /* Not Required */
rval = vhci_get_tpg_list_for_lu(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_TPG_PROP:
rval = vhci_get_tpg_prop(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_TARGET_PORT_LIST_FOR_TPG:
rval = vhci_get_target_port_list_for_tpg(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_SET_TPG_ACCESS_STATE:
rval = vhci_set_tpg_access_state(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_ASSIGN_LU_TO_TPG:
rval = vhci_assign_lu_to_tpg(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_PROPRIETARY_LOADBALANCE_LIST:
rval = vhci_get_prop_lb_list(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_GET_PROPRIETARY_LOADBALANCE_PROP:
rval = vhci_get_prop_lb_prop(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_ENABLE_AUTO_FAILBACK:
rval = vhci_enable_auto_failback(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_DISABLE_AUTO_FAILBACK:
rval = vhci_disable_auto_failback(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_ENABLE_PATH:
rval = vhci_enable_path(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_DISABLE_PATH:
rval = vhci_disable_path(vhci, mpioc,
input_data, output_data, mode);
break;
case MP_SEND_SCSI_CMD:
rval = vhci_send_uscsi_cmd(dev, vhci, mpioc,
input_data, output_data, mode);
break;
default:
rval = EINVAL;
break;
}
VHCI_DEBUG(6, (CE_NOTE, NULL, "vhci_mpapi_ioctl: output_data = %p, "
"mp_obuf = %p, mp_olen = %lx, mp_alen = %lx, mp_errno = %x, "
"mode = %x, rval=%x\n", (void *)output_data, (void *)mpioc->mp_obuf,
mpioc->mp_olen, mpioc->mp_alen, mpioc->mp_errno, mode, rval));
if (vhci_mpapi_copyout_iocdata(mpioc, udata, mode)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
"vhci_mpapi_copyout_iocdata FAILED"));
rval = EFAULT;
}
if (input_data) {
kmem_free(input_data, mpioc->mp_ilen);
}
if (output_data) {
kmem_free(output_data, mpioc->mp_olen);
}
return (rval);
}
/* ARGSUSED */
int
vhci_mpapi_init(struct scsi_vhci *vhci)
{
mpapi_item_list_t *ilist;
mpapi_item_t *item;
mp_driver_prop_t *drv;
uint8_t i;
/*
* This tstamp value is present in the upper 32-bits of all OIDs
* that are issued in this boot session. Use it to identify
* stale OIDs that an application/ioctl may pass to you and
* reject it - Done in vhci_mpapi_validate() routine.
*/
mutex_enter(&tod_lock);
vhci->mp_priv->tstamp = (time32_t)(tod_get().tv_sec);
mutex_exit(&tod_lock);
for (i = 0; i < MP_MAX_OBJECT_TYPE; i++) {
vhci->mp_priv->obj_hdr_list[i] = vhci_mpapi_create_list_head();
}
/*
* Let us now allocate and initialize the drv block.
*/
ilist = kmem_zalloc(sizeof (mpapi_item_list_t), KM_SLEEP);
item = kmem_zalloc(sizeof (mpapi_item_t), KM_SLEEP);
ilist->item = item;
item->oid.raw_oid = vhci_mpapi_create_oid(vhci->mp_priv,
MP_OBJECT_TYPE_PLUGIN);
drv = kmem_zalloc(sizeof (mp_driver_prop_t), KM_SLEEP);
drv->driverVersion[0] = '\0';
drv->supportedLoadBalanceTypes =
(MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN |
MP_DRVR_LOAD_BALANCE_TYPE_LBA_REGION);
drv->canSetTPGAccess = TRUE;
drv->canOverridePaths = FALSE;
drv->exposesPathDeviceFiles = FALSE;
drv->deviceFileNamespace[0] = '\0';
drv->onlySupportsSpecifiedProducts = 1;
drv->maximumWeight = 1;
drv->failbackPollingRateMax = 0;
drv->currentFailbackPollingRate = 0;
drv->autoFailbackSupport = 1;
drv->autoFailbackEnabled = 1;
drv->defaultLoadBalanceType = MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN;
drv->probingPollingRateMax = 0;
drv->currentProbingPollingRate = 0;
drv->autoProbingSupport = 0;
drv->autoProbingEnabled = 0;
item->idata = drv;
mutex_init(&item->item_mutex, NULL, MUTEX_DRIVER, NULL);
if (vhci_mpapi_add_to_list(vhci->mp_priv->obj_hdr_list
[MP_OBJECT_TYPE_PLUGIN], ilist) != 0) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_init: "
"vhci_mpapi_create_add_to_list() of PLUGIN failed"));
return (EFAULT);
}
return (0);
}
void
vhci_mpapi_add_dev_prod(struct scsi_vhci *vhci, char *vidpid)
{
mpapi_item_list_t *dev_prod_list;
mpapi_item_t *dev_prod_item;
mp_dev_prod_prop_t *dev_prod;
/* add to list */
dev_prod_list = kmem_zalloc(sizeof (mpapi_item_list_t), KM_SLEEP);
dev_prod_item = kmem_zalloc(sizeof (mpapi_item_t), KM_SLEEP);
dev_prod_list->item = dev_prod_item;
dev_prod_list->item->oid.raw_oid = vhci_mpapi_create_oid
(vhci->mp_priv, MP_OBJECT_TYPE_DEVICE_PRODUCT);
dev_prod = kmem_zalloc(sizeof (mp_dev_prod_prop_t), KM_SLEEP);
(void) strncpy(dev_prod->prodInfo.vendor, vidpid, strlen(vidpid));
dev_prod->supportedLoadBalanceTypes =
MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN;
dev_prod->id = dev_prod_list->item->oid.raw_oid;
dev_prod_list->item->idata = dev_prod;
(void) vhci_mpapi_add_to_list(vhci->mp_priv->obj_hdr_list
[MP_OBJECT_TYPE_DEVICE_PRODUCT], (void *)dev_prod_list);
vhci_mpapi_log_sysevent(vhci->vhci_dip,
&(dev_prod_list->item->oid.raw_oid),
ESC_SUN_MP_DEV_PROD_ADD);
}
/* ARGSUSED */
static uint64_t
vhci_mpapi_create_oid(mpapi_priv_t *mp_priv, uint8_t obj_type)
{
mpoid_t oid;
oid.disc_oid.tstamp = mp_priv->tstamp;
oid.disc_oid.type = obj_type;
oid.disc_oid.seq_id = ++(mp_priv->oid_seq[obj_type]);
return (oid.raw_oid);
}
/* ARGSUSED */
static int
vhci_mpapi_add_to_list(mpapi_list_header_t *hdr, mpapi_item_list_t *item)
{
mpapi_list_header_t *tmp_hdr = hdr;
mpapi_item_list_t *tmp_item = item;
if (item == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_add_to_list: "
"NULL item passed"));
return (EFAULT);
}
if (hdr == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_add_to_list: "
"NULL hdr passed"));
return (EFAULT);
}
/*
* Check if the item is already there in the list.
* Catches duplicates while assigning TPGs.
*/
tmp_item = tmp_hdr->head;
while (tmp_item != NULL) {
if (item == tmp_item) {
VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_mpapi_add_to_list: "
"Item already in list"));
return (1);
} else {
tmp_item = tmp_item->next;
}
}
item->next = NULL;
if (hdr->head == NULL) {
hdr->head = item;
hdr->tail = item;
} else {
hdr->tail->next = item;
hdr->tail = item;
}
return (0);
}
/*
* Local convenience routine to fetch reference to a mpapi item entry if it
* exits based on the pointer to the vhci resource that is passed.
* Returns NULL if no entry is found.
*/
/* ARGSUSED */
void*
vhci_get_mpapi_item(struct scsi_vhci *vhci, mpapi_list_header_t *list,
uint8_t obj_type, void* res)
{
mpapi_item_list_t *ilist;
if (list == NULL) {
/*
* Since the listhead is null, the search is being
* performed in implicit mode - that is to use the
* level one list.
*/
ilist = vhci->mp_priv->obj_hdr_list[obj_type]->head;
} else {
/*
* The search is being performed on a sublist within
* one of the toplevel list items. Use the listhead
* that is passed in.
*/
ilist = list->head;
}
if (res == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_mpapi_item: "
" Got Item w/ NULL resource ptr"));
return (NULL);
}
/*
* Since the resource field within the item data is specific
* to a particular object type, we need to use the object type
* to enable us to perform the search and compare appropriately.
*/
switch (obj_type) {
case MP_OBJECT_TYPE_INITIATOR_PORT:
while (ilist) {
void *wwn = ((mpapi_initiator_data_t *)
ilist->item->idata)->resp;
if (strncmp(wwn, res, strlen(res)) == 0) {
/* Found a match */
return ((void*)ilist);
}
ilist = ilist->next;
}
break;
case MP_OBJECT_TYPE_TARGET_PORT:
while (ilist) {
void *wwn = ((mpapi_tport_data_t *)ilist->
item->idata)->resp;
if (strncmp(wwn, res, strlen(res)) == 0) {
/* Found a match */
return ((void*)ilist);
}
ilist = ilist->next;
}
break;
case MP_OBJECT_TYPE_TARGET_PORT_GROUP:
/*
* For TPG Synthesis, Use TPG specific routines
* Use this case only for ALUA devices which give TPG ID
*/
while (ilist) {
void *tpg_id = ((mpapi_tpg_data_t *)ilist->
item->idata)->resp;
if (strncmp(tpg_id, res, strlen(res)) == 0) {
/* Found a match */
return ((void*)ilist);
}
ilist = ilist->next;
}
break;
case MP_OBJECT_TYPE_MULTIPATH_LU:
return ((void *)(vhci_mpapi_match_lu
(vhci, ilist, res)));
case MP_OBJECT_TYPE_PATH_LU:
return ((void *)(vhci_mpapi_match_pip
(vhci, ilist, res)));
default:
/*
* This should not happen
*/
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_mpapi_item:"
"Got Unsupported OBJECT TYPE"));
return (NULL);
}
return (NULL);
}
/*
* Local convenience routine to create and initialize mpapi item
* based on the object type passed.
*/
/* ARGSUSED */
static mpapi_item_list_t *
vhci_mpapi_create_item(struct scsi_vhci *vhci, uint8_t obj_type, void* res)
{
int major;
int instance;
mpapi_item_list_t *ilist;
mpapi_item_t *item;
char *pname = NULL;
ilist = kmem_zalloc(sizeof (mpapi_item_list_t), KM_SLEEP);
item = kmem_zalloc(sizeof (mpapi_item_t), KM_SLEEP);
mutex_init(&item->item_mutex, NULL, MUTEX_DRIVER, NULL);
ilist->item = item;
item->oid.raw_oid = 0;
switch (obj_type) {
case MP_OBJECT_TYPE_INITIATOR_PORT:
{
mpapi_initiator_data_t *init;
dev_info_t *pdip = res;
char *init_port_res;
char *interconnect;
int mp_interconnect_type, len;
int prop_not_ddi_alloced = 0;
pname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
major = (int)ddi_driver_major(pdip);
instance = ddi_get_instance(pdip);
(void) ddi_pathname(pdip, pname);
item->oid.raw_oid =
MP_STORE_INST_TO_ID(instance, item->oid.raw_oid);
item->oid.raw_oid =
MP_STORE_MAJOR_TO_ID(major, item->oid.raw_oid);
/*
* Just make a call to keep correct Sequence count.
* Don't use the OID returned though.
*/
(void) vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
init_port_res = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) strlcpy(init_port_res, pname, MAXPATHLEN);
if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, 0,
"initiator-interconnect-type",
&interconnect) != DDI_PROP_SUCCESS)) {
/* XXX: initiator-interconnect-type not set */
VHCI_DEBUG(1, (CE_WARN, NULL,
"vhci_mpapi_create_item: initiator-"
"-interconnect-type prop not found"));
len = strlen("UNKNOWN")+1;
interconnect = kmem_zalloc(len, KM_SLEEP);
(void) strlcpy(interconnect, "UNKNOWN", len);
prop_not_ddi_alloced = 1;
}
/*
* Map the initiator-interconnect-type values between
* SCSA(as defined in services.h) and MPAPI
* (as defined in mpapi_impl.h)
*/
if (strncmp(interconnect,
INTERCONNECT_FABRIC_STR,
strlen(interconnect)) == 0) {
mp_interconnect_type = 2;
} else if (strncmp(interconnect,
INTERCONNECT_PARALLEL_STR,
strlen(interconnect)) == 0) {
mp_interconnect_type = 3;
} else if (strncmp(interconnect,
INTERCONNECT_ISCSI_STR,
strlen(interconnect)) == 0) {
mp_interconnect_type = 4;
} else if (strncmp(interconnect,
INTERCONNECT_IBSRP_STR,
strlen(interconnect)) == 0) {
mp_interconnect_type = 5;
} else {
mp_interconnect_type = 0;
}
init = kmem_zalloc(
sizeof (mpapi_initiator_data_t), KM_SLEEP);
init->resp = init_port_res;
init->valid = 1;
init->prop.id = item->oid.raw_oid;
init->prop.portType = mp_interconnect_type;
(void) strlcpy(init->prop.portID, pname,
sizeof (init->prop.portID));
(void) strlcpy(init->prop.osDeviceFile, "/devices",
sizeof (init->prop.osDeviceFile));
(void) strlcat(init->prop.osDeviceFile, pname,
sizeof (init->prop.osDeviceFile));
init->path_list = vhci_mpapi_create_list_head();
item->idata = (void *)init;
vhci_mpapi_log_sysevent(vhci->vhci_dip,
&(item->oid.raw_oid), ESC_SUN_MP_INIT_PORT_CHANGE);
if (prop_not_ddi_alloced != 1) {
ddi_prop_free(interconnect);
} else {
kmem_free(interconnect, len);
}
if (pname) {
kmem_free(pname, MAXPATHLEN);
}
}
break;
case MP_OBJECT_TYPE_TARGET_PORT:
{
mpapi_tport_data_t *tport;
char *tgt_port_res;
item->oid.raw_oid =
vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
tport = kmem_zalloc(sizeof (mpapi_tport_data_t),
KM_SLEEP);
tgt_port_res = kmem_zalloc(strlen(res)+1, KM_SLEEP);
(void) strlcpy(tgt_port_res, res, strlen(res)+1);
tport->resp = tgt_port_res;
tport->valid = 1;
tport->prop.id = item->oid.raw_oid;
tport->prop.relativePortID = 0;
(void) strlcpy(tport->prop.portName, res,
sizeof (tport->prop.portName));
tport->path_list = vhci_mpapi_create_list_head();
item->idata = (void *)tport;
vhci_mpapi_log_sysevent(vhci->vhci_dip,
&(item->oid.raw_oid), ESC_SUN_MP_TARGET_PORT_ADD);
}
break;
case MP_OBJECT_TYPE_TARGET_PORT_GROUP:
{
mpapi_tpg_data_t *tpg;
char *tpg_res;
item->oid.raw_oid =
vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
tpg = kmem_zalloc(
sizeof (mpapi_tpg_data_t), KM_SLEEP);
tpg_res = kmem_zalloc(strlen(res)+1, KM_SLEEP);
(void) strlcpy(tpg_res, res, strlen(res)+1);
tpg->resp = tpg_res;
tpg->valid = 1;
tpg->prop.id = item->oid.raw_oid;
/*
* T10 TPG ID is a 2 byte value. Keep up with it.
*/
tpg->prop.tpgId =
((item->oid.raw_oid) & 0x000000000000ffff);
tpg->tport_list = vhci_mpapi_create_list_head();
tpg->lu_list = vhci_mpapi_create_list_head();
item->idata = (void *)tpg;
vhci_mpapi_log_sysevent(vhci->vhci_dip,
&(item->oid.raw_oid), ESC_SUN_MP_TPG_ADD);
}
break;
case MP_OBJECT_TYPE_MULTIPATH_LU:
{
mpapi_lu_data_t *lu;
scsi_vhci_lun_t *svl = res;
/*
* We cant use ddi_get_instance(svl->svl_dip) at this
* point because the dip is not yet in DS_READY state.
*/
item->oid.raw_oid =
vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
lu = kmem_zalloc(sizeof (mpapi_lu_data_t), KM_SLEEP);
lu->resp = res;
lu->valid = 1;
lu->prop.id = (uint64_t)item->oid.raw_oid;
/*
* XXX: luGroupID is currently unsupported
*/
lu->prop.luGroupID = 0xFFFFFFFF;
(void) strlcpy(lu->prop.name, svl->svl_lun_wwn,
sizeof (lu->prop.name));
(void) strlcpy(lu->prop.deviceFileName,
"/devices/scsi_vhci/ssd@g",
sizeof (lu->prop.deviceFileName));
(void) strlcat(lu->prop.deviceFileName, lu->prop.name,
sizeof (lu->prop.deviceFileName));
if ((svl->svl_fops != NULL) &&
!SCSI_FAILOVER_IS_SYM(svl->svl_fops)) {
lu->prop.asymmetric = 1;
}
lu->prop.autoFailbackEnabled =
((VHCI_CONF_FLAGS_AUTO_FAILBACK & vhci->
vhci_conf_flags) ? 1 : 0);
if (svl->svl_lb_policy_save == LOAD_BALANCE_NONE) {
lu->prop.currentLoadBalanceType =
MP_DRVR_LOAD_BALANCE_TYPE_NONE;
} else if (svl->svl_lb_policy_save == LOAD_BALANCE_RR) {
lu->prop.currentLoadBalanceType =
MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN;
} else if (svl->svl_lb_policy_save ==
LOAD_BALANCE_LBA) {
lu->prop.currentLoadBalanceType =
MP_DRVR_LOAD_BALANCE_TYPE_LBA_REGION;
} else {
/*
* We still map Load Balance Type to UNKNOWN
* although "none" also maps to the same case.
* MPAPI spec does not have a "NONE" LB type.
*/
lu->prop.currentLoadBalanceType =
MP_DRVR_LOAD_BALANCE_TYPE_UNKNOWN;
}
/*
* Allocate header lists for cross reference
*/
lu->path_list = vhci_mpapi_create_list_head();
lu->tpg_list = vhci_mpapi_create_list_head();
item->idata = (void *)lu;
vhci_mpapi_log_sysevent(vhci->vhci_dip,
&(item->oid.raw_oid), ESC_SUN_MP_LU_CHANGE);
}
break;
case MP_OBJECT_TYPE_PATH_LU:
{
mpapi_path_data_t *path;
mdi_pathinfo_t *pip = res;
scsi_vhci_lun_t *svl;
char *iport, *tport;
item->oid.raw_oid =
vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
path = kmem_zalloc(
sizeof (mpapi_path_data_t), KM_SLEEP);
pname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
iport = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) ddi_pathname(mdi_pi_get_phci(pip), iport);
if (mdi_prop_lookup_string(pip, "target-port",
&tport) != DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
tport = (char *)mdi_pi_get_addr(pip);
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_"
"create_item: mdi_prop_lookup_string() "
"returned failure; "));
}
svl = mdi_client_get_vhci_private
(mdi_pi_get_client(pip));
(void) strlcat(pname, iport, MAXPATHLEN);
(void) strlcat(pname, tport, MAXPATHLEN);
(void) strlcat(pname, svl->svl_lun_wwn, MAXPATHLEN);
kmem_free(iport, MAXPATHLEN);
path->resp = res;
path->path_name = pname;
path->valid = 1;
path->prop.id = item->oid.raw_oid;
item->idata = (void *)path;
vhci_mpapi_log_sysevent(vhci->vhci_dip,
&(item->oid.raw_oid), ESC_SUN_MP_PATH_ADD);
}
break;
case MP_OBJECT_TYPE_DEVICE_PRODUCT:
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_create_item:"
" DEVICE PRODUCT not handled here."));
break;
default:
/*
* This should not happen
*/
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_create_item:"
"Got Unsupported OBJECT TYPE"));
return (NULL);
}
(void) vhci_mpapi_add_to_list(vhci->mp_priv->obj_hdr_list[obj_type],
ilist);
return (ilist);
}
/*
* Local routine to allocate mpapi list header block
*/
/* ARGSUSED */
static mpapi_list_header_t *
vhci_mpapi_create_list_head()
{
mpapi_list_header_t *lh;
lh = kmem_zalloc(sizeof (mpapi_list_header_t), KM_SLEEP);
lh->head = lh->tail = NULL;
return (lh);
}
/*
* Routine to create Level 1 mpapi_private data structure and also
* establish cross references between the resources being managed
*/
/* ARGSUSED */
void
vhci_update_mpapi_data(struct scsi_vhci *vhci, scsi_vhci_lun_t *vlun,
mdi_pathinfo_t *pip)
{
char *tmp_wwn = NULL, *init = NULL, *path_class;
dev_info_t *pdip;
mpapi_item_list_t *lu_list, *path_list, *init_list, *tgt_list;
mpapi_item_list_t *tp_path_list, *init_path_list, *lu_path_list;
mpapi_lu_data_t *ld;
mpapi_path_data_t *pd;
mpapi_tport_data_t *tpd;
mpapi_initiator_data_t *initd;
int path_class_not_mdi_alloced = 0;
VHCI_DEBUG(6, (CE_NOTE, NULL, "vhci_update_mpapi_data: vhci: %p, "
"vlun: %p, pip: %p\n", (void *)vhci, (void *)vlun, (void *)pip));
/*
* Check that the lun is not a TPGS device
* TPGS devices create the same information in another routine.
*/
if (SCSI_FAILOVER_IS_TPGS(vlun->svl_fops)) {
return;
}
/*
* LEVEL 1 - Actions:
* Check if the appropriate resource pointers already
* exist in the Level 1 list and add them if they are new.
*/
/*
* Build MP LU list
*/
lu_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
if (lu_list == NULL) {
/* Need to create lu_list entry */
lu_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
} else {
/*
* Matched this lu w/ an existing one in current lu list.
* SAME LUN came online!! So, update the resp in main list.
*/
ld = lu_list->item->idata;
ld->valid = 1;
ld->resp = vlun;
}
/*
* Find out the "path-class" property on the pip
*/
if (mdi_prop_lookup_string(pip, "path-class", &path_class)
!= DDI_PROP_SUCCESS) {
/* XXX: path-class prop not found */
path_class = kmem_zalloc(MPAPI_SCSI_MAXPCLASSLEN, KM_SLEEP);
(void) strlcpy(path_class, "NONE", MPAPI_SCSI_MAXPCLASSLEN);
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_update_mpapi_data: "
"mdi_prop_lookup_string() returned failure; "
"Hence path_class = NONE"));
path_class_not_mdi_alloced = 1;
}
/*
* Build Path LU list
*/
path_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_PATH_LU, (void*)pip);
if (path_list == NULL) {
/* Need to create path_list entry */
path_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_PATH_LU, (void*)pip);
} else {
/*
* Matched this pip w/ an existing one in current pip list.
* SAME PATH came online!! So, update the resp in main list.
*/
pd = path_list->item->idata;
pd->valid = 1;
pd->resp = pip;
}
if (MDI_PI_IS_ONLINE(pip)) {
vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
MP_DRVR_PATH_STATE_ACTIVE);
} else if (MDI_PI_IS_STANDBY(pip)) {
vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
MP_DRVR_PATH_STATE_PASSIVE);
} else {
vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
MP_DRVR_PATH_STATE_UNKNOWN);
}
/*
* Build Initiator Port list
*/
pdip = mdi_pi_get_phci(pip);
init = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) ddi_pathname(pdip, init);
init_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init);
if (init_list == NULL) {
/*
* Need to create init_list entry
* The resource ptr is no really pdip. It will be changed
* in vhci_mpapi_create_item(). The real resource ptr
* is the Port ID. But we pass the pdip, to create OID.
*/
init_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
} else {
initd = init_list->item->idata;
initd->valid = 1;
}
kmem_free(init, MAXPATHLEN);
/*
* Build Target Port list
* Can get the tdip: tdip = mdi_pi_get_client(pip);
* But what's the use? We want TARGET_PORT.
* So try getting Target Port's WWN which is unique per port.
*/
tmp_wwn = NULL;
if (mdi_prop_lookup_string(pip, "target-port", &tmp_wwn)
!= DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
tmp_wwn = (char *)mdi_pi_get_addr(pip);
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_update_mpapi_data: "
"mdi_prop_lookup_string() returned failure; "
"Hence tmp_wwn = %p", (void *)tmp_wwn));
}
tgt_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_TARGET_PORT, (void*)tmp_wwn);
if (tgt_list == NULL) {
/* Need to create tgt_list entry */
tgt_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_TARGET_PORT, (void*)tmp_wwn);
} else {
tpd = tgt_list->item->idata;
tpd->valid = 1;
}
/*
* LEVEL 2 - Actions:
* Since all the Object type item lists are updated to account
* for the new resources, now lets cross-reference these
* resources (mainly through paths) to maintain the
* relationship between them.
*/
ld = (mpapi_lu_data_t *)lu_list->item->idata;
if (vhci_get_mpapi_item(vhci, ld->path_list,
MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
lu_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
lu_path_list->item = path_list->item;
(void) vhci_mpapi_add_to_list(ld->path_list, lu_path_list);
}
initd = (mpapi_initiator_data_t *)init_list->item->idata;
if (vhci_get_mpapi_item(vhci, initd->path_list,
MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
init_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
init_path_list->item = path_list->item;
(void) vhci_mpapi_add_to_list(initd->path_list, init_path_list);
}
tpd = (mpapi_tport_data_t *)tgt_list->item->idata;
if (vhci_get_mpapi_item(vhci, tpd->path_list,
MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
tp_path_list = kmem_zalloc(
sizeof (mpapi_item_list_t), KM_SLEEP);
tp_path_list->item = path_list->item;
(void) vhci_mpapi_add_to_list(tpd->path_list, tp_path_list);
}
/*
* Level-1: Fill-out Path Properties now, since we got all details.
* Actually, It is a structure copy, rather than just filling details.
*/
pd = path_list->item->idata;
(void) strlcpy(pd->pclass, path_class, sizeof (pd->pclass));
bcopy(&(ld->prop), &(pd->prop.logicalUnit),
sizeof (struct mp_logical_unit_prop));
bcopy(&(initd->prop), &(pd->prop.initPort),
sizeof (struct mp_init_port_prop));
bcopy(&(tpd->prop), &(pd->prop.targetPort),
sizeof (struct mp_target_port_prop));
vhci_mpapi_synthesize_tpg_data(vhci, vlun, pip);
if (path_class_not_mdi_alloced == 1) {
kmem_free(path_class, MPAPI_SCSI_MAXPCLASSLEN);
}
}
/*
* Routine to search (& return if found) a TPG object with a specified
* accessState for a specified vlun structure. Returns NULL if either
* TPG object or the lu item is not found.
* This routine is used for NON-TPGS devices.
*/
/* ARGSUSED */
static mpapi_item_list_t *
vhci_mpapi_get_tpg_item(struct scsi_vhci *vhci, uint32_t acc_state, void *vlun,
char *pclass, void *tp)
{
mpapi_list_header_t *tpghdr, *this_tpghdr;
mpapi_item_list_t *lulist, *tpglist, *this_lulist, *this_tpglist;
mpapi_tpg_data_t *tpgdata, *this_tpgdata;
VHCI_DEBUG(6, (CE_NOTE, NULL, "vhci_mpapi_get_tpg_item: ENTER: vlun="
"%p, acc_state=%x, pclass=%s, tp=%s\n",
(void *)vlun, acc_state, pclass, (char *)tp));
lulist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
while (lulist != NULL) {
tpghdr = ((mpapi_lu_data_t *)(lulist->item->idata))->tpg_list;
tpglist = tpghdr->head;
while (tpglist != NULL) {
tpgdata = tpglist->item->idata;
if ((tpgdata) &&
(vhci_mpapi_check_tp_in_tpg(tpgdata, tp) == 1) &&
(strncmp(tpgdata->pclass, pclass,
strlen(pclass)) == 0)) {
return (tpglist);
} else {
tpglist = tpglist->next;
}
}
lulist = lulist->next;
}
this_lulist = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_MULTIPATH_LU, vlun);
if (this_lulist != NULL) {
this_tpghdr = ((mpapi_lu_data_t *)(this_lulist->item->idata))
->tpg_list;
this_tpglist = this_tpghdr->head;
while (this_tpglist != NULL) {
this_tpgdata = this_tpglist->item->idata;
if ((this_tpgdata) &&
(strncmp(this_tpgdata->pclass, pclass,
strlen(pclass)) == 0)) {
return (this_tpglist);
} else {
this_tpglist = this_tpglist->next;
}
}
}
VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_mpapi_get_tpg_item: Returns NULL"));
return (NULL);
}
/*
* Routine to search (& return if found) a TPG object with a specified
* accessState for a specified vlun structure. Returns NULL if either
* TPG object or the lu item is not found.
* This routine is used for NON-TPGS devices.
*/
/* ARGSUSED */
mpapi_item_list_t *
vhci_mpapi_get_tpg_for_lun(struct scsi_vhci *vhci, char *pclass,
void *vlun, void *tp)
{
mpapi_list_header_t *this_tpghdr;
mpapi_item_list_t *this_lulist, *this_tpglist;
mpapi_tpg_data_t *this_tpgdata;
VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_get_tpg_for_lun: ENTER: vlun="
"%p, pclass=%s, tp=%s\n", (void *)vlun, pclass, (char *)tp));
this_lulist = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_MULTIPATH_LU, vlun);
if (this_lulist != NULL) {
this_tpghdr = ((mpapi_lu_data_t *)(this_lulist->item->idata))
->tpg_list;
this_tpglist = this_tpghdr->head;
while (this_tpglist != NULL) {
this_tpgdata = this_tpglist->item->idata;
if ((this_tpgdata) &&
(vhci_mpapi_check_tp_in_tpg(this_tpgdata,
tp) == 1) && (strncmp(this_tpgdata->pclass, pclass,
strlen(pclass)) == 0)) {
return (this_tpglist);
}
this_tpglist = this_tpglist->next;
}
}
VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_mpapi_get_tpg_for_lun: Returns "
"NULL"));
return (NULL);
}
/*
* Routine to search a Target Port in a TPG
*/
/* ARGSUSED */
static int
vhci_mpapi_check_tp_in_tpg(mpapi_tpg_data_t *tpgdata, void *tp)
{
mpapi_item_list_t *tplist;
if (tpgdata) {
tplist = tpgdata->tport_list->head;
} else {
return (0);
}
while (tplist != NULL) {
void *resp = ((mpapi_tport_data_t *)tplist->
item->idata)->resp;
if (strncmp(resp, tp, strlen(resp)) == 0) {
/* Found a match */
return (1);
}
tplist = tplist->next;
}
return (0);
}
/*
* Routine to create Level 1 mpapi_private data structure for TPG object &
* establish cross references between the TPG resources being managed.
* TPG SYNTHESIS MODE: Process for NON-SCSI_FAILOVER_IS_TPGS devices ONLY.
* SCSI_FAILOVER_IS_TPGS devices have TPGS(ALUA support) and provide
* REPORT_TARGET_PORT_GROUP data which we can parse directly in the next
* routine(vhci_mpapi_update_tpg_data) to create TPG list in mpapi_priv block.
*/
/* ARGSUSED */
void
vhci_mpapi_synthesize_tpg_data(struct scsi_vhci *vhci, scsi_vhci_lun_t *vlun,
mdi_pathinfo_t *pip)
{
uint32_t as;
char *tmp_wwn = NULL, *path_class = NULL;
mpapi_item_list_t *tpg_tport_list, *tpg_lu_list, *lu_list;
mpapi_item_list_t *lu_tpg_list, *item_list, *tpg_list;
mpapi_tpg_data_t *tpg_data;
int path_class_not_mdi_alloced = 0;
/*
* Build Target Port Group list
* Start by finding out the affected Target Port.
*/
if (mdi_prop_lookup_string(pip, "target-port", &tmp_wwn)
!= DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
tmp_wwn = (char *)mdi_pi_get_addr(pip);
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_synthesize_tpg_data: "
"mdi_prop_lookup_string() returned failure; "
"Hence tmp_wwn = %p", (void *)tmp_wwn));
}
/*
* Finding out the "path-class" property
*/
if (mdi_prop_lookup_string(pip, "path-class", &path_class)
!= DDI_PROP_SUCCESS) {
/* XXX: path-class prop not found */
path_class = kmem_zalloc(MPAPI_SCSI_MAXPCLASSLEN, KM_SLEEP);
(void) strlcpy(path_class, "NONE", MPAPI_SCSI_MAXPCLASSLEN);
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_synthesize_tpg_data: "
"mdi_prop_lookup_string() returned failure; "
"Hence path_class = NONE"));
path_class_not_mdi_alloced = 1;
}
/*
* Check the vlun's accessState through pip; we'll use it later.
*/
if (MDI_PI_IS_ONLINE(pip)) {
as = MP_DRVR_ACCESS_STATE_ACTIVE;
} else if (MDI_PI_IS_STANDBY(pip)) {
as = MP_DRVR_ACCESS_STATE_STANDBY;
} else {
as = MP_DRVR_ACCESS_STATE_UNAVAILABLE;
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_synthesize_tpg_data: "
"Unknown pip state seen in TPG synthesis"));
}
VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_synthesize_tpg_data: ENTER: "
"vlun=%s, acc_state=%x, path_class=%s, tp=%s\n",
vlun->svl_lun_wwn, as, path_class, tmp_wwn));
/*
* Create Level 1 and Level 2 data structures for type
*/
if (!SCSI_FAILOVER_IS_TPGS(vlun->svl_fops)) {
/*
* First check if the lun has a TPG list in its level 2
* structure then, check if this lun is already
* accounted for through a different Target Port.
* If yes, get the ptr to the TPG & skip new TPG creation.
*/
lu_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_MULTIPATH_LU, vlun);
tpg_list = vhci_mpapi_get_tpg_item(vhci, as, vlun, path_class,
(void *)tmp_wwn);
if (tpg_list == NULL) {
tpg_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_TARGET_PORT_GROUP, (void *)tmp_wwn);
tpg_data = tpg_list->item->idata;
(void) strlcpy(tpg_data->pclass, path_class,
sizeof (tpg_data->pclass));
tpg_data->prop.accessState = as;
} else {
tpg_data = tpg_list->item->idata;
}
if (!SCSI_FAILOVER_IS_SYM(vlun->svl_fops)) {
tpg_data->prop.explicitFailover = 1;
}
/*
* Level 2, Lun Cross referencing to TPG.
*/
if (vhci_get_mpapi_item(vhci, tpg_data->lu_list,
MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun) == NULL) {
tpg_lu_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
item_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun);
tpg_lu_list->item = item_list->item;
(void) vhci_mpapi_add_to_list(tpg_data->lu_list,
tpg_lu_list);
}
/*
* Level 2, Target Port Cross referencing to TPG.
*/
if (vhci_get_mpapi_item(vhci, tpg_data->tport_list,
MP_OBJECT_TYPE_TARGET_PORT, (void *)tmp_wwn) == NULL) {
tpg_tport_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
item_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_TARGET_PORT, (void *)tmp_wwn);
tpg_tport_list->item = item_list->item;
(void) vhci_mpapi_add_to_list(tpg_data->tport_list,
tpg_tport_list);
}
/*
* Level 2, TPG Cross referencing to Lun.
*/
lu_tpg_list = vhci_mpapi_get_tpg_for_lun
(vhci, path_class, vlun, tmp_wwn);
if (lu_tpg_list == NULL) {
lu_tpg_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
lu_tpg_list->item = tpg_list->item;
(void) vhci_mpapi_add_to_list(((mpapi_lu_data_t *)
(lu_list->item->idata))->tpg_list, lu_tpg_list);
}
/*
* Update the AccessState of related MPAPI TPGs
* This takes care of a special case where a failover doesn't
* happen but a TPG accessState needs to be updated from
* Unavailable to Standby
*/
(void) vhci_mpapi_update_tpg_acc_state_for_lu(vhci, vlun);
}
if (path_class_not_mdi_alloced == 1) {
kmem_free(path_class, MPAPI_SCSI_MAXPCLASSLEN);
}
}
/*
* Routine to create Level 1 mpapi_private data structure for TPG object,
* for devices which support TPG and establish cross references between
* the TPG resources being managed. The RTPG response sent by std_asymmetric
* module is parsed in this routine and mpapi_priv data structure is updated.
*/
/* ARGSUSED */
void
vhci_mpapi_update_tpg_data(struct scsi_address *ap, char *ptr)
{
struct scsi_vhci_lun *vlun = ADDR2VLUN(ap);
struct scsi_vhci *vhci = ADDR2VHCI(ap);
struct scsi_device *psd;
scsi_vhci_priv_t *svp;
mdi_pathinfo_t *pip;
dev_info_t *pdip;
char tpg_id[16], *tgt_port, *init = NULL;
unsigned char *inqbuf;
uint32_t int_tpg_id, rel_tid, as;
int i, rel_tport_cnt;
mpapi_item_list_t *path_list, *init_list;
mpapi_item_list_t *tp_path_list, *init_path_list, *lu_path_list;
mpapi_item_list_t *tpg_tport_list, *tpg_lu_list, *lu_list;
mpapi_item_list_t *lu_tpg_list, *item_list, *tpg_list, *tgt_list;
mpapi_lu_data_t *ld;
mpapi_tpg_data_t *tpg_data;
mpapi_path_data_t *pd;
mpapi_tport_data_t *tpd;
mpapi_initiator_data_t *initd;
/*
* Find out the TPG ID (resource ptr for TPG is T10 TPG ID)
*/
int_tpg_id = ((ptr[2] & 0xff) << 8) | (ptr[3] & 0xff);
(void) sprintf(tpg_id, "%04x", int_tpg_id);
/*
* Check the TPG's accessState; we'll use it later.
*/
as = (ptr[0] & 0x0f);
if (as == STD_ACTIVE_OPTIMIZED) {
as = MP_DRVR_ACCESS_STATE_ACTIVE_OPTIMIZED;
} else if (as == STD_ACTIVE_NONOPTIMIZED) {
as = MP_DRVR_ACCESS_STATE_ACTIVE_NONOPTIMIZED;
} else if (as == STD_STANDBY) {
as = MP_DRVR_ACCESS_STATE_STANDBY;
} else {
as = MP_DRVR_ACCESS_STATE_UNAVAILABLE;
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_update_tpg_data: "
"UNAVAILABLE accessState seen in ALUA TPG setup"));
}
/*
* Get the vlun through the following process;
* ADDR2VLUN(ap) doesn't give the scsi_vhci lun
*/
psd = ap->a_hba_tran->tran_sd;
inqbuf = (unsigned char *)psd->sd_inq;
pip = (mdi_pathinfo_t *)(uintptr_t)(psd->sd_private);
svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
vlun = svp->svp_svl;
/*
* Now get the vhci ptr using the walker
*/
mdi_walk_vhcis(vhci_mpapi_get_vhci, &vhci);
VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_update_tpg_data: vhci=%p, "
"(vlun)wwn=(%p)%s, pip=%p, ap=%p, ptr=%p, as=%x, tpg_id=%s, fops="
"%p\n", (void *)vhci, (void *)vlun, vlun->svl_lun_wwn, (void *)pip,
(void *)ap, (void *)ptr, as, tpg_id, (void *)vlun->svl_fops));
if ((vhci == NULL) || (vlun == NULL) || (pip == NULL) ||
!SCSI_FAILOVER_IS_TPGS(vlun->svl_fops)) {
/* Cant help, unfortunate situation */
return;
}
/*
* LEVEL 1 - Actions:
* Check if the appropriate resource pointers already
* exist in the Level 1 list and add them if they are new.
*/
/*
* Build MP LU list
*/
lu_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
if (lu_list == NULL) {
/* Need to create lu_list entry */
lu_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
} else {
/*
* Matched this lu w/ an existing one in current lu list.
* SAME LUN came online!! So, update the resp in main list.
*/
ld = lu_list->item->idata;
ld->valid = 1;
ld->resp = vlun;
}
/*
* Build Path LU list
*/
path_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_PATH_LU, (void*)pip);
if (path_list == NULL) {
/* Need to create path_list entry */
path_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_PATH_LU, (void*)pip);
} else {
/*
* Matched this pip w/ an existing one in current pip list.
* SAME PATH came online!! So, update the resp in main list.
*/
pd = path_list->item->idata;
pd->valid = 1;
pd->resp = pip;
}
if (MDI_PI_IS_ONLINE(pip)) {
vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
MP_DRVR_PATH_STATE_ACTIVE);
} else if (MDI_PI_IS_STANDBY(pip)) {
vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
MP_DRVR_PATH_STATE_PASSIVE);
} else {
vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
MP_DRVR_PATH_STATE_UNKNOWN);
}
/*
* Build Initiator Port list
*/
pdip = mdi_pi_get_phci(pip);
init = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) ddi_pathname(pdip, init);
init_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init);
if (init_list == NULL) {
/*
* Need to create init_list entry
* The resource ptr is no really pdip. It will be changed
* in vhci_mpapi_create_item(). The real resource ptr
* is the Port ID. But we pass the pdip, to create OID.
*/
init_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
} else {
initd = init_list->item->idata;
initd->valid = 1;
}
kmem_free(init, MAXPATHLEN);
/*
* LEVEL 2 - Actions:
* Since all the Object type item lists are updated to account
* for the new resources, now lets cross-reference these
* resources (mainly through paths) to maintain the
* relationship between them.
*/
ld = (mpapi_lu_data_t *)lu_list->item->idata;
if (vhci_get_mpapi_item(vhci, ld->path_list,
MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
lu_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
lu_path_list->item = path_list->item;
(void) vhci_mpapi_add_to_list(ld->path_list, lu_path_list);
}
initd = (mpapi_initiator_data_t *)init_list->item->idata;
if (vhci_get_mpapi_item(vhci, initd->path_list,
MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
init_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
init_path_list->item = path_list->item;
(void) vhci_mpapi_add_to_list(initd->path_list, init_path_list);
}
/*
* Create Level 1 & Level 2 data structures
* Parse REPORT_TARGET_PORT_GROUP data & update mpapi database.
*/
tpg_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_TARGET_PORT_GROUP, &tpg_id);
if (tpg_list == NULL) {
tpg_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_TARGET_PORT_GROUP, &tpg_id);
}
tpg_data = tpg_list->item->idata;
tpg_data->prop.accessState = as;
tpg_data->prop.tpgId = int_tpg_id;
/*
* Set explicitFailover for TPG -
* based on tpgs_bits setting in Std Inquiry response.
*/
if ((((inqbuf[5] & 0x30) >> 4) == 2) ||
(((inqbuf[5] & 0x30) >> 4) == 3)) {
tpg_data->prop.explicitFailover = 1;
} else if (((inqbuf[5] & 0x30) >> 4) == 1) {
tpg_data->prop.explicitFailover = 0;
} else if (((inqbuf[5] & 0x30) >> 4) == 0) {
tpg_data->prop.explicitFailover = 0;
}
/*
* Level 2, Lun Cross referencing to TPG.
*/
if (vhci_get_mpapi_item(vhci, tpg_data->lu_list,
MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun) == NULL) {
tpg_lu_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
item_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun);
tpg_lu_list->item = item_list->item;
(void) vhci_mpapi_add_to_list(tpg_data->lu_list,
tpg_lu_list);
}
/*
* Level 2, TPG Cross referencing to Lun.
*/
if (vhci_get_mpapi_item(vhci, ld->tpg_list,
MP_OBJECT_TYPE_TARGET_PORT_GROUP, &tpg_id) == 0) {
lu_tpg_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
lu_tpg_list->item = tpg_list->item;
(void) vhci_mpapi_add_to_list(((mpapi_lu_data_t *)
(lu_list->item->idata))->tpg_list, lu_tpg_list);
}
/*
* Building Target Port list is different here.
* For each different Relative Target Port. we have a new MPAPI
* Target Port OID generated.
* Just find out the main Target Port property here.
*/
tgt_port = NULL;
if (mdi_prop_lookup_string(pip, "target-port", &tgt_port)
!= DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
tgt_port = (char *)mdi_pi_get_addr(pip);
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_update_tpg_data: "
"mdi_prop_lookup_string() returned failure; "
"Hence tgt_port = %p", (void *)tgt_port));
}
/*
* Level 1, Relative Target Port + Target Port Creation
*/
rel_tport_cnt = (ptr[7] & 0xff);
ptr += 8;
for (i = 0; i < rel_tport_cnt; i++) {
rel_tid = 0;
rel_tid |= ((ptr[2] & 0Xff) << 8);
rel_tid |= (ptr[3] & 0xff);
VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_update_tpg_data: "
"TgtPort=%s, RelTgtPort=%x\n", tgt_port, rel_tid));
tgt_list = vhci_mpapi_get_rel_tport_pair(vhci, NULL,
(void *)tgt_port, rel_tid);
if (tgt_list == NULL) {
/* Need to create tgt_list entry */
tgt_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_TARGET_PORT,
(void *)tgt_port);
tpd = tgt_list->item->idata;
tpd->valid = 1;
tpd->prop.relativePortID = rel_tid;
} else {
tpd = tgt_list->item->idata;
tpd->valid = 1;
}
tpd = (mpapi_tport_data_t *)tgt_list->item->idata;
if (vhci_get_mpapi_item(vhci, tpd->path_list,
MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
tp_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
KM_SLEEP);
tp_path_list->item = path_list->item;
(void) vhci_mpapi_add_to_list(tpd->path_list,
tp_path_list);
}
if (vhci_mpapi_get_rel_tport_pair(vhci,
tpg_data->tport_list, tgt_port, rel_tid) == NULL) {
tpg_tport_list = kmem_zalloc
(sizeof (mpapi_item_list_t), KM_SLEEP);
tpg_tport_list->item = tgt_list->item;
(void) vhci_mpapi_add_to_list(tpg_data->
tport_list, tpg_tport_list);
}
ptr += 4;
}
/*
* Level-1: Fill-out Path Properties now, since we got all details.
* Actually, It is a structure copy, rather than just filling details.
*/
pd = path_list->item->idata;
bcopy(&(ld->prop), &(pd->prop.logicalUnit),
sizeof (struct mp_logical_unit_prop));
bcopy(&(initd->prop), &(pd->prop.initPort),
sizeof (struct mp_init_port_prop));
bcopy(&(tpd->prop), &(pd->prop.targetPort),
sizeof (struct mp_target_port_prop));
}
/*
* Routine to get mpapi ioctl argument structure from userland.
*/
/* ARGSUSED */
static int
vhci_get_mpiocdata(const void *data, mp_iocdata_t *mpioc, int mode)
{
int retval = 0;
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
mp_iocdata32_t ioc32;
VHCI_DEBUG(6, (CE_WARN, NULL, "vhci_get_mpiocdata: "
"Case DDI_MODEL_ILP32"));
if (ddi_copyin((void *)data, (void *)&ioc32,
sizeof (mp_iocdata32_t), mode)) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_mpiocdata: "
"ddi_copyin() FAILED"));
retval = EFAULT;
break;
}
mpioc->mp_xfer = (uint16_t)(uintptr_t)ioc32.mp_xfer;
mpioc->mp_cmd = (uint16_t)(uintptr_t)ioc32.mp_cmd;
mpioc->mp_flags = (uint16_t)(uintptr_t)ioc32.mp_flags;
mpioc->mp_cmd_flags = (uint16_t)ioc32.mp_cmd_flags;
mpioc->mp_ilen = (size_t)(uintptr_t)ioc32.mp_ilen;
mpioc->mp_ibuf = (caddr_t)(uintptr_t)ioc32.mp_ibuf;
mpioc->mp_olen = (size_t)(uintptr_t)ioc32.mp_olen;
mpioc->mp_obuf = (caddr_t)(uintptr_t)ioc32.mp_obuf;
mpioc->mp_alen = (size_t)(uintptr_t)ioc32.mp_alen;
mpioc->mp_abuf = (caddr_t)(uintptr_t)ioc32.mp_abuf;
mpioc->mp_errno = (int)(uintptr_t)ioc32.mp_errno;
break;
}
case DDI_MODEL_NONE:
if (ddi_copyin(data, (void*)mpioc, sizeof (*mpioc), mode)) {
retval = EFAULT;
break;
}
break;
default:
if (ddi_copyin(data, (void*)mpioc, sizeof (*mpioc), mode)) {
retval = EFAULT;
break;
}
break;
}
#else /* _MULTI_DATAMODEL */
if (ddi_copyin(data, (void *)mpioc, sizeof (*mpioc), mode)) {
retval = EFAULT;
}
#endif /* _MULTI_DATAMODEL */
if (retval) {
VHCI_DEBUG(2, (CE_WARN, NULL, "vhci_get_mpiocdata: cmd <%x> "
"iocdata copyin failed", mpioc->mp_cmd));
}
return (retval);
}
/* ARGSUSED */
static int
vhci_is_model_type32(int mode)
{
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
return (1);
default:
return (0);
}
#else /* _MULTI_DATAMODEL */
return (0);
#endif /* _MULTI_DATAMODEL */
}
/*
* Convenience routine to copy mp_iocdata(32) to user land
*/
/* ARGSUSED */
static int
vhci_mpapi_copyout_iocdata(void *mpioc, void *udata, int mode)
{
int rval = 0;
if (vhci_is_model_type32(mode)) {
mp_iocdata32_t *mpioc32;
mpioc32 = (mp_iocdata32_t *)kmem_zalloc
(sizeof (mp_iocdata32_t), KM_SLEEP);
mpioc32->mp_xfer = (uint16_t)((mp_iocdata_t *)mpioc)->mp_xfer;
mpioc32->mp_cmd = (uint16_t)((mp_iocdata_t *)mpioc)->mp_cmd;
mpioc32->mp_flags = (uint16_t)((mp_iocdata_t *)mpioc)->mp_flags;
mpioc32->mp_cmd_flags = (uint16_t)((mp_iocdata_t *)
mpioc)->mp_cmd_flags;
mpioc32->mp_ilen = (uint32_t)((mp_iocdata_t *)mpioc)->mp_ilen;
mpioc32->mp_ibuf = (caddr32_t)((mp_iocdata32_t *)
mpioc)->mp_ibuf;
mpioc32->mp_olen = (uint32_t)((mp_iocdata_t *)mpioc)->mp_olen;
mpioc32->mp_obuf = (caddr32_t)((mp_iocdata32_t *)
mpioc)->mp_obuf;
mpioc32->mp_alen = (uint32_t)((mp_iocdata_t *)mpioc)->mp_alen;
mpioc32->mp_abuf = (caddr32_t)((mp_iocdata32_t *)
mpioc)->mp_abuf;
mpioc32->mp_errno = (int32_t)((mp_iocdata_t *)mpioc)->mp_errno;
if (ddi_copyout(mpioc32, udata, sizeof (mp_iocdata32_t), mode)
!= 0) {
rval = EFAULT;
}
kmem_free(mpioc32, sizeof (mp_iocdata32_t));
} else {
/* 64-bit ddicopyout */
if (ddi_copyout(mpioc, udata, sizeof (mp_iocdata_t), mode)
!= 0) {
rval = EFAULT;
}
}
return (rval);
}
/*
* Routine to sync OIDs of MPLU to match with the ssd instance# of the
* scsi_vhci lun, to accommodate the DINFOCACHE implementation of the plugin.
* ssd instance# = devi_instance from the dev_info structure.
* dev_info structure of the scsi_vhci lun is pointed by svl_dip field of
* scsi_vhci_lun structure.
*/
/* ARGSUSED */
static int
vhci_mpapi_sync_lu_oid_list(struct scsi_vhci *vhci)
{
int rval = 0;
mpapi_item_list_t *ilist;
mpapi_lu_data_t *lud;
mpapi_path_data_t *pd;
scsi_vhci_lun_t *svl;
dev_info_t *lun_dip;
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
while (ilist != NULL) {
if (MP_GET_MAJOR_FROM_ID((uint64_t)
(ilist->item->oid.raw_oid)) != 0) {
lud = ilist->item->idata;
if (lud->valid == 1) {
svl = lud->resp;
ilist->item->oid.raw_oid =
(uint64_t)ddi_get_instance(svl->svl_dip);
lud->prop.id =
(uint64_t)ddi_get_instance(svl->svl_dip);
}
}
ilist = ilist->next;
}
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_PATH_LU]->head;
while (ilist != NULL) {
pd = ilist->item->idata;
if ((pd->valid == 1) && (MP_GET_MAJOR_FROM_ID((uint64_t)
(pd->prop.logicalUnit.id)) != 0)) {
lun_dip = mdi_pi_get_client
((mdi_pathinfo_t *)(pd->resp));
pd->prop.logicalUnit.id =
(uint64_t)ddi_get_instance(lun_dip);
}
ilist = ilist->next;
}
return (rval);
}
/*
* Routine to sync Initiator Port List with what MDI maintains. This means
* MP API knows about Initiator Ports which don't have a pip.
*/
/* ARGSUSED */
int
vhci_mpapi_sync_init_port_list(dev_info_t *pdip, void *arg)
{
int init_not_ddi_alloced = 0;
struct scsi_vhci *vhci = arg;
char *init, *init_port_res;
mpapi_item_list_t *init_list;
mpapi_initiator_data_t *initd;
if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
DDI_PROP_DONTPASS, "initiator-port", &init) != DDI_PROP_SUCCESS)) {
/* XXX: initiator-port prop not found */
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_sync_init_port_list: "
"initiator-port prop not found"));
init = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
init_not_ddi_alloced = 1;
(void) ddi_pathname(pdip, init);
}
init_port_res = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) ddi_pathname(pdip, init_port_res);
init_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init_port_res);
if (init_list == NULL) {
/*
* Need to create init_list entry
* The resource ptr is not really pdip. It will be changed
* in vhci_mpapi_create_item(). The real resource ptr
* is the Port ID. But we pass the pdip, to create OID.
*/
init_list = vhci_mpapi_create_item(vhci,
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
}
initd = init_list->item->idata;
initd->valid = 1;
(void) strlcpy(initd->prop.portID, init, sizeof (initd->prop.portID));
if (init_not_ddi_alloced == 1) {
kmem_free(init, MAXPATHLEN);
} else if (init) {
ddi_prop_free(init);
}
kmem_free(init_port_res, MAXPATHLEN);
return (DDI_WALK_CONTINUE);
}
/* ARGSUSED */
static void
vhci_mpapi_log_sysevent(dev_info_t *dip, uint64_t *oid, char *subclass)
{
nvlist_t *attr_list;
if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
KM_SLEEP) != DDI_SUCCESS) {
goto alloc_failed;
}
if (nvlist_add_uint64_array(attr_list, "oid", oid, 1) != DDI_SUCCESS) {
goto error;
}
(void) ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_SUN_MP, subclass,
attr_list, NULL, DDI_SLEEP);
error:
nvlist_free(attr_list);
return;
alloc_failed:
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_log_sysevent: "
"Unable to send sysevent"));
}
/* ARGSUSED */
void
vhci_mpapi_set_path_state(dev_info_t *vdip, mdi_pathinfo_t *pip, int state)
{
struct scsi_vhci *vhci;
struct scsi_vhci_lun *svl;
scsi_vhci_priv_t *svp;
mpapi_item_list_t *ilist, *lu_list;
mpapi_path_data_t *pp;
mpapi_lu_data_t *ld;
vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
ilist = vhci_get_mpapi_item(vhci, NULL, MP_OBJECT_TYPE_PATH_LU, pip);
if (ilist != NULL) {
mutex_enter(&ilist->item->item_mutex);
pp = ilist->item->idata;
pp->prop.pathState = state;
pp->valid = 1;
} else {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_set_path_state: "
"pip(%p) not found", (void *)pip));
return;
}
/*
* Find if there are any paths at all to the lun
*/
if ((state == MP_DRVR_PATH_STATE_REMOVED) || (state ==
MP_DRVR_PATH_STATE_PATH_ERR) || (state ==
MP_DRVR_PATH_STATE_LU_ERR) || (state ==
MP_DRVR_PATH_STATE_UNKNOWN)) {
pp->valid = 0;
svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
svl = svp->svp_svl;
/*
* Update the AccessState of related MPAPI TPGs
* This takes care of a special case where a path goes offline
* & the TPG accessState may need an update from
* Active/Standby to Unavailable.
*/
if (!SCSI_FAILOVER_IS_TPGS(svl->svl_fops)) {
(void) vhci_mpapi_update_tpg_acc_state_for_lu(vhci,
svl);
}
/*
* Following means the lun is offline
*/
if (vhci_mpapi_chk_last_path(pip) == -1) {
lu_list = vhci_get_mpapi_item(vhci, NULL,
MP_OBJECT_TYPE_MULTIPATH_LU, (void *)svl);
if (lu_list != NULL) {
ld = lu_list->item->idata;
ld->valid = 0;
}
}
}
mutex_exit(&ilist->item->item_mutex);
}
/* ARGSUSED */
static mpapi_item_list_t *
vhci_mpapi_match_pip(struct scsi_vhci *vhci, mpapi_item_list_t *ilist,
void *res)
{
mpapi_path_data_t *pd;
scsi_vhci_lun_t *this_svl;
mdi_pathinfo_t *this_pip;
char *this_iport;
char *this_tport;
char *pname;
this_pip = (mdi_pathinfo_t *)res;
if ((this_pip == NULL) || (ilist == NULL)) {
return (NULL);
}
this_iport = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) ddi_pathname(mdi_pi_get_phci(this_pip), this_iport);
if (mdi_prop_lookup_string(this_pip, "target-port", &this_tport)
!= DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
this_tport = (char *)mdi_pi_get_addr(this_pip);
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_match_pip: "
"mdi_prop_lookup_string() returned failure; "
"Hence this_tport = %p", (void *)this_tport));
}
this_svl = mdi_client_get_vhci_private(mdi_pi_get_client(this_pip));
pname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) strlcat(pname, this_iport, MAXPATHLEN);
(void) strlcat(pname, this_tport, MAXPATHLEN);
(void) strlcat(pname, this_svl->svl_lun_wwn, MAXPATHLEN);
kmem_free(this_iport, MAXPATHLEN);
while (ilist != NULL) {
pd = (mpapi_path_data_t *)(ilist->item->idata);
if ((pd != NULL) && (strncmp
(pd->path_name, pname, strlen(pname)) == 0)) {
VHCI_DEBUG(6, (CE_WARN, NULL, "vhci_mpapi_match_pip: "
"path_name = %s", pd->path_name));
kmem_free(pname, MAXPATHLEN);
return (ilist);
}
ilist = ilist->next;
}
kmem_free(pname, MAXPATHLEN);
return (NULL);
}
/* ARGSUSED */
static
mpapi_item_list_t *vhci_mpapi_match_lu(struct scsi_vhci *vhci,
mpapi_item_list_t *ilist, void *res)
{
mpapi_lu_data_t *ld;
scsi_vhci_lun_t *this_svl;
this_svl = (scsi_vhci_lun_t *)res;
if ((this_svl == NULL) || (ilist == NULL)) {
return (NULL);
}
while (ilist != NULL) {
ld = (mpapi_lu_data_t *)(ilist->item->idata);
if ((ld != NULL) && (strncmp
(ld->prop.name, this_svl->svl_lun_wwn,
strlen(this_svl->svl_lun_wwn)) == 0)) {
VHCI_DEBUG(6, (CE_WARN, NULL, "vhci_mpapi_match_lu: "
"this_wwn = %s", this_svl->svl_lun_wwn));
return (ilist);
}
ilist = ilist->next;
}
return (NULL);
}
/*
* Routine to handle TPG AccessState Change - Called after each LU failover
*/
int
vhci_mpapi_update_tpg_acc_state_for_lu(struct scsi_vhci *vhci,
scsi_vhci_lun_t *vlun)
{
int rval = 0;
mpapi_item_list_t *lu_list, *path_list, *tpg_list;
mpapi_lu_data_t *lu_data;
mpapi_path_data_t *path_data;
mpapi_tpg_data_t *tpg_data;
lu_list = vhci_get_mpapi_item(vhci, NULL, MP_OBJECT_TYPE_MULTIPATH_LU,
(void *)vlun);
if (lu_list == NULL) {
return (-1);
}
lu_data = lu_list->item->idata;
if (lu_data == NULL) {
return (-1);
}
lu_data->resp = vlun;
lu_data->valid = 1;
/*
* For each "pclass of PATH" and "pclass of TPG" match of this LU,
* Update the TPG AccessState to reflect the state of the path.
* Exit the inner loop after the 1st successful ACTIVE/STANDBY update
* is made, because subsequent matches also lead to the same TPG.
*/
tpg_list = lu_data->tpg_list->head;
while (tpg_list != NULL) {
tpg_data = tpg_list->item->idata;
path_list = lu_data->path_list->head;
while (path_list != NULL) {
path_data = path_list->item->idata;
if ((path_data->valid == 1) &&
(strncmp(path_data->pclass, tpg_data->pclass,
strlen(tpg_data->pclass)) == 0)) {
VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_"
"update_tpg_acc_state_for_lu: Operating on "
"LUN(%s), PATH(%p), TPG(%x: %s)\n",
lu_data->prop.name, path_data->resp,
tpg_data->prop.tpgId, tpg_data->pclass));
if (MDI_PI_IS_ONLINE(path_data->resp)) {
tpg_data->prop.accessState =
MP_DRVR_ACCESS_STATE_ACTIVE;
break;
} else if (MDI_PI_IS_STANDBY(path_data->resp)) {
tpg_data->prop.accessState =
MP_DRVR_ACCESS_STATE_STANDBY;
break;
} else {
tpg_data->prop.accessState =
MP_DRVR_ACCESS_STATE_UNAVAILABLE;
}
}
path_list = path_list->next;
}
tpg_list = tpg_list->next;
}
return (rval);
}
int
vhci_mpapi_get_vhci(dev_info_t *vdip, void *ptr2vhci)
{
struct scsi_vhci *local_vhci;
if (strncmp("scsi_vhci", ddi_get_name(vdip),
strlen("scsi_vhci")) == 0) {
local_vhci = ddi_get_soft_state(vhci_softstate,
ddi_get_instance(vdip));
bcopy(&local_vhci, ptr2vhci, sizeof (local_vhci));
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
/* ARGSUSED */
void *
vhci_mpapi_get_rel_tport_pair(struct scsi_vhci *vhci, mpapi_list_header_t *list,
void *tgt_port, uint32_t rel_tid)
{
mpapi_item_list_t *ilist;
mpapi_tport_data_t *tpd;
if (list == NULL) {
/*
* Since the listhead is null, the search is being
* performed in implicit mode - that is to use the
* level one list.
*/
ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT]
->head;
} else {
/*
* The search is being performed on a sublist within
* one of the toplevel list items. Use the listhead
* that is passed in.
*/
ilist = list->head;
}
if (tgt_port == NULL) {
VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_mpapi_item: "
" Got Target Port w/ NULL resource"));
return (NULL);
}
while (ilist) {
tpd = (mpapi_tport_data_t *)ilist->item->idata;
if ((strncmp(tpd->resp, tgt_port, strlen(tgt_port)) == 0) &&
(tpd->prop.relativePortID == rel_tid)) {
/* Match */
return ((void*)ilist);
} else {
ilist = ilist->next;
}
}
return (NULL);
}
/*
* Returns 0, if 2 more paths are available to the lun;
* Returns 1, if ONLY 1 path is available to the lun;
* Return -1 for all other cases.
*/
static int
vhci_mpapi_chk_last_path(mdi_pathinfo_t *pip)
{
dev_info_t *pdip = NULL, *cdip = NULL;
int count = 0, circular;
mdi_pathinfo_t *ret_pip;
if (pip == NULL) {
return (-1);
} else {
pdip = mdi_pi_get_phci(pip);
cdip = mdi_pi_get_client(pip);
}
if ((pdip == NULL) || (cdip == NULL)) {
return (-1);
}
ndi_devi_enter(cdip, &circular);
ret_pip = mdi_get_next_phci_path(cdip, NULL);
while ((ret_pip != NULL) && (count < 2)) {
mdi_pi_lock(ret_pip);
if ((MDI_PI_IS_ONLINE(ret_pip) ||
MDI_PI_IS_STANDBY(ret_pip) ||
MDI_PI_IS_INIT(ret_pip)) &&
!(MDI_PI_IS_DISABLE(ret_pip) ||
MDI_PI_IS_TRANSIENT(ret_pip))) {
count++;
}
mdi_pi_unlock(ret_pip);
ret_pip = mdi_get_next_phci_path(cdip, ret_pip);
}
ndi_devi_exit(cdip, circular);
if (count > 1) {
return (0);
} else if (count == 1) {
return (1);
}
return (-1);
}