xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/cpu.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <errno.h>
28 #include <limits.h>
29 #include <strings.h>
30 #include <unistd.h>
31 #include <topo_error.h>
32 #include <fm/topo_mod.h>
33 #include <sys/fm/protocol.h>
34 
35 #include <topo_method.h>
36 #include <cpu.h>
37 
38 /*
39  * platform specific cpu module
40  */
41 #define	PLATFORM_CPU_VERSION	CPU_VERSION
42 #define	PLATFORM_CPU_NAME	"platform-cpu"
43 
44 static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
45     topo_instance_t, void *, void *);
46 static void cpu_release(topo_mod_t *, tnode_t *);
47 static int cpu_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
48     nvlist_t **);
49 static int cpu_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
50     nvlist_t **);
51 static int cpu_fmri_asru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
52     nvlist_t **);
53 static int cpu_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
54     nvlist_t *, nvlist_t **);
55 static nvlist_t *fmri_create(topo_mod_t *, uint32_t, uint8_t, char *);
56 
57 static const topo_method_t cpu_methods[] = {
58 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
59 	    TOPO_STABILITY_INTERNAL, cpu_nvl2str },
60 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
61 	    TOPO_STABILITY_INTERNAL, cpu_str2nvl },
62 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
63 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
64 	    cpu_fmri_asru },
65 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
66 	    TOPO_STABILITY_INTERNAL, cpu_fmri_create_meth },
67 	{ NULL }
68 };
69 
70 static const topo_modops_t cpu_ops =
71 	{ cpu_enum, cpu_release };
72 
73 static const topo_modinfo_t cpu_info =
74 	{ "cpu", FM_FMRI_SCHEME_CPU, CPU_VERSION, &cpu_ops };
75 
76 int
77 cpu_init(topo_mod_t *mod, topo_version_t version)
78 {
79 	cpu_node_t *cpuip;
80 
81 	if (getenv("TOPOCPUDEBUG"))
82 		topo_mod_setdebug(mod);
83 	topo_mod_dprintf(mod, "initializing cpu builtin\n");
84 
85 	if (version != CPU_VERSION)
86 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
87 
88 	if ((cpuip = topo_mod_zalloc(mod, sizeof (cpu_node_t))) == NULL)
89 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
90 
91 	if ((cpuip->cn_kc = kstat_open()) == NULL) {
92 		topo_mod_dprintf(mod, "kstat_open failed: %s\n",
93 		    strerror(errno));
94 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
95 		return (-1);
96 	}
97 
98 	cpuip->cn_ncpustats = sysconf(_SC_CPUID_MAX);
99 	if ((cpuip->cn_cpustats = topo_mod_zalloc(mod, (
100 	    cpuip->cn_ncpustats + 1) * sizeof (kstat_t *))) == NULL) {
101 		(void) kstat_close(cpuip->cn_kc);
102 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
103 		return (-1);
104 	}
105 
106 	if (topo_mod_register(mod, &cpu_info, TOPO_VERSION) != 0) {
107 		topo_mod_dprintf(mod, "failed to register cpu_info: "
108 		    "%s\n", topo_mod_errmsg(mod));
109 		topo_mod_free(mod, cpuip->cn_cpustats,
110 		    (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *));
111 		(void) kstat_close(cpuip->cn_kc);
112 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
113 		return (-1);
114 	}
115 
116 	topo_mod_setspecific(mod, (void *)cpuip);
117 
118 	return (0);
119 }
120 
121 void
122 cpu_fini(topo_mod_t *mod)
123 {
124 	cpu_node_t *cpuip;
125 
126 	cpuip = topo_mod_getspecific(mod);
127 
128 	if (cpuip->cn_cpustats != NULL)
129 		topo_mod_free(mod, cpuip->cn_cpustats,
130 		    (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *));
131 
132 	(void) kstat_close(cpuip->cn_kc);
133 	topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
134 
135 	topo_mod_unregister(mod);
136 }
137 
138 static int
139 cpu_kstat_init(cpu_node_t *cpuip, int i)
140 {
141 	kstat_t *ksp;
142 
143 	if (cpuip->cn_cpustats[i] == NULL) {
144 		if ((ksp = kstat_lookup(cpuip->cn_kc, "cpu_info", i, NULL)) ==
145 		    NULL || kstat_read(cpuip->cn_kc, ksp, NULL) < 0)
146 			return (-1);
147 
148 		cpuip->cn_cpustats[i] = ksp;
149 	} else {
150 		ksp = cpuip->cn_cpustats[i];
151 	}
152 
153 	return (ksp->ks_instance);
154 }
155 
156 /*ARGSUSED*/
157 static int
158 cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name,
159     topo_instance_t min, topo_instance_t max, cpu_node_t *cpuip)
160 {
161 	int i;
162 	processorid_t cpu_id;
163 	char *s, sbuf[21];
164 	kstat_named_t *ks;
165 	nvlist_t *fmri;
166 
167 	for (i = 0; i <= cpuip->cn_ncpustats; i++) {
168 
169 		if ((cpu_id = cpu_kstat_init(cpuip, i)) < 0)
170 			continue;
171 
172 		if ((ks = kstat_data_lookup(cpuip->cn_cpustats[i],
173 		    "device_ID")) != NULL) {
174 			(void) snprintf(sbuf, 21, "%llX", ks->value.ui64);
175 			s = sbuf;
176 		} else {
177 			s = NULL;
178 		}
179 
180 		if ((fmri = fmri_create(mod, cpu_id, 0, s)) == NULL)
181 			continue;
182 		(void) topo_node_bind(mod, rnode, name, cpu_id, fmri);
183 		nvlist_free(fmri);
184 	}
185 
186 	return (0);
187 }
188 
189 
190 /*ARGSUSED*/
191 static int
192 cpu_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
193     topo_instance_t min, topo_instance_t max, void *arg, void *notused2)
194 {
195 	topo_mod_t *nmp;
196 	cpu_node_t *cpuip = (cpu_node_t *)arg;
197 
198 	if ((nmp = topo_mod_load(mod, PLATFORM_CPU_NAME,
199 	    PLATFORM_CPU_VERSION)) == NULL) {
200 		if (topo_mod_errno(mod) == ETOPO_MOD_NOENT) {
201 			/*
202 			 * There is no platform specific cpu module, so use
203 			 * the default enumeration with kstats of this builtin
204 			 * cpu module.
205 			 */
206 			if (topo_node_range_create(mod, pnode, name, 0,
207 			    cpuip->cn_ncpustats + 1) < 0) {
208 				topo_mod_dprintf(mod,
209 				    "cpu enumeration failed to create "
210 				    "cpu range [0-%d]: %s\n",
211 				    cpuip->cn_ncpustats + 1,
212 				    topo_mod_errmsg(mod));
213 				return (-1); /* mod_errno set */
214 			}
215 			(void) topo_method_register(mod, pnode, cpu_methods);
216 			return (cpu_create(mod, pnode, name, min, max, cpuip));
217 
218 		} else {
219 			/* Fail to load the module */
220 			topo_mod_dprintf(mod,
221 			    "Failed to load module %s: %s",
222 			    PLATFORM_CPU_NAME,
223 			    topo_mod_errmsg(mod));
224 			return (-1);
225 		}
226 	}
227 
228 	if (topo_mod_enumerate(nmp, pnode, PLATFORM_CPU_NAME, name,
229 	    min, max, NULL) < 0) {
230 		topo_mod_dprintf(mod,
231 		    "%s failed to enumerate: %s",
232 		    PLATFORM_CPU_NAME,
233 		    topo_mod_errmsg(mod));
234 		return (-1);
235 	}
236 	(void) topo_method_register(mod, pnode, cpu_methods);
237 
238 	return (0);
239 }
240 
241 static void
242 cpu_release(topo_mod_t *mod, tnode_t *node)
243 {
244 	topo_method_unregister_all(mod, node);
245 }
246 
247 ssize_t
248 fmri_nvl2str(nvlist_t *nvl, uint8_t version, char *buf, size_t buflen)
249 {
250 	int rc;
251 	uint8_t	type;
252 	uint32_t cpuid, index, way;
253 	uint64_t serint;
254 	char *serstr = NULL;
255 
256 	if (version == CPU_SCHEME_VERSION0) {
257 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 ||
258 		    nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint)
259 		    != 0)
260 			return (0);
261 
262 		return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX",
263 		    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
264 		    (u_longlong_t)serint));
265 
266 	} else if (version == CPU_SCHEME_VERSION1) {
267 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
268 			return (0);
269 
270 		/*
271 		 * Serial number is an optional element
272 		 */
273 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
274 		    &serstr)) != 0)
275 
276 			if (rc != ENOENT)
277 				return (0);
278 
279 		/*
280 		 * Cache index, way and type are optional elements
281 		 * But if we have one of them, we must have them all.
282 		 */
283 		rc = nvlist_lookup_uint32(nvl, FM_FMRI_CPU_CACHE_INDEX,
284 		    &index);
285 		rc |= nvlist_lookup_uint32(nvl, FM_FMRI_CPU_CACHE_WAY, &way);
286 		rc |= nvlist_lookup_uint8(nvl, FM_FMRI_CPU_CACHE_TYPE, &type);
287 
288 		/* Insure there were no errors accessing the nvl */
289 		if (rc != 0 && rc != ENOENT)
290 			return (0);
291 
292 		if (serstr == NULL) {
293 			/* If we have a serial string and no cache info */
294 			if (rc == ENOENT)
295 				return (snprintf(buf, buflen, "cpu:///%s=%u",
296 				    FM_FMRI_CPU_ID, cpuid));
297 			else {
298 				return (snprintf(buf, buflen,
299 				    "cpu:///%s=%u/%s=%u/%s=%u/%s=%d",
300 				    FM_FMRI_CPU_ID, cpuid,
301 				    FM_FMRI_CPU_CACHE_INDEX, index,
302 				    FM_FMRI_CPU_CACHE_WAY, way,
303 				    FM_FMRI_CPU_CACHE_TYPE, type));
304 			}
305 		} else {
306 			if (rc == ENOENT) {
307 				return (snprintf(buf, buflen,
308 				    "cpu:///%s=%u/%s=%s",
309 				    FM_FMRI_CPU_ID, cpuid,
310 				    FM_FMRI_CPU_SERIAL_ID, serstr));
311 			} else {
312 				return (snprintf(buf, buflen,
313 				    "cpu:///%s=%u/%s=%s/%s=%u/%s=%u/%s=%d",
314 				    FM_FMRI_CPU_ID, cpuid,
315 				    FM_FMRI_CPU_SERIAL_ID, serstr,
316 				    FM_FMRI_CPU_CACHE_INDEX, index,
317 				    FM_FMRI_CPU_CACHE_WAY, way,
318 				    FM_FMRI_CPU_CACHE_TYPE, type));
319 			}
320 		}
321 	} else
322 		return (0);
323 }
324 
325 /*ARGSUSED*/
326 static int
327 cpu_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
328     nvlist_t *in, nvlist_t **out)
329 {
330 	uint8_t fver;
331 	ssize_t len;
332 	char *name;
333 
334 	if (version > TOPO_METH_NVL2STR_VERSION)
335 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
336 
337 	if (nvlist_lookup_uint8(in, FM_VERSION, &fver) != 0)
338 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
339 
340 	if ((len = fmri_nvl2str(in, fver, NULL, 0)) == 0 ||
341 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
342 	    fmri_nvl2str(in, fver, name, len + 1) == 0)
343 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
344 
345 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
346 		topo_mod_free(mod, name, len + 1);
347 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
348 	}
349 
350 	if (nvlist_add_string(*out, "fmri-string", name) != 0) {
351 		topo_mod_free(mod, name, len + 1);
352 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
353 	}
354 	topo_mod_free(mod, name, len + 1);
355 
356 	return (0);
357 }
358 
359 /*ARGSUSED*/
360 static int
361 cpu_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
362     nvlist_t *in, nvlist_t **out)
363 {
364 	int err;
365 	ulong_t cpuid;
366 	uint8_t	type;
367 	uint32_t way, index = NULL;
368 	char *str, *s, *end, *serial_end;
369 	char *serial = NULL;
370 	nvlist_t *fmri;
371 
372 	if (version > TOPO_METH_STR2NVL_VERSION)
373 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
374 
375 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
376 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
377 
378 	/* We're expecting a string version of a cpu scheme FMRI */
379 	if (strncmp(str, "cpu:///", 7) != 0)
380 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
381 
382 	s = strchr(str + 7, '=');
383 	if (s == NULL)
384 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
385 
386 	++s;
387 	cpuid = strtoul(s, &end, 0);
388 
389 	if (cpuid == ULONG_MAX && errno == ERANGE)
390 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
391 
392 	/* If there is a serial #, then there might also be cache data */
393 	if (*(s = end) == '/') {
394 		s = strchr(s, '=');
395 		++s;
396 		serial = s;
397 		serial_end = strchr(s, '/');
398 		/* If there is cache data, all must be present */
399 		if (s != NULL) {
400 			s = strchr(s, '=');
401 			++s;
402 			index = strtoul(s, &end, 0);
403 			if (*(s = end) != '/') {
404 				return (topo_mod_seterrno(mod,
405 				    EMOD_FMRI_MALFORM));
406 			}
407 			s = strchr(s, '=');
408 			++s;
409 			way = strtoul(s, &end, 0);
410 			if (*(s = end) != '/') {
411 				return (topo_mod_seterrno(mod,
412 				    EMOD_FMRI_MALFORM));
413 			}
414 			s = strchr(s, '=');
415 			++s;
416 			type = strtoul(s, &end, 0);
417 			if (*(s = end) != '/') {
418 				return (topo_mod_seterrno(mod,
419 				    EMOD_FMRI_MALFORM));
420 			}
421 		}
422 		/* Now terminate the serial string */
423 		if (serial_end != NULL)
424 			*serial_end = '\n';
425 
426 	}
427 
428 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
429 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
430 
431 	err = nvlist_add_uint8(fmri, FM_VERSION, CPU_SCHEME_VERSION1);
432 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
433 	err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, (uint32_t)cpuid);
434 	err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, 0);
435 	if (serial != NULL)
436 		err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID,
437 		    serial);
438 
439 	if (index != NULL) {
440 		err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_CACHE_INDEX,
441 		    index);
442 		err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_CACHE_WAY,
443 		    way);
444 		err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_CACHE_TYPE,
445 		    type);
446 	}
447 	if (err != 0) {
448 		nvlist_free(fmri);
449 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
450 	}
451 	*out = fmri;
452 
453 	return (0);
454 }
455 
456 static nvlist_t *
457 fmri_create(topo_mod_t *mod, uint32_t cpu_id, uint8_t cpumask, char *s)
458 {
459 	int err;
460 	nvlist_t *fmri;
461 
462 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) {
463 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
464 		return (NULL);
465 	}
466 
467 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION);
468 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
469 	err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpu_id);
470 	err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask);
471 	if (s != NULL)
472 		err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, s);
473 	if (err != 0) {
474 		nvlist_free(fmri);
475 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
476 		return (NULL);
477 	}
478 
479 	return (fmri);
480 }
481 
482 /*ARGSUSED*/
483 static int
484 cpu_fmri_asru(topo_mod_t *mod, tnode_t *node, topo_version_t version,
485     nvlist_t *in, nvlist_t **out)
486 {
487 	int rc;
488 	uint32_t cpu_id;
489 	uint8_t cpumask = 0;
490 	char *serial = NULL;
491 
492 	if ((rc = nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpu_id)) != 0) {
493 		if (rc == ENOENT)
494 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
495 		else
496 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
497 	}
498 
499 	(void) nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID, &serial);
500 	(void) nvlist_lookup_uint8(in, FM_FMRI_CPU_MASK, &cpumask);
501 
502 	*out = fmri_create(mod, cpu_id, cpumask, serial);
503 
504 	return (0);
505 }
506 
507 /*ARGSUSED*/
508 static int
509 cpu_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version,
510     nvlist_t *in, nvlist_t **out)
511 {
512 	int		rc;
513 	nvlist_t	*args;
514 	uint32_t	cpu_id;
515 	uint8_t		cpumask = 0;
516 	char		*serial = NULL;
517 
518 	if (version > TOPO_METH_FMRI_VERSION) {
519 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
520 	}
521 
522 	rc = nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args);
523 	if (rc != 0) {
524 		/*
525 		 * This routine requires arguments to be packed in the
526 		 * format used in topo_fmri_create()
527 		 */
528 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
529 	}
530 
531 	if (nvlist_lookup_string(args, FM_FMRI_CPU_SERIAL_ID, &serial) != 0 ||
532 	    nvlist_lookup_uint32(args, FM_FMRI_CPU_ID, &cpu_id) != 0 ||
533 	    nvlist_lookup_uint8(args, FM_FMRI_CPU_MASK, &cpumask) != 0) {
534 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
535 	}
536 
537 	*out = fmri_create(mod, cpu_id, cpumask, serial);
538 	if (*out == NULL) {
539 		return (-1);
540 	}
541 
542 	return (0);
543 }
544