1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Implements auxiliary routines declared in pcp_utils.h to facilitate 28 * finding the appropriate communication transport & device path for a 29 * given service. This supports the transition from the legacy service channel 30 * transport (glvc) to the logical domain channel (vldc) transport native 31 * to platforms running Logical Domains (LDoms). 32 */ 33 34 #include <fcntl.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <strings.h> 38 #include <stdlib.h> 39 #include <libgen.h> 40 #include <unistd.h> 41 #include <stdio.h> 42 #include <libdevinfo.h> 43 44 #include "pcp_utils.h" 45 46 typedef enum { false = 0, true = 1 } bool_t; 47 48 #define SERVICE_PREFIX "SUNW,sun4v-" 49 #define DEVICES_DIR "/devices" 50 #define GLVC ":glvc" 51 #define VCHAN "virtual-channel@" 52 #define VCHAN_C "virtual-channel-client@" 53 54 /* 55 * The mechanism to relate a service to a device path is different for 56 * vldc and glvc, due to the way the device pathnames are encoded: 57 * Sample service: sunvts 58 * Service Name: SUNW,sun4v-sunvts 59 * GLVC device path: 60 * "/devices/virtual-devices@100/sunvts@a:glvc" 61 * VLDC device path: 62 * "/devices/virtual-devices@100/channel-devices@200/virtual-channel@3:sunvts" 63 * 64 * As VLDC is the communication mechanism used in an LDoms environment, it is 65 * the preferred channel, and its existence is checked for first. 66 */ 67 68 /* 69 * Extract from dev_path the "service name" portion. 70 * For vldc, the service corresponds to the minor name of the device path 71 * (e.g. virtual-channel@3:sunvts for sunvts). If service is non-NULL, it must 72 * match the extracted service name for the function to succeed. 73 * The service name is returned in match (if non-NULL), and the function 74 * itself returns true on success; false on failure. 75 */ 76 static bool_t 77 get_vldc_svc_name(char *dev_path, char *service, char **match) 78 { 79 bool_t ret = false; 80 char *pathname = strdup(dev_path); 81 char *devname, *s; 82 83 if (NULL == pathname) 84 return (false); 85 86 devname = basename(pathname); 87 s = strrchr(devname, ':'); 88 89 if (s++ == NULL) { 90 goto end; 91 } 92 93 if ((strncmp(devname, VCHAN, strlen(VCHAN)) == 0) || 94 (strncmp(devname, VCHAN_C, strlen(VCHAN_C)) == 0)) { 95 /* 96 * If in addition, a service string is specified to 97 * be matched, do a comparison 98 */ 99 if (service != NULL) { 100 if (strcmp(s, service) == 0) { 101 if (match) 102 *match = strdup(s); 103 ret = true; 104 goto end; 105 } else { 106 ret = false; 107 goto end; 108 } 109 } else if (match) { 110 *match = strdup(s); 111 } 112 113 ret = true; 114 goto end; 115 } 116 end: 117 118 free(pathname); 119 return (ret); 120 } 121 122 /* 123 * Extract from dev_path the "service name" portion. 124 * For glvc, the service corresponds to the node name of the device path 125 * (e.g. sunvts@a:glvc for sunvts). If service is non-NULL, it must 126 * match the extracted service name for the function to succeed. 127 * The service name is returned in match (if non-NULL), and the function 128 * itself returns true on success; false on failure. 129 */ 130 static bool_t 131 get_glvc_svc_name(char *dev_path, char *service, char **match) 132 { 133 bool_t ret = true; 134 char *pathname = strdup(dev_path); 135 char *devname, *substr, *t; 136 int len; 137 138 if (NULL == pathname) 139 return (false); 140 141 devname = basename(pathname); 142 substr = strstr(devname, GLVC); 143 144 if (!((substr != NULL) && (strcmp(substr, GLVC) == 0))) { 145 ret = false; 146 goto end; 147 } 148 149 if ((t = strrchr(devname, '@')) == NULL) { 150 ret = false; 151 goto end; 152 } 153 154 len = t - devname; 155 156 /* 157 * If a service string is specified, check if there 158 * is a match 159 */ 160 if ((service != NULL) && (strncmp(devname, service, len) != 0)) 161 ret = false; 162 163 if ((ret == true) && (match != NULL)) { 164 *match = calloc(len + 1, 1); 165 if (*match) 166 (void) strncpy(*match, devname, len); 167 } 168 169 end: 170 free(pathname); 171 return (ret); 172 } 173 174 /* 175 * This routine accepts either a prefixed service name or a legacy full 176 * pathname (which might not even exist in the filesystem), and in either case 177 * returns a canonical service name. If the parameter is neither a service 178 * name (i.e. with a "SUNW,sun4v-" prefix), nor a path to a legacy glvc or 179 * vldc device, NULL is returned. 180 */ 181 char * 182 platsvc_extract_svc_name(char *devname) 183 { 184 char *sname = NULL; 185 char *vldc_path, *glvc_path; 186 187 /* 188 * First check whether a service name 189 */ 190 if (strncmp(devname, SERVICE_PREFIX, strlen(SERVICE_PREFIX)) == 0) { 191 sname = strdup(devname + strlen(SERVICE_PREFIX)); 192 return (sname); 193 } 194 195 /* 196 * Not a service name, check if it's a valid pathname 197 */ 198 if (!(devname[0] == '/' || devname[0] == '.')) { 199 return (NULL); 200 } 201 202 /* 203 * Ideally, we should only check for a valid glvc pathname, 204 * requiring all vldc access to be only via service names. But 205 * to prevent a flag day with code that's already passing in 206 * vldc full pathnames (e.g. sunMC), we allow them here. 207 */ 208 if (get_vldc_svc_name(devname, NULL, &vldc_path) == true) { 209 return (vldc_path); 210 } else if (get_glvc_svc_name(devname, NULL, &glvc_path) == true) { 211 return (glvc_path); 212 } 213 214 return (NULL); 215 } 216 217 /* 218 * Walk all "service" device nodes to find the one with the 219 * matching glvc minor name 220 */ 221 static char * 222 svc_name_to_glvc_dev_path(char *service) 223 { 224 di_node_t root_node, service_node; 225 char *glvc_path; 226 char *minor_name; 227 di_minor_t minor; 228 char *dev_path = NULL; 229 230 if (service == NULL) 231 return (NULL); 232 233 /* Ensure that the 'glvc' driver is loaded */ 234 root_node = di_init_driver("glvc", DINFOCPYALL); 235 if (root_node == DI_NODE_NIL) { 236 return (dev_path); 237 } 238 239 service_node = di_drv_first_node("glvc", root_node); 240 241 while (service_node != DI_NODE_NIL) { 242 /* Make sure node name matches service name */ 243 if (strcmp(service, di_node_name(service_node)) == 0) { 244 /* Walk minor nodes */ 245 minor = di_minor_next(service_node, DI_NODE_NIL); 246 247 while (minor != DI_NODE_NIL) { 248 glvc_path = di_devfs_minor_path(minor); 249 minor_name = di_minor_name(minor); 250 251 if (strcmp(minor_name, "glvc") == 0) { 252 dev_path = malloc(strlen(glvc_path) + 253 strlen(DEVICES_DIR) + 1); 254 (void) strcpy(dev_path, DEVICES_DIR); 255 (void) strcat(dev_path, glvc_path); 256 di_devfs_path_free(glvc_path); 257 break; 258 } 259 260 di_devfs_path_free(glvc_path); 261 minor = di_minor_next(service_node, minor); 262 } 263 } 264 if (dev_path != NULL) 265 break; 266 267 service_node = di_drv_next_node(service_node); 268 } 269 270 di_fini(root_node); 271 return (dev_path); 272 } 273 274 /* 275 * Walk all vldc device nodes to find the one with the 276 * matching minor name 277 */ 278 static char * 279 svc_name_to_vldc_dev_path(char *service) 280 { 281 di_node_t root_node, vldc_node; 282 char *vldc_path; 283 char *minor_name; 284 di_minor_t minor; 285 char *dev_path = NULL; 286 287 /* Ensure that the 'vldc' driver is loaded */ 288 root_node = di_init_driver("vldc", DINFOCPYALL); 289 if (root_node == DI_NODE_NIL) { 290 return (dev_path); 291 } 292 293 vldc_node = di_drv_first_node("vldc", root_node); 294 295 while (vldc_node != DI_NODE_NIL) { 296 /* Walk minor nodes */ 297 minor = di_minor_next(vldc_node, DI_NODE_NIL); 298 299 while (minor != DI_NODE_NIL) { 300 vldc_path = di_devfs_minor_path(minor); 301 minor_name = di_minor_name(minor); 302 303 if (strcmp(minor_name, service) == 0) { 304 dev_path = malloc(strlen(vldc_path) + 305 strlen(DEVICES_DIR) + 1); 306 (void) strcpy(dev_path, DEVICES_DIR); 307 (void) strcat(dev_path, vldc_path); 308 di_devfs_path_free(vldc_path); 309 break; 310 } 311 312 di_devfs_path_free(vldc_path); 313 minor = di_minor_next(vldc_node, minor); 314 } 315 if (dev_path != NULL) 316 break; 317 318 vldc_node = di_drv_next_node(vldc_node); 319 } 320 321 di_fini(root_node); 322 return (dev_path); 323 } 324 325 /* 326 * Given a service name or a full legacy pathname, return 327 * the full pathname to the appropriate vldc or glvc device. 328 */ 329 char * 330 platsvc_name_to_path(char *svc_or_path, pcp_xport_t *type) 331 { 332 char *pathn_p; 333 char *service; 334 335 if ((service = platsvc_extract_svc_name(svc_or_path)) == NULL) 336 return (NULL); 337 338 /* 339 * First lookup vldc nodes 340 */ 341 pathn_p = svc_name_to_vldc_dev_path(service); 342 if (pathn_p != NULL) { 343 *type = VLDC_STREAMING; 344 } else { 345 /* 346 * If no vldc, try to find a glvc node 347 */ 348 pathn_p = svc_name_to_glvc_dev_path(service); 349 if (pathn_p != NULL) { 350 *type = GLVC_NON_STREAM; 351 } 352 } 353 354 free(service); 355 return (pathn_p); 356 } 357