mpapi_impl.c revision cc25db92ff252ca1b7f63bd54f087f4ddf0dda4e
/*
* 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
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* SNIA Multipath Management API implementation
*/
#include <sys/mdi_impldefs.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 *);
void vhci_update_mpapi_data(struct scsi_vhci *,
scsi_vhci_lun_t *, mdi_pathinfo_t *);
uint8_t, void*);
int vhci_mpapi_sync_init_port_list(dev_info_t *, void *);
int vhci_mpapi_get_vhci(dev_info_t *, void *);
mdi_pathinfo_t *);
void vhci_mpapi_update_tpg_data(struct scsi_address *, char *, int);
int vhci_mpapi_update_tpg_acc_state_for_lu(struct scsi_vhci *,
scsi_vhci_lun_t *);
/* Static Functions */
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
static int vhci_get_path_list_for_target_port(struct scsi_vhci *,
mp_iocdata_t *, void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
void *, void *, int);
mp_iocdata_t *, int, cred_t *);
uint8_t, void *);
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 *);
char *, void *, void *);
mpapi_item_list_t *, void *);
mpapi_item_list_t *, void *);
/*
* Extern variables, structures and functions
*/
extern void *vhci_softstate;
extern char vhci_version_name[];
extern int vhci_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 *);
/*
* Routine for SCSI VHCI MPAPI IOCTL implementation.
*/
/* ARGSUSED */
int
{
int retval = 0;
/* Check for validity of vhci structure */
return (ENXIO);
}
return (ENXIO);
}
/* Get the vhci dip */
/*
* Get IOCTL parameters from userland
*/
"vhci_get_mpiocdata() failed"));
}
return (ENXIO);
}
return (retval);
}
/* ARGSUSED */
static int
{
int mode32 = 0;
mode32 = 1;
}
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:
{
}
/* We don't know alen yet, No point trying to set it */
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;
}
}
}
}
break;
case MP_GET_DEV_PROD_PROP:
{
olen = sizeof (mp_dev_prod_prop_t);
}
}
}
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;
}
}
}
}
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;
}
}
}
}
break;
case MP_GET_INIT_PORT_PROP:
{
olen = sizeof (mp_init_port_prop_t);
}
}
}
break;
case MP_GET_TARGET_PORT_PROP:
{
olen = sizeof (mp_target_port_prop_t);
}
}
}
break;
case MP_GET_TPG_PROP:
{
olen = sizeof (mp_tpg_prop_t);
}
}
}
break;
{
olen = sizeof (mp_proprietary_loadbalance_prop_t);
/* Adjust olen to account for the caddr_t in 32-bit mode */
if (mode32 == 1) {
olen -= 4;
}
}
}
}
break;
case MP_GET_LU_LIST_FROM_TPG:
case MP_GET_TPG_LIST_FOR_LU:
{
}
/* We don't know alen yet, No point trying to set it */
rval = MP_MORE_DATA;
}
}
break;
case MP_SET_TPG_ACCESS_STATE:
{
break;
}
}
}
break;
case MP_ENABLE_AUTO_FAILBACK:
case MP_DISABLE_AUTO_FAILBACK:
{
break;
}
}
}
break;
case MP_ENABLE_PATH:
case MP_DISABLE_PATH:
{
break;
}
}
}
break;
case MP_SEND_SCSI_CMD:
{
int olen = 0;
cr = ddi_get_cred();
break;
}
if (mode32 == 1) {
olen = sizeof (struct uscsi_cmd32);
} else {
}
/* oid is in the ibuf and the uscsi cmd is in the obuf */
}
}
break;
case MP_ASSIGN_LU_TO_TPG:
{
break;
}
}
}
break;
default:
{
}
} /* Closing the main switch */
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
if (output_data == NULL) {
return (EINVAL);
}
sizeof (mpdp->driverVersion));
sizeof (mpdp->deviceFileNamespace));
mpdp->failbackPollingRateMax = 0;
1 : 0);
mpdp->probingPollingRateMax = 0;
mpdp->autoProbingSupport = 0;
mpdp->autoProbingEnabled = 0;
"ddi_copyout() for 64-bit failed"));
} else {
}
return (rval);
}
/* ARGSUSED */
static int
{
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.
*/
} else {
rval = MP_MORE_DATA;
}
count++;
}
return (EINVAL);
}
"ddi_copyout() failed"));
} else {
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
return (EINVAL);
}
}
return (EINVAL);
}
} else {
"OID NOT FOUND"));
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
sizeof (mp_dev_prod_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
{
if (output_data == NULL) {
return (EINVAL);
}
} else {
rval = MP_MORE_DATA;
}
count--;
}
count++;
}
return (EINVAL);
}
"ddi_copyout() FAILED"));
} else {
}
return (rval);
}
/* ARGSUSED */
static int
{
->head;
"OID NOT FOUND"));
"tpg: OID NOT FOUND - TPG IS INVALID"));
return (EINVAL);
}
} else {
"Unknown Error"));
}
while (tpg_lu_list != NULL) {
} else {
rval = MP_MORE_DATA;
}
/*
* Get rid of the latest entry if item is invalid
*/
count--;
}
count++;
}
return (EINVAL);
}
"ddi_copyout() FAILED"));
}
return (rval);
}
/* ARGSUSED */
static int
{
"OID NOT FOUND"));
"lu: OID NOT FOUND - LU IS OFFLINE"));
return (EINVAL);
}
} else {
"Unknown Error"));
}
while (mplu_tpg_list != NULL) {
} else {
rval = MP_MORE_DATA;
}
count--;
}
count++;
}
return (EINVAL);
}
"ddi_copyout() FAILED"));
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
}
"idata in ilist is NULL"));
return (EINVAL);
"OID NOT FOUND - LU GONE OFFLINE"));
return (EINVAL);
}
} else {
"OID NOT FOUND"));
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
sizeof (mp_logical_unit_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
{
"OID NOT FOUND"));
"mp_lu: MP_DRVR_PATH_STATE_LU_ERR - LU OFFLINE"));
return (EINVAL);
}
} else {
"Unknown Error"));
}
while (mplu_path_list != NULL) {
/* skip a path that should be hidden. */
/*
* check if the pip is marked as device removed.
* When pi_flag MDI_PATHINFO_FLAGS_DEVICE_REMOVED is set
* the node should have been destroyed but did not
* due to open on the client node.
* The driver tracks such a node through the hide flag
* and doesn't report it throuth ioctl response.
* The devinfo driver doesn't report such a path.
*/
if (!(MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip))) {
} else {
rval = MP_MORE_DATA;
}
count++;
}
}
}
return (EINVAL);
}
"ddi_copyout() FAILED"));
}
return (rval);
}
/* ARGSUSED */
static int
{
/*
* 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.
*/
}
vhci);
"port: OID NOT FOUND"));
"init_port: OID NOT FOUND - INIT PORT INVALID"));
return (EINVAL);
}
} else {
"port: Unknown Error"));
}
while (mpinit_path_list != NULL) {
/* skip a path that should be hidden. */
/*
* check if the pip is marked as device removed.
* When pi_flag MDI_PATHINFO_FLAGS_DEVICE_REMOVED is set
* the node should have been destroyed but did not
* due to open on the client node.
* The driver tracks such a node through the hide flag
* and doesn't report it throuth ioctl response.
* The devinfo driver doesn't report such a path.
*/
if (!(MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip))) {
} else {
rval = MP_MORE_DATA;
}
count++;
}
}
}
return (EINVAL);
}
"port: ddi_copyout() FAILED"));
}
return (rval);
}
/* ARGSUSED */
static int
{
"port: OID NOT FOUND"));
"target_port: OID NOT FOUND - TGT PORT INVALID"));
return (EINVAL);
}
} else {
"port: Unknown Error"));
}
while (mptp_path_list != NULL) {
/* skip a path that should be hidden. */
/*
* check if the pip is marked as device removed.
* When pi_flag MDI_PATHINFO_FLAGS_DEVICE_REMOVED is set
* the node should have been destroyed but did not
* due to open on the client node.
* The driver tracks such a node through the hide flag
* and doesn't report it throuth ioctl response.
* The devinfo driver doesn't report such a path.
*/
if (!(MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip))) {
} else {
rval = MP_MORE_DATA;
}
count++;
}
}
}
return (EINVAL);
}
"port: ddi_copyout() FAILED"));
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
"idata in ilist is NULL"));
return (EINVAL);
}
} else {
"OID NOT FOUND"));
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
sizeof (mp_path_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
{
/*
* 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.
*/
}
vhci);
} else {
rval = MP_MORE_DATA;
}
/*
* Get rid of the latest entry if item is invalid
*/
count--;
}
count++;
}
return (EINVAL);
}
"ddi_copyout() FAILED"));
} else {
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
/*
* 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.
*/
}
vhci);
}
" idata in ilist is NULL"));
return (EINVAL);
": OID NOT FOUND - INIT PORT IS INVALID"));
return (EINVAL);
}
} else {
"OID NOT FOUND"));
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
sizeof (mp_init_port_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
}
"prop: idata in ilist is NULL"));
return (EINVAL);
"prop: OID NOT FOUND - TARGET PORT INVALID"));
return (EINVAL);
}
} else {
"OID NOT FOUND"));
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
sizeof (mp_target_port_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
head;
}
"idata in ilist is NULL"));
return (EINVAL);
"OID NOT FOUND - TPG INVALID"));
return (EINVAL);
}
} else {
"OID NOT FOUND"));
return (EINVAL);
}
/*
* Here were are not using the 'output_data' that is
* passed as the required information is already
* in the required format!
*/
sizeof (mp_tpg_prop_t), mode) != 0) {
return (EFAULT);
}
return (rval);
}
/* ARGSUSED */
static int
{
->head;
"tpg: OID NOT FOUND"));
"list_for_tpg: OID NOT FOUND - TPG INVALID"));
return (EINVAL);
}
} else {
"tpg: Unknown Error"));
}
while (tpg_tp_list != NULL) {
} else {
rval = MP_MORE_DATA;
}
count--;
}
count++;
}
return (EINVAL);
}
"tpg: ddi_copyout() FAILED"));
}
return (rval);
}
/* ARGSUSED */
static int
{
->head;
->head;
"OID NOT FOUND"));
return (EINVAL);
}
if ((desired_state != MP_DRVR_ACCESS_STATE_ACTIVE) &&
return (EINVAL);
}
"state: TPG already in desired State"));
return (EINVAL);
}
/*
* All input seems to be ok, Go ahead & change state.
*/
/*
* retval specifically cares about failover
* status and not about this routine's success.
*/
if (retval != 0) {
"state: FAILOVER FAILED: %x", retval));
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().
*/
"state: FAILOVER SUCCESS: %x", retval));
}
} else {
/*
* Send SET_TARGET_PORT_GROUP SCSI Command. This is supported
* ONLY by devices which have TPGS EXPLICIT Failover support.
*/
"state: Unable to find path: %x", retval));
return (EINVAL);
}
"state: Unable to find vhci private data"));
return (EINVAL);
}
"state: Unable to find scsi device"));
return (EINVAL);
}
if (retval != 0) {
"state:(ALUA) FAILOVER FAILED: %x", retval));
return (EIO);
} else {
/*
* Don't set accessState here.
* std_report_target_groups() call needs to sync up
* properly.
*/
"state:(ALUA) FAILOVER SUCCESS: %x", retval));
if (!held) {
return (TRAN_BUSY);
} else {
vhci_update_pathstates((void *)svl);
}
"access_state: TPGAccessState NOT Set: "
"des_state=%x, cur_state=%x", desired_state,
return (EIO);
}
}
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
"ddi_copyout() FAILED"));
} else {
}
return (rval);
}
/* ARGSUSED */
static int
{
return (rval);
}
/*
* Operation not supported currently as we do not know
* support any devices that allow this in the first place.
*/
/* ARGSUSED */
static int
{
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
/* Enable auto-failback for each lun in MPAPI database */
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
/* Disable auto-failback for each lun in MPAPI database */
}
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 *
{
"OID NOT FOUND. oid: %p", (void *)oid));
return (NULL);
}
return (ilist);
}
"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.
*/
{
"pathinfo is not valid: %p", (void *)mpp));
return (NULL);
}
/* make sure it is the same pip by checking 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
*/
{
int rval;
/* make sure it is the same pip by checking path */
"Can not match pip: %p", (void *)pip));
return (NULL);
}
/*
* use the select path to find the right pip since
* it does all the state checking and locks the pip
*/
do {
" Unable to find path: %x.", rval));
return (NULL);
}
break;
}
return (npip);
}
/*
* Initialize the uscsi command. Lock the pip and the item in
* the item list.
*/
static mp_uscsi_cmd_t *
{
int arq_enabled;
struct scsi_address *ap;
"vhci_init_uscsi_cmd: enter"));
/* lock the item */
"vhci_init_uscsi_cmd: exit EINVAL"));
return (NULL);
}
/* lock the pip */
(MDI_SELECT_STANDBY_PATH|MDI_SELECT_ONLINE_PATH))) == 0) {
"vhci_init_uscsi_cmd: exit PATH_UNAVAIL"));
return (NULL);
};
/* get the address of the pip */
" Unable to find vhci private data"));
return (NULL);
}
" Unable to find scsi device"));
return (NULL);
}
/* initialize the buffer */
/* initialize the mp_uscsi_cmd */
/* used to debug a manual sense */
if (vhci_force_manual_sense) {
} else {
}
}
if (arq_enabled == 1) {
} else {
mp_uscmdp->arq_enabled = 0;
}
/* set the list pointer for the caller */
"vhci_init_uscsi_cmd: mp_uscmdp: %p ilist: %p mp_errno: %d "
"bp: %p arq: %d",
(void *)bp, arq_enabled));
return (mp_uscmdp);
}
/*
* Initialize the uscsi information and then issue the command.
*/
/* ARGSUSED */
static int
{
"vhci_send_uscsi_cmd: enter: mode: %x", mode));
"vhci_send_uscsi_cmd: exit INVALID_ID. rval: %d", rval));
return (EINVAL);
}
if (rval != 0) {
"scsi_uscsi_alloc_and_copyin failed. rval: %d", rval));
return (EINVAL);
}
/* initialize the mp_uscsi_cmd with the uscsi_cmd from uscsi_alloc */
/* start the command sending the buffer as an argument */
if (rval != 0) {
"scsi_uscsi_handle_cmd failed. rval: %d", rval));
}
"scsi_uscsi_copyout_and_free failed. rval: %d", rval));
}
/* cleanup */
"vhci_send_uscsi_cmd: rval: %d mp_errno: %d",
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
MP_OBJECT_TYPE_PATH_LU)) == NULL) {
return (EINVAL);
}
return (EINVAL);
}
} else {
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
MP_OBJECT_TYPE_PATH_LU)) == NULL) {
return (EINVAL);
}
"received to disable last path. Cant disable, Sorry!"));
return (EINVAL);
}
if (vhci_mpapi_chk_last_path(pip) != 0) {
"received to disable last path. Cant disable, Sorry!"));
return (EINVAL);
}
"received to disable last path. Cant disable, Sorry!"));
} else {
}
return (rval);
}
/* ARGSUSED */
static int
{
int rval = 0;
/* validate mpioc */
" vhci_mpapi_validate() Returned %x: INVALID DATA", rval));
"vhci_mpapi_copyout_iocdata FAILED in EINVAL"));
}
return (rval);
" vhci_mpapi_validate() Returned %x: NO CREDS", rval));
"vhci_mpapi_copyout_iocdata FAILED in EPERM"));
}
return (rval);
/* Process good cases & also cases where we need to get correct alen */
/* allocate an input buffer */
KM_SLEEP);
"OID = %lx w/ mpioc = %p mp_cmd = %x\n",
}
}
}
if (vhci_mpapi_sync_lu_oid_list(vhci) != 0) {
"vhci_mpapi_sync_lu_oid_list() failed"));
}
/* process ioctls */
case MP_GET_DRIVER_PROP:
break;
case MP_GET_DEV_PROD_LIST:
break;
case MP_GET_DEV_PROD_PROP:
break;
case MP_GET_LU_LIST:
break;
case MP_GET_LU_LIST_FROM_TPG:
break;
case MP_GET_TPG_LIST_FOR_LU:
break;
case MP_GET_LU_PROP:
break;
break;
break;
break;
case MP_GET_PATH_PROP:
break;
case MP_GET_INIT_PORT_LIST: /* Not Required */
break;
case MP_GET_INIT_PORT_PROP:
break;
case MP_GET_TARGET_PORT_PROP:
break;
case MP_GET_TPG_LIST: /* Not Required */
break;
case MP_GET_TPG_PROP:
break;
break;
case MP_SET_TPG_ACCESS_STATE:
break;
case MP_ASSIGN_LU_TO_TPG:
break;
break;
break;
case MP_ENABLE_AUTO_FAILBACK:
break;
case MP_DISABLE_AUTO_FAILBACK:
break;
case MP_ENABLE_PATH:
break;
case MP_DISABLE_PATH:
break;
case MP_SEND_SCSI_CMD:
break;
default:
break;
}
"mp_obuf = %p, mp_olen = %lx, mp_alen = %lx, mp_errno = %x, "
"vhci_mpapi_copyout_iocdata FAILED"));
}
if (input_data) {
}
if (output_data) {
}
return (rval);
}
/* ARGSUSED */
int
{
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.
*/
for (i = 0; i < MP_MAX_OBJECT_TYPE; i++) {
}
/*
* Let us now allocate and initialize the drv block.
*/
drv->failbackPollingRateMax = 0;
drv->probingPollingRateMax = 0;
drv->currentProbingPollingRate = 0;
drv->autoProbingSupport = 0;
drv->autoProbingEnabled = 0;
[MP_OBJECT_TYPE_PLUGIN], ilist) != 0) {
"vhci_mpapi_create_add_to_list() of PLUGIN failed"));
return (EFAULT);
}
return (0);
}
void
{
/* add to list */
[MP_OBJECT_TYPE_DEVICE_PRODUCT], (void *)dev_prod_list);
}
/* ARGSUSED */
static uint64_t
{
}
/* ARGSUSED */
static int
{
"NULL item passed"));
return (EFAULT);
}
"NULL hdr passed"));
return (EFAULT);
}
/*
* Check if the item is already there in the list.
* Catches duplicates while assigning TPGs.
*/
"Item already in list"));
return (1);
} else {
}
}
} else {
}
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*
{
/*
* Since the listhead is null, the search is being
* performed in implicit mode - that is to use the
* level one list.
*/
} else {
/*
* The search is being performed on a sublist within
* one of the toplevel list items. Use the listhead
* that is passed in.
*/
}
" 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) {
while (ilist) {
void *wwn = ((mpapi_initiator_data_t *)
/* Found a match */
return ((void*)ilist);
}
}
break;
while (ilist) {
/* Found a match */
return ((void*)ilist);
}
}
break;
/*
* For TPG Synthesis, Use TPG specific routines
* Use this case only for ALUA devices which give TPG ID
*/
while (ilist) {
/* Found a match */
return ((void*)ilist);
}
}
break;
return ((void *)(vhci_mpapi_match_lu
case MP_OBJECT_TYPE_PATH_LU:
return ((void *)(vhci_mpapi_match_pip
default:
/*
* This should not happen
*/
"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 *
{
int major;
int instance;
switch (obj_type) {
{
char *init_port_res;
char *interconnect;
int mp_interconnect_type, len;
int prop_not_ddi_alloced = 0;
/*
* Just make a call to keep correct Sequence count.
* Don't use the OID returned though.
*/
"initiator-interconnect-type",
&interconnect) != DDI_PROP_SUCCESS)) {
/* XXX: initiator-interconnect-type not set */
"vhci_mpapi_create_item: initiator-"
"-interconnect-type prop not found"));
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,
strlen(interconnect)) == 0) {
} else if (strncmp(interconnect,
strlen(interconnect)) == 0) {
} else if (strncmp(interconnect,
strlen(interconnect)) == 0) {
} else if (strncmp(interconnect,
strlen(interconnect)) == 0) {
} else {
}
init = kmem_zalloc(
sizeof (mpapi_initiator_data_t), KM_SLEEP);
if (prop_not_ddi_alloced != 1) {
} else {
}
if (pname) {
}
}
break;
{
char *tgt_port_res;
KM_SLEEP);
}
break;
{
char *tpg_res;
tpg = kmem_zalloc(
sizeof (mpapi_tpg_data_t), KM_SLEEP);
/*
* T10 TPG ID is a 2 byte value. Keep up with it.
*/
}
break;
{
/*
* We cant use ddi_get_instance(svl->svl_dip) at this
* point because the dip is not yet in DS_READY state.
*/
/*
* XXX: luGroupID is currently unsupported
*/
(SCSI_FAILOVER_IS_ASYM(svl) ||
}
vhci_conf_flags) ? 1 : 0);
} else if (svl->svl_lb_policy_save ==
} 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.
*/
}
/*
* Allocate header lists for cross reference
*/
}
break;
case MP_OBJECT_TYPE_PATH_LU:
{
path = kmem_zalloc(
sizeof (mpapi_path_data_t), KM_SLEEP);
/* XXX: target-port prop not found */
"create_item: mdi_prop_lookup_string() "
"returned failure; "));
}
(mdi_pi_get_client(pip));
}
break;
" DEVICE PRODUCT not handled here."));
break;
default:
/*
* This should not happen
*/
"Got Unsupported OBJECT TYPE"));
return (NULL);
}
ilist);
return (ilist);
}
/*
* Local routine to allocate mpapi list header block
*/
/* ARGSUSED */
static mpapi_list_header_t *
{
return (lh);
}
/*
* Routine to create Level 1 mpapi_private data structure and also
* establish cross references between the resources being managed
*/
/* ARGSUSED */
void
{
int path_class_not_mdi_alloced = 0;
/*
* Check that the lun is not a TPGS device
* TPGS devices create the same information in another routine.
*/
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
*/
MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
/* Need to create lu_list entry */
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.
*/
}
/*
* Find out the "path-class" property on the pip
*/
!= DDI_PROP_SUCCESS) {
/* XXX: path-class prop not found */
"mdi_prop_lookup_string() returned failure; "
"Hence path_class = NONE"));
}
/*
* Build Path LU list
*/
MP_OBJECT_TYPE_PATH_LU, (void*)pip);
/* Need to create path_list entry */
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.
*/
}
if (MDI_PI_IS_ONLINE(pip)) {
} else if (MDI_PI_IS_STANDBY(pip)) {
} else {
}
/*
* Build Initiator Port list
*/
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init);
/*
* 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.
*/
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
} else {
}
/*
* 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) != DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
"mdi_prop_lookup_string() returned failure; "
"Hence tmp_wwn = %p", (void *)tmp_wwn));
}
MP_OBJECT_TYPE_TARGET_PORT, (void*)tmp_wwn);
/* Need to create tgt_list entry */
MP_OBJECT_TYPE_TARGET_PORT, (void*)tmp_wwn);
} else {
}
/*
* 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.
*/
KM_SLEEP);
}
KM_SLEEP);
}
sizeof (mpapi_item_list_t), KM_SLEEP);
}
/*
* Level-1: Fill-out Path Properties now, since we got all details.
* Actually, It is a structure copy, rather than just filling details.
*/
sizeof (struct mp_logical_unit_prop));
sizeof (struct mp_init_port_prop));
sizeof (struct mp_target_port_prop));
if (path_class_not_mdi_alloced == 1) {
}
}
/*
* 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 *
{
"%p, acc_state=%x, pclass=%s, tp=%s\n",
if ((tpgdata) &&
return (tpglist);
} else {
}
}
}
if (this_lulist != NULL) {
->tpg_list;
while (this_tpglist != NULL) {
if ((this_tpgdata) &&
return (this_tpglist);
} else {
}
}
}
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 */
{
if (this_lulist != NULL) {
->tpg_list;
while (this_tpglist != NULL) {
if ((this_tpgdata) &&
return (this_tpglist);
}
}
}
"NULL"));
return (NULL);
}
/*
* Routine to search a Target Port in a TPG
*/
/* ARGSUSED */
static int
{
if (tpgdata) {
} else {
return (0);
}
/* Found a match */
return (1);
}
}
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
{
int path_class_not_mdi_alloced = 0;
/*
* Build Target Port Group list
* Start by finding out the affected Target Port.
*/
&tmp_wwn) != DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
"mdi_prop_lookup_string() returned failure; "
"Hence tmp_wwn = %p", (void *)tmp_wwn));
}
/*
* Finding out the "path-class" property
*/
!= DDI_PROP_SUCCESS) {
/* XXX: path-class prop not found */
"mdi_prop_lookup_string() returned failure; "
"Hence path_class = NONE"));
}
/*
* Check the vlun's accessState through pip; we'll use it later.
*/
if (MDI_PI_IS_ONLINE(pip)) {
} else if (MDI_PI_IS_STANDBY(pip)) {
} else {
"Unknown pip state seen in TPG synthesis"));
}
"vlun=%s, acc_state=%x, path_class=%s, tp=%s\n",
/*
* Create Level 1 and Level 2 data structures for type
*/
/*
* 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.
*/
(void *)tmp_wwn);
MP_OBJECT_TYPE_TARGET_PORT_GROUP, (void *)tmp_wwn);
} else {
}
}
/*
* Level 2, Lun Cross referencing to TPG.
*/
KM_SLEEP);
MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun);
}
/*
* Level 2, Target Port Cross referencing to TPG.
*/
KM_SLEEP);
MP_OBJECT_TYPE_TARGET_PORT, (void *)tmp_wwn);
}
/*
* Level 2, TPG Cross referencing to Lun.
*/
if (lu_tpg_list == NULL) {
KM_SLEEP);
(void) vhci_mpapi_add_to_list(((mpapi_lu_data_t *)
}
/*
* 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
*/
}
if (path_class_not_mdi_alloced == 1) {
}
}
/*
* 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
int rel_tgt_port)
{
struct scsi_vhci_lun *vlun;
int i, rel_tport_cnt;
/*
* Find out the TPG ID (resource ptr for TPG is T10 TPG ID)
*/
/*
* Check the TPG's accessState; we'll use it later.
*/
if (as == STD_ACTIVE_OPTIMIZED) {
} else if (as == STD_ACTIVE_NONOPTIMIZED) {
} else if (as == STD_STANDBY) {
} else {
"UNAVAILABLE accessState seen in ALUA TPG setup"));
}
/*
* The scsi_address passed is associated with a scsi_vhci allocated
* scsi_device structure for a pathinfo node. Getting the vlun from
* this is a bit complicated.
*/
/*
* It is possable for this code to be called without the sd_pathinfo
* being set. This may happen as part of a probe to see if a device
* should be mapped under mdi. At this point we know enough to answer
* correctly so we can return.
*/
return;
/*
* Now get the vhci ptr using the walker
*/
"(vlun)wwn=(%p)%s, pip=%p, ap=%p, ptr=%p, as=%x, tpg_id=%s, 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
*/
MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
/* Need to create lu_list entry */
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.
*/
}
/*
* Build Path LU list
*/
MP_OBJECT_TYPE_PATH_LU, (void*)pip);
/* Need to create path_list entry */
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.
*/
}
if (MDI_PI_IS_ONLINE(pip)) {
} else if (MDI_PI_IS_STANDBY(pip)) {
} else {
}
/*
* Build Initiator Port list
*/
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init);
/*
* 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.
*/
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
} else {
}
/*
* 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.
*/
KM_SLEEP);
}
KM_SLEEP);
}
/*
* Create Level 1 & Level 2 data structures
* Parse REPORT_TARGET_PORT_GROUP data & update mpapi database.
*/
}
/*
* Set explicitFailover for TPG -
* based on tpgs_bits setting in Std Inquiry response.
*/
case TPGS_FAILOVER_EXPLICIT:
case TPGS_FAILOVER_BOTH:
break;
case TPGS_FAILOVER_IMPLICIT:
break;
default:
return;
}
/*
* Level 2, Lun Cross referencing to TPG.
*/
KM_SLEEP);
MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun);
}
/*
* Level 2, TPG Cross referencing to Lun.
*/
MP_OBJECT_TYPE_TARGET_PORT_GROUP, &tpg_id) == 0) {
KM_SLEEP);
(void) vhci_mpapi_add_to_list(((mpapi_lu_data_t *)
}
/*
* 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) != DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
"mdi_prop_lookup_string() returned failure; "
"Hence tgt_port = %p", (void *)tgt_port));
}
/*
* Level 1, Relative Target Port + Target Port Creation
*/
ptr += 8;
for (i = 0; i < rel_tport_cnt; i++) {
rel_tid = 0;
if (rel_tid != rel_tgt_port) {
ptr += 4;
continue;
}
/* Need to create tgt_list entry */
(void *)tgt_port);
} else {
}
KM_SLEEP);
}
(sizeof (mpapi_item_list_t), KM_SLEEP);
(void) vhci_mpapi_add_to_list(tpg_data->
}
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.
*/
sizeof (struct mp_logical_unit_prop));
sizeof (struct mp_init_port_prop));
sizeof (struct mp_target_port_prop));
}
/*
* Routine to get mpapi ioctl argument structure from userland.
*/
/* ARGSUSED */
static int
{
int retval = 0;
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
"Case DDI_MODEL_ILP32"));
sizeof (mp_iocdata32_t), mode)) {
"ddi_copyin() FAILED"));
break;
}
break;
}
case DDI_MODEL_NONE:
break;
}
break;
default:
break;
}
break;
}
#else /* _MULTI_DATAMODEL */
}
#endif /* _MULTI_DATAMODEL */
if (retval) {
}
return (retval);
}
/* ARGSUSED */
static int
vhci_is_model_type32(int mode)
{
#ifdef _MULTI_DATAMODEL
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
{
int rval = 0;
if (vhci_is_model_type32(mode)) {
(sizeof (mp_iocdata32_t), KM_SLEEP);
!= 0) {
}
} else {
/* 64-bit ddicopyout */
!= 0) {
}
}
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
{
int rval = 0;
}
}
}
}
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
{
int init_not_ddi_alloced = 0;
char *init, *init_port_res;
SCSI_ADDR_PROP_INITIATOR_PORT " prop not found"));
init_not_ddi_alloced = 1;
}
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init_port_res);
/*
* 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.
*/
MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
}
if (init_not_ddi_alloced == 1) {
} else if (init) {
}
return (DDI_WALK_CONTINUE);
}
/* ARGSUSED */
static void
{
KM_SLEEP) != DDI_SUCCESS) {
goto alloc_failed;
}
goto error;
}
return;
"Unable to send sysevent"));
}
/* ARGSUSED */
void
{
struct scsi_vhci_lun *svl;
} else {
"pip(%p) not found", (void *)pip));
return;
}
/*
* Check if the pathinfo is uninitialized(destroyed).
*/
if (state == MP_DRVR_PATH_STATE_UNINIT) {
"path(pip: %p) is uninited(destroyed).",
(void *)pip));
} else {
}
/*
* Find if there are any paths at all to the lun
*/
MP_DRVR_PATH_STATE_PATH_ERR) || (state ==
MP_DRVR_PATH_STATE_LU_ERR) || (state ==
"path(pip: %p) is not okay state. Set to invalid.",
(void *)pip));
/*
* 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
*/
svl);
}
/*
* Following means the lun is offline
*/
MP_OBJECT_TYPE_MULTIPATH_LU, (void *)svl);
"vhci_mpapi_set_path_state: "
}
}
}
}
/* ARGSUSED */
static mpapi_item_list_t *
void *res)
{
char *this_iport;
char *this_tport;
char *pname;
return (NULL);
}
&this_tport) != DDI_PROP_SUCCESS) {
/* XXX: target-port prop not found */
"mdi_prop_lookup_string() returned failure; "
"Hence this_tport = %p", (void *)this_tport));
}
return (ilist);
}
}
return (NULL);
}
/* ARGSUSED */
static
{
return (NULL);
}
return (ilist);
}
}
return (NULL);
}
/*
* Routine to handle TPG AccessState Change - Called after each LU failover
*/
int
{
int rval = 0;
(void *)vlun);
return (-1);
}
return (-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.
* is made, because subsequent matches also lead to the same TPG.
*/
"vhci_mpapi_update_tpg_acc_state_"
"for_ lu: Operating on LUN(%s), "
" PATH(%p), TPG(%x: %s)\n",
break;
} else if (MDI_PI_IS_STANDBY(
break;
} else {
}
} else {
/*
* if path is not valid any more,
* mark the associated tpg as
* unavailable.
*/
}
}
}
}
return (rval);
}
int
{
struct scsi_vhci *local_vhci;
strlen("scsi_vhci")) == 0) {
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
/* ARGSUSED */
void *
{
/*
* Since the listhead is null, the search is being
* performed in implicit mode - that is to use the
* level one list.
*/
->head;
} else {
/*
* The search is being performed on a sublist within
* one of the toplevel list items. Use the listhead
* that is passed in.
*/
}
" Got Target Port w/ NULL resource"));
return (NULL);
}
while (ilist) {
/* Match */
return ((void*)ilist);
} else {
}
}
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
{
return (-1);
} else {
}
return (-1);
}
if ((MDI_PI_IS_ONLINE(ret_pip) ||
MDI_PI_IS_INIT(ret_pip)) &&
!(MDI_PI_IS_DISABLE(ret_pip) ||
count++;
}
}
if (count > 1) {
return (0);
} else if (count == 1) {
return (1);
}
return (-1);
}