OpenSolaris_b135/lib/mpapi/libmpscsi_vhci/common/mp_utils.c

/*
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "mp_utils.h"
#include <sys/sunddi.h>

#ifndef OIDLIST
#define	OIDLIST "oid"
#endif


/* Remove these 5 when this source can compile with sunddi.h */
#ifndef EC_DDI
#define	EC_DDI				"EC_ddi"
#endif

#ifndef ESC_DDI_INITIATOR_REGISTER
#define	ESC_DDI_INITIATOR_REGISTER	"ESC_ddi_initiator_register"
#endif

#ifndef ESC_DDI_INITIATOR_UNREGISTER
#define	ESC_DDI_INITIATOR_UNREGISTER	"ESC_ddi_initiator_unregister"
#endif

#ifndef DDI_DRIVER_MAJOR
#define	DDI_DRIVER_MAJOR		"ddi.major"
#endif

#ifndef DDI_INSTANCE
#define	DDI_INSTANCE			"ddi.instance"
#endif


#define	VISA_CHANGE 1
#define	PROP_CHANGE 2



MP_STATUS
getStatus4ErrorCode(int driverError)
{
	MP_STATUS mpStatus = MP_STATUS_FAILED;

	log(LOG_INFO, "getStatus4ErrorCode()", "- enter");

	switch (driverError) {

		case MP_DRVR_INVALID_ID:
			log(LOG_INFO, "getStatus4ErrorCode()",
				" received mp_errno=MP_DRVR_INVALID_ID"
				" from driver call.");
			log(LOG_INFO, "getStatus4ErrorCode()",
				" returning MP_STATUS_OBJECT_NOT_FOUND"
				" to caller.");
			mpStatus = MP_STATUS_OBJECT_NOT_FOUND;
			break;


		case MP_DRVR_ID_OBSOLETE:
			log(LOG_INFO, "getStatus4ErrorCode()",
				" received mp_errno=MP_DRVR_ID_OBSOLETE"
				" from driver call.");
			log(LOG_INFO, "getStatus4ErrorCode()",
				" returning MP_STATUS_OBJECT_NOT_FOUND"
				" to caller.");
			mpStatus = MP_STATUS_OBJECT_NOT_FOUND;
			break;


		case MP_DRVR_ACCESS_SYMMETRIC:
			log(LOG_INFO, "getStatus4ErrorCode()",
				" received mp_errno=MP_DRVR_ACCESS_SYMMETRIC"
				" from driver call.");
			log(LOG_INFO, "getStatus4ErrorCode()",
				" returning MP_STATUS_INVALID_PARAMETER"
				" to caller.");
			mpStatus = MP_STATUS_INVALID_PARAMETER;
			break;


		case MP_DRVR_PATH_UNAVAILABLE:
			log(LOG_INFO, "getStatus4ErrorCode()",
				" received mp_errno=MP_DRVR_PATH_UNAVAILABLE"
				" from driver call.");
			log(LOG_INFO, "getStatus4ErrorCode()",
				" returning MP_STATUS_PATH_NONOPERATIONAL"
				" to caller.");
			mpStatus = MP_STATUS_PATH_NONOPERATIONAL;
			break;


		case MP_DRVR_IDS_NOT_ASSOCIATED:
			log(LOG_INFO, "getStatus4ErrorCode()",
				" received mp_errno=MP_DRVR_IDS_NOT_ASSOCIATED"
				" from driver call.");
			log(LOG_INFO, "getStatus4ErrorCode()",
				" returning MP_STATUS_INVALID_PARAMETER"
				" to caller.");
			mpStatus = MP_STATUS_INVALID_PARAMETER;
			break;


		case MP_DRVR_ILLEGAL_ACCESS_STATE_REQUEST:
			log(LOG_INFO, "getStatus4ErrorCode()",
				" received mp_errno="
				"MP_DRVR_ILLEGAL_ACCESS_STATE_REQUEST"
				" from driver call.");
			log(LOG_INFO, "getStatus4ErrorCode()",
				" returning MP_STATUS_INVALID_PARAMETER"
				" to caller.");
			mpStatus = MP_STATUS_ACCESS_STATE_INVALID;
			break;


		default:
			log(LOG_INFO, "getStatus4ErrorCode()",
				" - received (unsupported) mp_errno=%d from"
				" driver call.", driverError);
			log(LOG_INFO, "getStatus4ErrorCode()",
				" - returning MP_STATUS_FAILED to caller.");
			mpStatus = MP_STATUS_FAILED;
	}

	log(LOG_INFO, "getStatus4ErrorCode()", "- exit");

	return (mpStatus);
}



MP_OID_LIST
*createOidList(int size) {

	MP_OID_LIST *pOidList = NULL;


	log(LOG_INFO, "createOidList()", "- enter");


	if (size < 1) {

		log(LOG_INFO, "createOidList()",
			"requested size is less than 1");
		log(LOG_INFO, "createOidList()",
			" - error exit");
		return (NULL);

	} else {

		pOidList = (MP_OID_LIST*)calloc(1,
			sizeof (MP_OID_LIST) +
			((size - 1) *
		    sizeof (MP_OID)));

		if (NULL == pOidList) {
			log(LOG_INFO, "createOidList()",
				"no memory for pOidList");
			log(LOG_INFO, "createOidList()",
				" - error exit");
			return (NULL);
		}

		log(LOG_INFO,
		    "createOidList()",
			"- exit(%d)",
			size);

		return (pOidList);
	}
}

/* Calls the client callback function, if one is registered */
static void
notifyClient(sysevent_t *ev)
{
	nvlist_t *attr_list = NULL;

	uint64_t *val = NULL;
	int32_t  *instance = NULL;
	int32_t  *major = NULL;

	int valAllocated = 0;

	uint_t nelem = 0;

	int i = 0;
	int eventType = 0;
	int index = -1;

	void *pCallerData = NULL;

	char subClassName[256];

	MP_BOOL becomingVisible = MP_FALSE;

	MP_OID_LIST *oidList = NULL;


	log(LOG_INFO, "notifyClient()", "- enter");


	(void) strncpy(subClassName, sysevent_get_subclass_name(ev), 256);

	if (strstr(subClassName, "change")) {

		eventType = PROP_CHANGE;

		log(LOG_INFO, "notifyClient()", "- got a change event");
		log(LOG_INFO, "notifyClient()", ": [%s]",
		    subClassName);

		if (strncmp(subClassName, ESC_SUN_MP_LU_CHANGE, 255) == 0) {

			index = MP_OBJECT_TYPE_MULTIPATH_LU;

		} else if (strncmp(subClassName, ESC_SUN_MP_PATH_CHANGE, 255)
		    == 0) {

			index = MP_OBJECT_TYPE_PATH_LU;

		} else if (strncmp(subClassName, ESC_SUN_MP_INIT_PORT_CHANGE,
					255) == 0) {

			index = MP_OBJECT_TYPE_INITIATOR_PORT;

		} else if (strncmp(subClassName, ESC_SUN_MP_TPG_CHANGE, 255)
		    == 0) {

			index = MP_OBJECT_TYPE_TARGET_PORT_GROUP;

		} else if (strncmp(subClassName, ESC_SUN_MP_TARGET_PORT_CHANGE,
					255) == 0) {

			index = MP_OBJECT_TYPE_TARGET_PORT;

		} else if (strncmp(subClassName, ESC_SUN_MP_DEV_PROD_CHANGE,
					255) == 0) {

			index = MP_OBJECT_TYPE_DEVICE_PRODUCT;
		}

	} else if ((strstr(subClassName, "add")) ||
			(strstr(subClassName, "initiator_register"))) {

		eventType = VISA_CHANGE;
		becomingVisible = MP_TRUE;

		log(LOG_INFO, "notifyClient()", "- got a visibility"
		    " add event");
		log(LOG_INFO, "notifyClient()", ": [%s]",
		    subClassName);

		if (strncmp(subClassName, ESC_DEVFS_DEVI_ADD, 255) == 0) {

			index = MP_OBJECT_TYPE_MULTIPATH_LU;

		} else if (strncmp(subClassName, ESC_SUN_MP_PATH_ADD, 255)
		    == 0) {

			index = MP_OBJECT_TYPE_PATH_LU;

		} else if (strncmp(subClassName, ESC_DDI_INITIATOR_REGISTER,
					244) == 0) {

			index = MP_OBJECT_TYPE_INITIATOR_PORT;

		} else if (strncmp(subClassName, ESC_SUN_MP_TPG_ADD,
					255) == 0) {

			index = MP_OBJECT_TYPE_TARGET_PORT_GROUP;

		} else if (strncmp(subClassName, ESC_SUN_MP_TARGET_PORT_ADD,
					255) == 0) {

			index = MP_OBJECT_TYPE_TARGET_PORT;

		} else if (strncmp(subClassName, ESC_SUN_MP_DEV_PROD_ADD, 255)
		    == 0) {

			index = MP_OBJECT_TYPE_DEVICE_PRODUCT;
		}


	} else if ((strstr(subClassName, "remove")) ||
			(strstr(subClassName, "initiator_unregister"))) {

		eventType = VISA_CHANGE;
		becomingVisible = MP_FALSE;

		log(LOG_INFO, "notifyClient()", "- got a visibility"
		    " remove event");
		log(LOG_INFO, "notifyClient()", ": [%s]",
		    subClassName);

		if (strncmp(subClassName, ESC_DEVFS_DEVI_REMOVE, 255) == 0) {

			index = MP_OBJECT_TYPE_MULTIPATH_LU;

		} else if (strncmp(subClassName, ESC_SUN_MP_PATH_REMOVE, 255)
		    == 0) {

			index = MP_OBJECT_TYPE_PATH_LU;

		} else if (strncmp(subClassName, ESC_DDI_INITIATOR_UNREGISTER,
					255) == 0) {

			index = MP_OBJECT_TYPE_INITIATOR_PORT;

		} else if (strncmp(subClassName, ESC_SUN_MP_TPG_REMOVE, 255)
		    == 0) {

			index = MP_OBJECT_TYPE_TARGET_PORT_GROUP;

		} else if (strncmp(subClassName, ESC_SUN_MP_TARGET_PORT_REMOVE,
					255) == 0) {

			index = MP_OBJECT_TYPE_TARGET_PORT;

		} else if (strncmp(subClassName, ESC_SUN_MP_DEV_PROD_REMOVE,
					255) == 0) {

			index = MP_OBJECT_TYPE_DEVICE_PRODUCT;
		}


	} else {
		log(LOG_INFO, "notifyClient()", "- got an unsupported event");
		return;
	}

	if (index < 0) {

		log(LOG_INFO, "notifyClient()", "- index is less than zero");
		return;
	}

	if (eventType == VISA_CHANGE) {

		(void) pthread_mutex_lock(&g_visa_mutex);

		if (NULL == g_Visibility_Callback_List[index].pClientFn) {

			log(LOG_INFO, "notifyClient()",
			    "- no visibility change callback to notify");

			(void) pthread_mutex_unlock(&g_visa_mutex);

			return;
		}

		(void) pthread_mutex_unlock(&g_visa_mutex);
	}

	if (eventType == PROP_CHANGE) {

		(void) pthread_mutex_lock(&g_prop_mutex);

		if (NULL == g_Property_Callback_List[index].pClientFn) {

			log(LOG_INFO, "notifyClient()",
			    "- no property change callback to notify");

			    (void) pthread_mutex_unlock(&g_prop_mutex);

			    return;
		    }

		    (void) pthread_mutex_unlock(&g_prop_mutex);
	}

	(void) sysevent_get_attr_list(ev, &attr_list);
	if (NULL != attr_list) {

		if ((VISA_CHANGE == eventType) &&
		    (MP_OBJECT_TYPE_MULTIPATH_LU == index)) {

			(void) nvlist_lookup_int32_array(attr_list,
			    DEVFS_INSTANCE, &instance, &nelem);

			log(LOG_INFO, "notifyClient()",
			    "- event has [%d] elements",
			    nelem);

			if (NULL != instance) {

				val = (uint64_t *)malloc(sizeof (uint64_t));

				valAllocated = 1;

				*val = *instance;
				nelem = 1;

			} else {

				nelem = 0;
			}

		} else if ((VISA_CHANGE == eventType) &&
			(MP_OBJECT_TYPE_INITIATOR_PORT == index)) {

			(void) nvlist_lookup_int32_array(attr_list,
			    DDI_INSTANCE, &instance, &nelem);

			log(LOG_INFO, "notifyClient()",
			    "- event (PHCI_INSTANCE) has [%d] elements",
			    nelem);

			(void) nvlist_lookup_int32_array(attr_list,
			    DDI_DRIVER_MAJOR, &major, &nelem);

			log(LOG_INFO, "notifyClient()",
			    "- event (PHCI_DRIVER_MAJOR) has [%d] elements",
			    nelem);

			if ((NULL != instance) & (NULL != major)) {

				val = (uint64_t *)malloc(sizeof (uint64_t));

				valAllocated = 1;

				*val = MP_STORE_INST_TO_ID(*instance, *val);
				*val = MP_STORE_MAJOR_TO_ID(*major, *val);

				nelem = 1;

			} else {

				nelem = 0;
			}

		} else {

			(void) nvlist_lookup_uint64_array(attr_list, OIDLIST,
					    &val, &nelem);

			log(LOG_INFO, "notifyClient()",
			    "- event has [%d] elements",
			    nelem);
		}

		if (nelem > 0) {

			for (i = 0; i < nelem; i++) {

				log(LOG_INFO, "notifyClient()",
				    "- event [%d] = %llx",
				    i, val[i]);
			}

			oidList = createOidList(nelem);
			if (NULL == oidList) {

				log(LOG_INFO, "notifyClient()",
				    "- unable to create MP_OID_LIST");

				log(LOG_INFO, "notifyClient()",
				    "- error exit");

				nvlist_free(attr_list);

				return;
			}

			oidList->oidCount = nelem;

			for (i = 0; i < nelem; i++) {

				oidList->oids[i].objectType = index;
				oidList->oids[i].ownerId = g_pluginOwnerID;
				oidList->oids[i].objectSequenceNumber = val[i];
			}

			if (valAllocated) {

				free(val);
			}

			for (i = 0; i < oidList->oidCount; i++) {

				log(LOG_INFO, "notifyClient()",
					"oidList->oids[%d].objectType"
					"           = %d",
					i, oidList->oids[i].objectType);
				log(LOG_INFO, "notifyClient()",
					"oidList->oids[%d].ownerId"
					"              = %d",
					i, oidList->oids[i].ownerId);
				log(LOG_INFO, "notifyClient()",
					"oidList->oids[%d].objectSequenceNumber"
					" = %llx",
					i,
					oidList->oids[i].objectSequenceNumber);
			}

			if (eventType == PROP_CHANGE) {

				(void) pthread_mutex_lock(&g_prop_mutex);

				pCallerData = g_Property_Callback_List[index].
					pCallerData;

				(g_Property_Callback_List[index].pClientFn)
				(oidList, pCallerData);

				(void) pthread_mutex_unlock(&g_prop_mutex);

			} else if (eventType == VISA_CHANGE) {

				(void) pthread_mutex_lock(&g_visa_mutex);

				pCallerData = g_Visibility_Callback_List[index].
					pCallerData;

				(g_Visibility_Callback_List[index].pClientFn)
				(becomingVisible, oidList, pCallerData);

				(void) pthread_mutex_unlock(&g_visa_mutex);

			}
		}

		nvlist_free(attr_list);
	}


	log(LOG_INFO, "notifyClient()", "- exit");
}

/* Event handler called by system */
static void
sysevent_handler(sysevent_t *ev)
{
	log(LOG_INFO, "sysevent_handler()", "- enter");

	/* Is the event one of ours? */
	if ((strncmp(EC_SUN_MP, sysevent_get_class_name(ev), 9) != 0) &&
	    (strncmp(EC_DEVFS,  sysevent_get_class_name(ev), 8) != 0) &&
	    (strncmp(EC_DDI,    sysevent_get_class_name(ev), 6) != 0)) {

		return;
	}

	/* Notify client if it cares */
	notifyClient(ev);


	log(LOG_INFO, "sysevent_handler()", "- exit");
}

/* Registers the plugin to the sysevent framework */
MP_STATUS init_sysevents() {

	const char *subclass_list[] = {

		ESC_SUN_MP_LU_CHANGE,

		ESC_SUN_MP_PATH_CHANGE,
		ESC_SUN_MP_PATH_ADD,
		ESC_SUN_MP_PATH_REMOVE,

		ESC_SUN_MP_INIT_PORT_CHANGE,

		ESC_SUN_MP_TPG_CHANGE,
		ESC_SUN_MP_TPG_ADD,
		ESC_SUN_MP_TPG_REMOVE,

		ESC_SUN_MP_TARGET_PORT_CHANGE,
		ESC_SUN_MP_TARGET_PORT_ADD,
		ESC_SUN_MP_TARGET_PORT_REMOVE,

		ESC_SUN_MP_DEV_PROD_CHANGE,
		ESC_SUN_MP_DEV_PROD_ADD,
		ESC_SUN_MP_DEV_PROD_REMOVE

	};

	const char *lu_subclass_list[] = {

		ESC_DEVFS_DEVI_ADD,
		ESC_DEVFS_DEVI_REMOVE

	};

	const char *init_port_subclass_list[] = {

		ESC_DDI_INITIATOR_REGISTER,
		ESC_DDI_INITIATOR_UNREGISTER
	};



	log(LOG_INFO, "init_sysevents()", "- enter");


	g_SysEventHandle = sysevent_bind_handle(sysevent_handler);
	if (g_SysEventHandle == NULL) {

		log(LOG_INFO, "init_sysevents()",
		    "- sysevent_bind_handle() failed");

		log(LOG_INFO, "init_sysevents()", "- error exit");

		return (MP_STATUS_FAILED);
	}

	if (sysevent_subscribe_event(g_SysEventHandle, EC_SUN_MP,
					subclass_list, 14) != 0) {


		log(LOG_INFO, "init_sysevents()",
		    "- sysevent_subscribe_event() failed for subclass_list");

		log(LOG_INFO, "init_sysevents()", "- error exit");

		sysevent_unbind_handle(g_SysEventHandle);

		return (MP_STATUS_FAILED);
	}

	if (sysevent_subscribe_event(g_SysEventHandle, EC_DEVFS,
					lu_subclass_list, 2) != 0) {


		log(LOG_INFO, "init_sysevents()",
		    "- sysevent_subscribe_event() failed for lu_subclass_list");

		log(LOG_INFO, "init_sysevents()", "- error exit");

		sysevent_unbind_handle(g_SysEventHandle);

		return (MP_STATUS_FAILED);
	}

	if (sysevent_subscribe_event(g_SysEventHandle, EC_DDI,
					init_port_subclass_list, 2) != 0) {


		log(LOG_INFO, "init_sysevents()",
		    "- sysevent_subscribe_event() failed "
		    "for init_port_subclass_list");

		log(LOG_INFO, "init_sysevents()", "- error exit");

		sysevent_unbind_handle(g_SysEventHandle);

		return (MP_STATUS_FAILED);
	}


	log(LOG_INFO, "init_sysevents()", "- exit");

	return (MP_STATUS_SUCCESS);
}