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