xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/cpu.c (revision 80ab886d)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <errno.h>
30 #include <kstat.h>
31 #include <limits.h>
32 #include <strings.h>
33 #include <unistd.h>
34 #include <fm/topo_mod.h>
35 #include <sys/fm/protocol.h>
36 
37 #include <topo_error.h>
38 
39 typedef struct cpu_node {
40 	kstat_ctl_t *cn_kc;
41 	kstat_t **cn_cpustats;
42 	uint_t cn_ncpustats;
43 } cpu_node_t;
44 
45 static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
46     topo_instance_t, 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_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
53     nvlist_t **);
54 static int cpu_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
55     nvlist_t **);
56 static int cpu_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
57     nvlist_t **);
58 static int cpu_expand(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
59     nvlist_t **);
60 static int cpu_fmri_asru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
61     nvlist_t **);
62 static nvlist_t *fmri_create(topo_mod_t *, uint32_t, uint8_t, char *);
63 
64 #define	CPU_VERSION	TOPO_VERSION
65 
66 static const topo_method_t cpu_methods[] = {
67 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
68 	    TOPO_STABILITY_INTERNAL, cpu_nvl2str },
69 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
70 	    TOPO_STABILITY_INTERNAL, cpu_str2nvl },
71 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
72 	    TOPO_STABILITY_INTERNAL, cpu_present },
73 	{ TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
74 	    TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, cpu_contains },
75 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
76 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, cpu_unusable },
77 	{ TOPO_METH_EXPAND, TOPO_METH_EXPAND_DESC,
78 	    TOPO_METH_EXPAND_VERSION, TOPO_STABILITY_INTERNAL, cpu_expand },
79 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
80 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
81 	    cpu_fmri_asru },
82 	{ NULL }
83 };
84 
85 static const topo_modinfo_t cpu_info =
86 	{ "cpu", CPU_VERSION, cpu_enum, cpu_release };
87 
88 void
89 cpu_init(topo_mod_t *mod)
90 {
91 	cpu_node_t *cpuip;
92 
93 	topo_mod_setdebug(mod, TOPO_DBG_ALL);
94 	topo_mod_dprintf(mod, "initializing cpu builtin\n");
95 
96 	if ((cpuip = topo_mod_zalloc(mod, sizeof (cpu_node_t))) == NULL)
97 		return;
98 
99 	if ((cpuip->cn_kc = kstat_open()) == NULL) {
100 		topo_mod_dprintf(mod, "kstat_open failed: %s\n",
101 		    strerror(errno));
102 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
103 		return;
104 	}
105 
106 	cpuip->cn_ncpustats = sysconf(_SC_CPUID_MAX);
107 	if ((cpuip->cn_cpustats = topo_mod_zalloc(mod, (
108 	    cpuip->cn_ncpustats + 1) * sizeof (kstat_t *))) == NULL) {
109 		(void) kstat_close(cpuip->cn_kc);
110 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
111 		return;
112 	}
113 
114 	if (topo_mod_register(mod, &cpu_info, (void *)cpuip) != 0) {
115 		topo_mod_dprintf(mod, "failed to register cpu_info: "
116 		    "%s\n", topo_mod_errmsg(mod));
117 		topo_mod_free(mod, cpuip->cn_cpustats,
118 		    (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *));
119 		(void) kstat_close(cpuip->cn_kc);
120 		topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
121 		return;
122 	}
123 }
124 
125 void
126 cpu_fini(topo_mod_t *mod)
127 {
128 	cpu_node_t *cpuip;
129 
130 	cpuip = topo_mod_private(mod);
131 
132 	if (cpuip->cn_cpustats != NULL)
133 		topo_mod_free(mod, cpuip->cn_cpustats,
134 		    (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *));
135 
136 	(void) kstat_close(cpuip->cn_kc);
137 	topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
138 
139 	topo_mod_unregister(mod);
140 }
141 
142 static int
143 cpu_kstat_init(cpu_node_t *cpuip, int i)
144 {
145 	kstat_t *ksp;
146 
147 	if (cpuip->cn_cpustats[i] == NULL) {
148 		if ((ksp = kstat_lookup(cpuip->cn_kc, "cpu_info", i, NULL)) ==
149 		    NULL || kstat_read(cpuip->cn_kc, ksp, NULL) < 0)
150 			return (-1);
151 
152 		cpuip->cn_cpustats[i] = ksp;
153 	} else {
154 		ksp = cpuip->cn_cpustats[i];
155 	}
156 
157 	return (ksp->ks_instance);
158 }
159 
160 /*ARGSUSED*/
161 static int
162 cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name,
163     topo_instance_t min, topo_instance_t max, cpu_node_t *cpuip)
164 {
165 	int i;
166 	processorid_t cpu_id;
167 	char *s, sbuf[21];
168 	kstat_named_t *ks;
169 	nvlist_t *fmri;
170 
171 	for (i = 0; i <= cpuip->cn_ncpustats; i++) {
172 
173 		if ((cpu_id = cpu_kstat_init(cpuip, i)) < 0)
174 			continue;
175 
176 		if ((ks = kstat_data_lookup(cpuip->cn_cpustats[i],
177 		    "device_ID")) != NULL) {
178 			(void) snprintf(sbuf, 21, "%llX", ks->value.ui64);
179 			s = sbuf;
180 		} else {
181 			s = NULL;
182 		}
183 
184 		if ((fmri = fmri_create(mod, cpu_id, 0, s)) == NULL)
185 			continue;
186 		(void) topo_node_bind(mod, rnode, name, cpu_id, fmri, NULL);
187 		nvlist_free(fmri);
188 	}
189 
190 	return (0);
191 }
192 
193 
194 /*ARGSUSED*/
195 static int
196 cpu_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
197     topo_instance_t min, topo_instance_t max, void *arg)
198 {
199 	cpu_node_t *cpuip = (cpu_node_t *)arg;
200 
201 	if (topo_node_range_create(mod, pnode, "cpu", 0,
202 	    cpuip->cn_ncpustats + 1) < 0) {
203 		topo_mod_dprintf(mod, "cpu enumeration failed to create cpu "
204 		    "range [0-%d]: %s\n", cpuip->cn_ncpustats + 1,
205 		    topo_mod_errmsg(mod));
206 		return (-1); /* mod_errno set */
207 	}
208 
209 	(void) topo_method_register(mod, pnode, cpu_methods);
210 
211 	return (cpu_create(mod, pnode, name, min, max, cpuip));
212 }
213 
214 static void
215 cpu_release(topo_mod_t *mod, tnode_t *node)
216 {
217 	topo_method_unregister_all(mod, node);
218 }
219 
220 ssize_t
221 fmri_nvl2str(nvlist_t *nvl, uint8_t version, char *buf, size_t buflen)
222 {
223 	int rc;
224 	uint32_t cpuid;
225 	uint64_t serint;
226 	char *serstr;
227 
228 	if (version == CPU_SCHEME_VERSION0) {
229 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 ||
230 		    nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint)
231 		    != 0)
232 			return (0);
233 
234 		return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX",
235 		    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
236 		    (u_longlong_t)serint));
237 	} else if (version == CPU_SCHEME_VERSION1) {
238 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
239 			return (0);
240 
241 		/*
242 		 * Serial number is an optional element
243 		 */
244 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
245 		    &serstr)) != 0)
246 			if (rc == ENOENT)
247 				return (snprintf(buf, buflen, "cpu:///%s=%u",
248 				    FM_FMRI_CPU_ID, cpuid));
249 			else
250 				return (0);
251 		else
252 			return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%s",
253 			    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
254 			    serstr));
255 
256 	} else {
257 		return (0);
258 	}
259 }
260 
261 /*ARGSUSED*/
262 static int
263 cpu_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
264     nvlist_t *in, nvlist_t **out)
265 {
266 	uint8_t fver;
267 	ssize_t len;
268 	char *name;
269 
270 	if (version > TOPO_METH_NVL2STR_VERSION)
271 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
272 
273 	if (nvlist_lookup_uint8(in, FM_VERSION, &fver) != 0)
274 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
275 
276 	if ((len = fmri_nvl2str(in, fver, NULL, 0)) == 0 ||
277 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
278 	    fmri_nvl2str(in, fver, name, len + 1) == 0)
279 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
280 
281 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
282 		topo_mod_free(mod, name, len + 1);
283 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
284 	}
285 
286 	if (nvlist_add_string(*out, "fmri-string", name) != 0) {
287 		topo_mod_free(mod, name, len + 1);
288 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
289 	}
290 	topo_mod_free(mod, name, len + 1);
291 
292 	return (0);
293 }
294 
295 /*ARGSUSED*/
296 static int
297 cpu_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
298     nvlist_t *in, nvlist_t **out)
299 {
300 	int err;
301 	ulong_t cpuid;
302 	char *str, *s, *end;
303 	char *serial = NULL;
304 	nvlist_t *fmri;
305 
306 	if (version > TOPO_METH_STR2NVL_VERSION)
307 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
308 
309 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
310 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
311 
312 	/* We're expecting a string version of a cpu scheme FMRI */
313 	if (strncmp(str, "cpu:///", 7) != 0)
314 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
315 
316 	s = strchr(str + 7, '=');
317 	if (s == NULL)
318 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
319 
320 	++s;
321 	cpuid = strtoul(s, &end, 0);
322 
323 	if (cpuid == ULONG_MAX && errno == ERANGE)
324 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
325 
326 	if (*(s = end) == '/') {
327 		s = strchr(s, '=');
328 		++s;
329 		serial = s;
330 	}
331 
332 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
333 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
334 
335 	err = nvlist_add_uint8(fmri, FM_VERSION, CPU_SCHEME_VERSION1);
336 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
337 	err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, (uint32_t)cpuid);
338 	err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, 0);
339 	if (serial != NULL)
340 		err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID,
341 		    serial);
342 
343 	if (err != 0) {
344 		nvlist_free(fmri);
345 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
346 	}
347 	*out = fmri;
348 
349 	return (0);
350 }
351 
352 /*ARGSUSED*/
353 static int
354 cpu_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
355     nvlist_t *in, nvlist_t **out)
356 {
357 	return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
358 }
359 
360 /*ARGSUSED*/
361 static int
362 cpu_contains(topo_mod_t *mod, tnode_t *node, topo_version_t version,
363     nvlist_t *in, nvlist_t **out)
364 {
365 	return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
366 }
367 
368 /*ARGSUSED*/
369 static int
370 cpu_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
371     nvlist_t *in, nvlist_t **out)
372 {
373 	return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
374 }
375 
376 /*ARGSUSED*/
377 static int
378 cpu_expand(topo_mod_t *mod, tnode_t *node, topo_version_t version,
379     nvlist_t *in, nvlist_t **out)
380 {
381 	return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
382 }
383 
384 static nvlist_t *
385 fmri_create(topo_mod_t *mod, uint32_t cpu_id, uint8_t cpumask, char *s)
386 {
387 	int err;
388 	nvlist_t *fmri;
389 
390 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) {
391 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
392 		return (NULL);
393 	}
394 
395 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION);
396 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
397 	err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpu_id);
398 	err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask);
399 	if (s != NULL)
400 		err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, s);
401 	if (err != 0) {
402 		nvlist_free(fmri);
403 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
404 		return (NULL);
405 	}
406 
407 	return (fmri);
408 }
409 
410 /*ARGSUSED*/
411 static int
412 cpu_fmri_asru(topo_mod_t *mod, tnode_t *node, topo_version_t version,
413     nvlist_t *in, nvlist_t **out)
414 {
415 	int rc;
416 	uint32_t cpu_id;
417 	uint8_t cpumask = 0;
418 	char *serial = NULL;
419 
420 	if ((rc = nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpu_id)) != 0) {
421 		if (rc == ENOENT)
422 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
423 		else
424 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
425 	}
426 
427 	(void) nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID, &serial);
428 	(void) nvlist_lookup_uint8(in, FM_FMRI_CPU_MASK, &cpumask);
429 
430 	*out = fmri_create(mod, cpu_id, cpumask, serial);
431 
432 	return (0);
433 }
434