xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/cpu.c (revision 002c70ff32f5df6f93c15f88d351ce26443e6ee7)
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 <limits.h>
31 #include <strings.h>
32 #include <unistd.h>
33 #include <topo_error.h>
34 #include <fm/topo_mod.h>
35 #include <sys/fm/protocol.h>
36 
37 #include <topo_method.h>
38 #include <cpu.h>
39 
40 /*
41  * platform specific cpu module
42  */
43 #define	PLATFORM_CPU_VERSION	CPU_VERSION
44 #define	PLATFORM_CPU_NAME	"platform-cpu"
45 
46 static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
47     topo_instance_t, void *, void *);
48 static void cpu_release(topo_mod_t *, tnode_t *);
49 static int cpu_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
50     nvlist_t **);
51 static int cpu_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
52     nvlist_t **);
53 static int cpu_fmri_asru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
54     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_asru },
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 	uint32_t cpuid;
252 	uint64_t serint;
253 	char *serstr;
254 
255 	if (version == CPU_SCHEME_VERSION0) {
256 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 ||
257 		    nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint)
258 		    != 0)
259 			return (0);
260 
261 		return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX",
262 		    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
263 		    (u_longlong_t)serint));
264 	} else if (version == CPU_SCHEME_VERSION1) {
265 		if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
266 			return (0);
267 
268 		/*
269 		 * Serial number is an optional element
270 		 */
271 		if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
272 		    &serstr)) != 0)
273 			if (rc == ENOENT)
274 				return (snprintf(buf, buflen, "cpu:///%s=%u",
275 				    FM_FMRI_CPU_ID, cpuid));
276 			else
277 				return (0);
278 		else
279 			return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%s",
280 			    FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
281 			    serstr));
282 
283 	} else {
284 		return (0);
285 	}
286 }
287 
288 /*ARGSUSED*/
289 static int
290 cpu_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
291     nvlist_t *in, nvlist_t **out)
292 {
293 	uint8_t fver;
294 	ssize_t len;
295 	char *name;
296 
297 	if (version > TOPO_METH_NVL2STR_VERSION)
298 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
299 
300 	if (nvlist_lookup_uint8(in, FM_VERSION, &fver) != 0)
301 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
302 
303 	if ((len = fmri_nvl2str(in, fver, NULL, 0)) == 0 ||
304 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
305 	    fmri_nvl2str(in, fver, name, len + 1) == 0)
306 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
307 
308 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
309 		topo_mod_free(mod, name, len + 1);
310 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
311 	}
312 
313 	if (nvlist_add_string(*out, "fmri-string", name) != 0) {
314 		topo_mod_free(mod, name, len + 1);
315 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
316 	}
317 	topo_mod_free(mod, name, len + 1);
318 
319 	return (0);
320 }
321 
322 /*ARGSUSED*/
323 static int
324 cpu_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
325     nvlist_t *in, nvlist_t **out)
326 {
327 	int err;
328 	ulong_t cpuid;
329 	char *str, *s, *end;
330 	char *serial = NULL;
331 	nvlist_t *fmri;
332 
333 	if (version > TOPO_METH_STR2NVL_VERSION)
334 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
335 
336 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
337 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
338 
339 	/* We're expecting a string version of a cpu scheme FMRI */
340 	if (strncmp(str, "cpu:///", 7) != 0)
341 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
342 
343 	s = strchr(str + 7, '=');
344 	if (s == NULL)
345 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
346 
347 	++s;
348 	cpuid = strtoul(s, &end, 0);
349 
350 	if (cpuid == ULONG_MAX && errno == ERANGE)
351 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
352 
353 	if (*(s = end) == '/') {
354 		s = strchr(s, '=');
355 		++s;
356 		serial = s;
357 	}
358 
359 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
360 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
361 
362 	err = nvlist_add_uint8(fmri, FM_VERSION, CPU_SCHEME_VERSION1);
363 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
364 	err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, (uint32_t)cpuid);
365 	err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, 0);
366 	if (serial != NULL)
367 		err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID,
368 		    serial);
369 
370 	if (err != 0) {
371 		nvlist_free(fmri);
372 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
373 	}
374 	*out = fmri;
375 
376 	return (0);
377 }
378 
379 static nvlist_t *
380 fmri_create(topo_mod_t *mod, uint32_t cpu_id, uint8_t cpumask, char *s)
381 {
382 	int err;
383 	nvlist_t *fmri;
384 
385 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) {
386 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
387 		return (NULL);
388 	}
389 
390 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION);
391 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
392 	err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpu_id);
393 	err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask);
394 	if (s != NULL)
395 		err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, s);
396 	if (err != 0) {
397 		nvlist_free(fmri);
398 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
399 		return (NULL);
400 	}
401 
402 	return (fmri);
403 }
404 
405 /*ARGSUSED*/
406 static int
407 cpu_fmri_asru(topo_mod_t *mod, tnode_t *node, topo_version_t version,
408     nvlist_t *in, nvlist_t **out)
409 {
410 	int rc;
411 	uint32_t cpu_id;
412 	uint8_t cpumask = 0;
413 	char *serial = NULL;
414 
415 	if ((rc = nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpu_id)) != 0) {
416 		if (rc == ENOENT)
417 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
418 		else
419 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
420 	}
421 
422 	(void) nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID, &serial);
423 	(void) nvlist_lookup_uint8(in, FM_FMRI_CPU_MASK, &cpumask);
424 
425 	*out = fmri_create(mod, cpu_id, cpumask, serial);
426 
427 	return (0);
428 }
429