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