14bac2208Snarayan /*
24bac2208Snarayan  * CDDL HEADER START
34bac2208Snarayan  *
44bac2208Snarayan  * The contents of this file are subject to the terms of the
54bac2208Snarayan  * Common Development and Distribution License (the "License").
64bac2208Snarayan  * You may not use this file except in compliance with the License.
74bac2208Snarayan  *
84bac2208Snarayan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94bac2208Snarayan  * or http://www.opensolaris.org/os/licensing.
104bac2208Snarayan  * See the License for the specific language governing permissions
114bac2208Snarayan  * and limitations under the License.
124bac2208Snarayan  *
134bac2208Snarayan  * When distributing Covered Code, include this CDDL HEADER in each
144bac2208Snarayan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154bac2208Snarayan  * If applicable, add the following below this CDDL HEADER, with the
164bac2208Snarayan  * fields enclosed by brackets "[]" replaced with your own identifying
174bac2208Snarayan  * information: Portions Copyright [yyyy] [name of copyright owner]
184bac2208Snarayan  *
194bac2208Snarayan  * CDDL HEADER END
204bac2208Snarayan  */
214bac2208Snarayan /*
22*94048829Sranenc  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
234bac2208Snarayan  * Use is subject to license terms.
244bac2208Snarayan  */
254bac2208Snarayan 
264bac2208Snarayan /*
274bac2208Snarayan  * Implements auxiliary routines declared in pcp_utils.h to facilitate
284bac2208Snarayan  * finding the appropriate communication transport & device path for a
294bac2208Snarayan  * given service.  This supports the transition from the legacy service channel
304bac2208Snarayan  * transport (glvc) to the logical domain channel (vldc) transport native
314bac2208Snarayan  * to platforms running Logical Domains (LDoms).
324bac2208Snarayan  */
334bac2208Snarayan 
344bac2208Snarayan #pragma ident	"%Z%%M%	%I%	%E% SMI"
354bac2208Snarayan 
364bac2208Snarayan #include <fcntl.h>
374bac2208Snarayan #include <sys/types.h>
384bac2208Snarayan #include <sys/stat.h>
394bac2208Snarayan #include <strings.h>
404bac2208Snarayan #include <stdlib.h>
414bac2208Snarayan #include <libgen.h>
424bac2208Snarayan #include <unistd.h>
434bac2208Snarayan #include <stdio.h>
444bac2208Snarayan #include <libdevinfo.h>
454bac2208Snarayan 
464bac2208Snarayan #include "pcp_utils.h"
474bac2208Snarayan 
484bac2208Snarayan typedef enum { false = 0, true = 1 } bool_t;
494bac2208Snarayan 
504bac2208Snarayan #define	SERVICE_PREFIX	"SUNW,sun4v-"
514bac2208Snarayan #define	DEVICES_DIR "/devices"
524bac2208Snarayan #define	GLVC	":glvc"
534bac2208Snarayan #define	VCHAN	"virtual-channel@"
544bac2208Snarayan #define	VCHAN_C	"virtual-channel-client@"
554bac2208Snarayan 
564bac2208Snarayan /*
574bac2208Snarayan  * The mechanism to relate a service to a device path is different for
584bac2208Snarayan  * vldc and glvc, due to the way the device pathnames are encoded:
594bac2208Snarayan  * Sample service: sunvts
604bac2208Snarayan  * Service Name: SUNW,sun4v-sunvts
614bac2208Snarayan  * GLVC device path:
624bac2208Snarayan  * "/devices/virtual-devices@100/sunvts@a:glvc"
634bac2208Snarayan  * VLDC device path:
644bac2208Snarayan  * "/devices/virtual-devices@100/channel-devices@200/virtual-channel@3:sunvts"
654bac2208Snarayan  *
664bac2208Snarayan  * As VLDC is the communication mechanism used in an LDoms environment, it is
674bac2208Snarayan  * the preferred channel, and its existence is checked for first.
684bac2208Snarayan  */
694bac2208Snarayan 
704bac2208Snarayan /*
714bac2208Snarayan  * Extract from dev_path the "service name" portion.
724bac2208Snarayan  * For vldc, the service corresponds to the minor name of the device path
734bac2208Snarayan  * (e.g. virtual-channel@3:sunvts for sunvts).  If service is non-NULL, it must
744bac2208Snarayan  * match the extracted service name for the function to succeed.
754bac2208Snarayan  * The service name is returned in match (if non-NULL), and the function
764bac2208Snarayan  * itself returns true on success; false on failure.
774bac2208Snarayan  */
784bac2208Snarayan static bool_t
794bac2208Snarayan get_vldc_svc_name(char *dev_path, char *service, char **match)
804bac2208Snarayan {
814bac2208Snarayan 	bool_t ret = false;
824bac2208Snarayan 	char *pathname = strdup(dev_path);
834bac2208Snarayan 	char *devname, *s;
844bac2208Snarayan 
854bac2208Snarayan 	if (NULL == pathname)
864bac2208Snarayan 		return (false);
874bac2208Snarayan 
884bac2208Snarayan 	devname = basename(pathname);
894bac2208Snarayan 	s = strrchr(devname, ':');
904bac2208Snarayan 
914bac2208Snarayan 	if (s++ == NULL) {
924bac2208Snarayan 		goto end;
934bac2208Snarayan 	}
944bac2208Snarayan 
954bac2208Snarayan 	if ((strncmp(devname, VCHAN, strlen(VCHAN)) == 0) ||
964bac2208Snarayan 	    (strncmp(devname, VCHAN_C, strlen(VCHAN_C)) == 0)) {
974bac2208Snarayan 		/*
984bac2208Snarayan 		 * If in addition, a service string is specified to
994bac2208Snarayan 		 * be matched, do a comparison
1004bac2208Snarayan 		 */
1014bac2208Snarayan 		if (service != NULL) {
1024bac2208Snarayan 			if (strcmp(s, service) == 0) {
1034bac2208Snarayan 				if (match)
1044bac2208Snarayan 					*match = strdup(s);
1054bac2208Snarayan 				ret = true;
1064bac2208Snarayan 				goto end;
1074bac2208Snarayan 			} else {
1084bac2208Snarayan 				ret = false;
1094bac2208Snarayan 				goto end;
1104bac2208Snarayan 			}
1114bac2208Snarayan 		} else if (match) {
1124bac2208Snarayan 			*match = strdup(s);
1134bac2208Snarayan 		}
1144bac2208Snarayan 
1154bac2208Snarayan 		ret = true;
1164bac2208Snarayan 		goto end;
1174bac2208Snarayan 	}
1184bac2208Snarayan end:
1194bac2208Snarayan 
1204bac2208Snarayan 	free(pathname);
1214bac2208Snarayan 	return (ret);
1224bac2208Snarayan }
1234bac2208Snarayan 
1244bac2208Snarayan /*
1254bac2208Snarayan  * Extract from dev_path the "service name" portion.
1264bac2208Snarayan  * For glvc, the service corresponds to the node name of the device path
1274bac2208Snarayan  * (e.g. sunvts@a:glvc for sunvts).  If service is non-NULL, it must
1284bac2208Snarayan  * match the extracted service name for the function to succeed.
1294bac2208Snarayan  * The service name is returned in match (if non-NULL), and the function
1304bac2208Snarayan  * itself returns true on success; false on failure.
1314bac2208Snarayan  */
1324bac2208Snarayan static bool_t
1334bac2208Snarayan get_glvc_svc_name(char *dev_path, char *service, char **match)
1344bac2208Snarayan {
1354bac2208Snarayan 	bool_t ret = true;
1364bac2208Snarayan 	char *pathname = strdup(dev_path);
1374bac2208Snarayan 	char *devname, *substr, *t;
1384bac2208Snarayan 	int len;
1394bac2208Snarayan 
1404bac2208Snarayan 	if (NULL == pathname)
1414bac2208Snarayan 		return (false);
1424bac2208Snarayan 
1434bac2208Snarayan 	devname = basename(pathname);
1444bac2208Snarayan 	substr = strstr(devname, GLVC);
1454bac2208Snarayan 
1464bac2208Snarayan 	if (!((substr != NULL) && (strcmp(substr, GLVC) == 0))) {
1474bac2208Snarayan 		ret = false;
1484bac2208Snarayan 		goto end;
1494bac2208Snarayan 	}
1504bac2208Snarayan 
1514bac2208Snarayan 	if ((t = strrchr(devname, '@')) == NULL) {
1524bac2208Snarayan 		ret = false;
1534bac2208Snarayan 		goto end;
1544bac2208Snarayan 	}
1554bac2208Snarayan 
1564bac2208Snarayan 	len = t - devname;
1574bac2208Snarayan 
1584bac2208Snarayan 	/*
1594bac2208Snarayan 	 * If a service string is specified, check if there
1604bac2208Snarayan 	 * is a match
1614bac2208Snarayan 	 */
1624bac2208Snarayan 	if ((service != NULL) && (strncmp(devname, service, len) != 0))
1634bac2208Snarayan 		ret = false;
1644bac2208Snarayan 
1654bac2208Snarayan 	if ((ret == true) && (match != NULL)) {
1664bac2208Snarayan 		*match = calloc(len + 1, 1);
1674bac2208Snarayan 		if (*match)
1684bac2208Snarayan 			(void) strncpy(*match, devname, len);
1694bac2208Snarayan 	}
1704bac2208Snarayan 
1714bac2208Snarayan end:
1724bac2208Snarayan 	free(pathname);
1734bac2208Snarayan 	return (ret);
1744bac2208Snarayan }
1754bac2208Snarayan 
1764bac2208Snarayan /*
1774bac2208Snarayan  * This routine accepts either a prefixed service name or a legacy full
1784bac2208Snarayan  * pathname (which might not even exist in the filesystem), and in either case
1794bac2208Snarayan  * returns a canonical service name.  If the parameter is neither a service
1804bac2208Snarayan  * name (i.e. with a "SUNW,sun4v-" prefix), nor a path to a legacy glvc or
1814bac2208Snarayan  * vldc device, NULL is returned.
1824bac2208Snarayan  */
1834bac2208Snarayan char *
1844bac2208Snarayan platsvc_extract_svc_name(char *devname)
1854bac2208Snarayan {
1864bac2208Snarayan 	char *sname = NULL;
1874bac2208Snarayan 	char *vldc_path, *glvc_path;
1884bac2208Snarayan 
1894bac2208Snarayan 	/*
1904bac2208Snarayan 	 * First check whether a service name
1914bac2208Snarayan 	 */
1924bac2208Snarayan 	if (strncmp(devname, SERVICE_PREFIX, strlen(SERVICE_PREFIX)) == 0) {
1934bac2208Snarayan 		sname = strdup(devname + strlen(SERVICE_PREFIX));
1944bac2208Snarayan 		return (sname);
1954bac2208Snarayan 	}
1964bac2208Snarayan 
1974bac2208Snarayan 	/*
1984bac2208Snarayan 	 * Not a service name, check if it's a valid pathname
1994bac2208Snarayan 	 */
2004bac2208Snarayan 	if (!(devname[0] == '/' || devname[0] == '.')) {
2014bac2208Snarayan 		return (NULL);
2024bac2208Snarayan 	}
2034bac2208Snarayan 
2044bac2208Snarayan 	/*
2054bac2208Snarayan 	 * Ideally, we should only check for a valid glvc pathname,
2064bac2208Snarayan 	 * requiring all vldc access to be only via service names.  But
2074bac2208Snarayan 	 * to prevent a flag day with code that's already passing in
2084bac2208Snarayan 	 * vldc full pathnames (e.g. sunMC), we allow them here.
2094bac2208Snarayan 	 */
2104bac2208Snarayan 	if (get_vldc_svc_name(devname, NULL, &vldc_path) == true) {
2114bac2208Snarayan 		return (vldc_path);
2124bac2208Snarayan 	} else if (get_glvc_svc_name(devname, NULL, &glvc_path) == true) {
2134bac2208Snarayan 		return (glvc_path);
2144bac2208Snarayan 	}
2154bac2208Snarayan 
2164bac2208Snarayan 	return (NULL);
2174bac2208Snarayan }
2184bac2208Snarayan 
2194bac2208Snarayan /*
2204bac2208Snarayan  * Walk all "service" device nodes to find the one with the
2214bac2208Snarayan  * matching glvc minor name
2224bac2208Snarayan  */
2234bac2208Snarayan static char *
2244bac2208Snarayan svc_name_to_glvc_dev_path(char *service)
2254bac2208Snarayan {
2264bac2208Snarayan 	di_node_t root_node, service_node;
2274bac2208Snarayan 	char *glvc_path;
2284bac2208Snarayan 	char *minor_name;
2294bac2208Snarayan 	di_minor_t minor;
2304bac2208Snarayan 	char *dev_path = NULL;
2314bac2208Snarayan 
2324bac2208Snarayan 	if (service == NULL)
2334bac2208Snarayan 		return (NULL);
2344bac2208Snarayan 
2353af08d82Slm 	/* Ensure that the 'glvc' driver is loaded */
236468f746cSlm 	(void) di_init_driver("glvc", DI_CACHE_SNAPSHOT_FLAGS | DINFOFORCE);
2373af08d82Slm 
2384bac2208Snarayan 	/* Get device node */
2394bac2208Snarayan 	root_node = di_init("/", DINFOCPYALL);
2404bac2208Snarayan 	if (root_node == DI_NODE_NIL) {
2414bac2208Snarayan 		return (dev_path);
2424bac2208Snarayan 	}
2434bac2208Snarayan 
2444bac2208Snarayan 	service_node = di_drv_first_node("glvc", root_node);
2454bac2208Snarayan 
2464bac2208Snarayan 	while (service_node != DI_NODE_NIL) {
2474bac2208Snarayan 		/* Make sure node name matches service name */
2484bac2208Snarayan 		if (strcmp(service, di_node_name(service_node)) == 0) {
2494bac2208Snarayan 			/* Walk minor nodes */
2504bac2208Snarayan 			minor = di_minor_next(service_node, DI_NODE_NIL);
2514bac2208Snarayan 
2524bac2208Snarayan 			while (minor != DI_NODE_NIL) {
2534bac2208Snarayan 				glvc_path = di_devfs_minor_path(minor);
2544bac2208Snarayan 				minor_name = di_minor_name(minor);
2554bac2208Snarayan 
2564bac2208Snarayan 				if (strcmp(minor_name, "glvc") == 0) {
2574bac2208Snarayan 					dev_path = malloc(strlen(glvc_path) +
2584bac2208Snarayan 					    strlen(DEVICES_DIR) + 1);
2594bac2208Snarayan 					(void) strcpy(dev_path, DEVICES_DIR);
2604bac2208Snarayan 					(void) strcat(dev_path, glvc_path);
2614bac2208Snarayan 					di_devfs_path_free(glvc_path);
2624bac2208Snarayan 					break;
2634bac2208Snarayan 				}
2644bac2208Snarayan 
2654bac2208Snarayan 				di_devfs_path_free(glvc_path);
2664bac2208Snarayan 				minor = di_minor_next(service_node, minor);
2674bac2208Snarayan 			}
2684bac2208Snarayan 		}
2694bac2208Snarayan 		if (dev_path != NULL)
2704bac2208Snarayan 			break;
2714bac2208Snarayan 
2724bac2208Snarayan 		service_node = di_drv_next_node(service_node);
2734bac2208Snarayan 	}
2744bac2208Snarayan 
2754bac2208Snarayan 	di_fini(root_node);
2764bac2208Snarayan 	return (dev_path);
2774bac2208Snarayan }
2784bac2208Snarayan 
2794bac2208Snarayan /*
2804bac2208Snarayan  * Walk all vldc device nodes to find the one with the
2814bac2208Snarayan  * matching minor name
2824bac2208Snarayan  */
2834bac2208Snarayan static char *
2844bac2208Snarayan svc_name_to_vldc_dev_path(char *service)
2854bac2208Snarayan {
2864bac2208Snarayan 	di_node_t root_node, vldc_node;
2874bac2208Snarayan 	char *vldc_path;
2884bac2208Snarayan 	char *minor_name;
2894bac2208Snarayan 	di_minor_t minor;
2904bac2208Snarayan 	char *dev_path = NULL;
2914bac2208Snarayan 
292*94048829Sranenc 	/* Ensure that the 'vldc' driver is loaded */
293*94048829Sranenc 	(void) di_init_driver("vldc", DI_CACHE_SNAPSHOT_FLAGS | DINFOFORCE);
294*94048829Sranenc 
2954bac2208Snarayan 	/* Get device node */
2964bac2208Snarayan 	root_node = di_init("/", DINFOCPYALL);
2974bac2208Snarayan 	if (root_node == DI_NODE_NIL) {
2984bac2208Snarayan 		return (dev_path);
2994bac2208Snarayan 	}
3004bac2208Snarayan 
3014bac2208Snarayan 	vldc_node = di_drv_first_node("vldc", root_node);
3024bac2208Snarayan 
3034bac2208Snarayan 	while (vldc_node != DI_NODE_NIL) {
3044bac2208Snarayan 		/* Walk minor nodes */
3054bac2208Snarayan 		minor = di_minor_next(vldc_node, DI_NODE_NIL);
3064bac2208Snarayan 
3074bac2208Snarayan 		while (minor != DI_NODE_NIL) {
3084bac2208Snarayan 			vldc_path = di_devfs_minor_path(minor);
3094bac2208Snarayan 			minor_name = di_minor_name(minor);
3104bac2208Snarayan 
3114bac2208Snarayan 			if (strcmp(minor_name, service) == 0) {
3124bac2208Snarayan 				dev_path = malloc(strlen(vldc_path) +
3134bac2208Snarayan 				    strlen(DEVICES_DIR) + 1);
3144bac2208Snarayan 				(void) strcpy(dev_path, DEVICES_DIR);
3154bac2208Snarayan 				(void) strcat(dev_path, vldc_path);
3164bac2208Snarayan 				di_devfs_path_free(vldc_path);
3174bac2208Snarayan 				break;
3184bac2208Snarayan 			}
3194bac2208Snarayan 
3204bac2208Snarayan 			di_devfs_path_free(vldc_path);
3214bac2208Snarayan 			minor = di_minor_next(vldc_node, minor);
3224bac2208Snarayan 		}
3234bac2208Snarayan 		if (dev_path != NULL)
3244bac2208Snarayan 			break;
3254bac2208Snarayan 
3264bac2208Snarayan 		vldc_node = di_drv_next_node(vldc_node);
3274bac2208Snarayan 	}
3284bac2208Snarayan 
3294bac2208Snarayan 	di_fini(root_node);
3304bac2208Snarayan 	return (dev_path);
3314bac2208Snarayan }
3324bac2208Snarayan 
3334bac2208Snarayan /*
3344bac2208Snarayan  * Given a service name or a full legacy pathname, return
3354bac2208Snarayan  * the full pathname to the appropriate vldc or glvc device.
3364bac2208Snarayan  */
3374bac2208Snarayan char *
3384bac2208Snarayan platsvc_name_to_path(char *svc_or_path, pcp_xport_t *type)
3394bac2208Snarayan {
3404bac2208Snarayan 	char		*pathn_p;
3414bac2208Snarayan 	char		*service;
3424bac2208Snarayan 
3434bac2208Snarayan 	if ((service = platsvc_extract_svc_name(svc_or_path)) == NULL)
3444bac2208Snarayan 		return (NULL);
3454bac2208Snarayan 
3464bac2208Snarayan 	/*
3474bac2208Snarayan 	 * First lookup vldc nodes
3484bac2208Snarayan 	 */
3494bac2208Snarayan 	pathn_p = svc_name_to_vldc_dev_path(service);
3504bac2208Snarayan 	if (pathn_p != NULL) {
3514bac2208Snarayan 		*type = VLDC_STREAMING;
3524bac2208Snarayan 	} else {
3534bac2208Snarayan 		/*
3544bac2208Snarayan 		 * If no vldc, try to find a glvc node
3554bac2208Snarayan 		 */
3564bac2208Snarayan 		pathn_p = svc_name_to_glvc_dev_path(service);
3574bac2208Snarayan 		if (pathn_p != NULL) {
3584bac2208Snarayan 			*type = GLVC_NON_STREAM;
3594bac2208Snarayan 		}
3604bac2208Snarayan 	}
3614bac2208Snarayan 
3624bac2208Snarayan 	free(service);
3634bac2208Snarayan 	return (pathn_p);
3644bac2208Snarayan }
365