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
get_vldc_svc_name(char * dev_path,char * service,char ** match)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
get_glvc_svc_name(char * dev_path,char * service,char ** match)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 *
platsvc_extract_svc_name(char * devname)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 *
svc_name_to_glvc_dev_path(char * service)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 *
svc_name_to_vldc_dev_path(char * service)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 *
platsvc_name_to_path(char * svc_or_path,pcp_xport_t * type)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