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 <strings.h>
28 #include <string.h>
29 #include <libnvpair.h>
30 #include <sys/fm/ldom.h>
31 #include <fm/libtopo.h>
32 #include <fm/topo_mod.h>
33 #include <fm/fmd_fmri.h>
34 #include <fm/fmd_agent.h>
35 #include <sys/fm/ldom.h>
36 
37 struct cpu_walk_data {
38 	tnode_t		*parent;	/* walk start node */
39 	ldom_hdl_t	*lhp;		/* ldom handle */
40 	int		(*func)(ldom_hdl_t *, nvlist_t *); /* callback func */
41 	int		err;		/* walk errors count */
42 	int		online;		/* online cpus count */
43 	int		offline;	/* offline cpus count */
44 	int		fail;		/* callback fails */
45 };
46 
47 static topo_method_f
48 	cpu_retire, cpu_unretire, cpu_service_state,
49 	cpu_unusable, mem_asru_compute, dimm_page_unusable,
50 	dimm_page_service_state, dimm_page_retire, dimm_page_unretire;
51 
52 const topo_method_t pi_cpu_methods[] = {
53 	{ TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC,
54 	    TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL,
55 	    cpu_retire },
56 	{ TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC,
57 	    TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL,
58 	    cpu_unretire },
59 	{ TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
60 	    TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
61 	    cpu_service_state },
62 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
63 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
64 	    cpu_unusable },
65 	{ NULL }
66 };
67 
68 const topo_method_t pi_mem_methods[] = {
69 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
70 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
71 	    mem_asru_compute },
72 	{ TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
73 	    TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
74 	    dimm_page_service_state },
75 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
76 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
77 	    dimm_page_unusable },
78 	{ TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC,
79 	    TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL,
80 	    dimm_page_retire },
81 	{ TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC,
82 	    TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL,
83 	    dimm_page_unretire },
84 	{ NULL }
85 };
86 
87 static ldom_hdl_t *pi_lhp = NULL;
88 
89 #pragma init(pi_ldom_init)
90 static void
pi_ldom_init(void)91 pi_ldom_init(void)
92 {
93 	pi_lhp = ldom_init(NULL, NULL);
94 }
95 
96 #pragma fini(pi_ldom_fini)
97 static void
pi_ldom_fini(void)98 pi_ldom_fini(void)
99 {
100 	if (pi_lhp != NULL)
101 		ldom_fini(pi_lhp);
102 }
103 
104 static int
set_retnvl(topo_mod_t * mod,nvlist_t ** out,const char * retname,uint32_t ret)105 set_retnvl(topo_mod_t *mod, nvlist_t **out, const char *retname, uint32_t ret)
106 {
107 	nvlist_t *nvl;
108 
109 	topo_mod_dprintf(mod, "topo method set \"%s\" = %u\n", retname, ret);
110 
111 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) < 0)
112 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
113 
114 	if (nvlist_add_uint32(nvl, retname, ret) != 0) {
115 		nvlist_free(nvl);
116 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
117 	}
118 
119 	*out = nvl;
120 	return (0);
121 }
122 
123 /*
124  * For each visited cpu node, call the callback function with its ASRU.
125  */
126 static int
cpu_walker(topo_mod_t * mod,tnode_t * node,void * pdata)127 cpu_walker(topo_mod_t *mod, tnode_t *node, void *pdata)
128 {
129 	struct cpu_walk_data *swdp = pdata;
130 	nvlist_t *asru;
131 	int err, rc;
132 
133 	/*
134 	 * Terminate the walk if we reach start-node's sibling
135 	 */
136 	if (node != swdp->parent &&
137 	    topo_node_parent(node) == topo_node_parent(swdp->parent))
138 		return (TOPO_WALK_TERMINATE);
139 
140 	if (strcmp(topo_node_name(node), CPU) != 0 &&
141 	    strcmp(topo_node_name(node), STRAND) != 0)
142 		return (TOPO_WALK_NEXT);
143 
144 	if (topo_node_asru(node, &asru, NULL, &err) != 0) {
145 		swdp->fail++;
146 		return (TOPO_WALK_NEXT);
147 	}
148 
149 	rc = swdp->func(swdp->lhp, asru);
150 
151 	/*
152 	 * The "offline" and "online" counter are only useful for the "status"
153 	 * callback.
154 	 */
155 	if (rc == P_OFFLINE || rc == P_FAULTED) {
156 		swdp->offline++;
157 		err = 0;
158 	} else if (rc == P_ONLINE) {
159 		swdp->online++;
160 		err = 0;
161 	} else {
162 		swdp->fail++;
163 		err = errno;
164 	}
165 
166 	/* dump out status info if debug is turned on. */
167 	if (getenv("TOPOCHIPDBG") != NULL ||
168 	    getenv("TOPOSUN4VPIDBG") != NULL) {
169 		const char *op;
170 		char *fmristr = NULL;
171 
172 		if (swdp->func == ldom_fmri_retire)
173 			op = "retire";
174 		else if (swdp->func == ldom_fmri_unretire)
175 			op = "unretire";
176 		else if (swdp->func == ldom_fmri_status)
177 			op = "check status";
178 		else
179 			op = "unknown op";
180 
181 		(void) topo_mod_nvl2str(mod, asru, &fmristr);
182 		topo_mod_dprintf(mod, "%s cpu (%s): rc = %d, err = %s\n",
183 		    op, fmristr == NULL ? "unknown fmri" : fmristr,
184 		    rc, strerror(err));
185 		if (fmristr != NULL)
186 			topo_mod_strfree(mod, fmristr);
187 	}
188 
189 	nvlist_free(asru);
190 	return (TOPO_WALK_NEXT);
191 }
192 
193 static int
walk_cpus(topo_mod_t * mod,struct cpu_walk_data * swdp,tnode_t * parent,int (* func)(ldom_hdl_t *,nvlist_t *))194 walk_cpus(topo_mod_t *mod, struct cpu_walk_data *swdp, tnode_t *parent,
195     int (*func)(ldom_hdl_t *, nvlist_t *))
196 {
197 	topo_walk_t *twp;
198 	int err;
199 
200 	swdp->lhp = pi_lhp;
201 	swdp->parent = parent;
202 	swdp->func = func;
203 	swdp->err = swdp->offline = swdp->online = swdp->fail = 0;
204 
205 	/*
206 	 * Return failure if ldom service is not initialized.
207 	 */
208 	if (pi_lhp == NULL) {
209 		swdp->fail++;
210 		return (0);
211 	}
212 
213 	twp = topo_mod_walk_init(mod, parent, cpu_walker, swdp, &err);
214 	if (twp == NULL)
215 		return (-1);
216 
217 	err = topo_walk_step(twp, TOPO_WALK_CHILD);
218 	topo_walk_fini(twp);
219 
220 	if (err == TOPO_WALK_ERR || swdp->err > 0)
221 		return (-1);
222 
223 	return (0);
224 }
225 
226 /* ARGSUSED */
227 int
cpu_retire(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)228 cpu_retire(topo_mod_t *mod, tnode_t *node, topo_version_t version,
229     nvlist_t *in, nvlist_t **out)
230 {
231 	struct cpu_walk_data swd;
232 	uint32_t rc;
233 
234 	if (version > TOPO_METH_RETIRE_VERSION)
235 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
236 
237 	if (walk_cpus(mod, &swd, node, ldom_fmri_retire) == -1)
238 		return (-1);
239 
240 	rc = swd.fail > 0 ? FMD_AGENT_RETIRE_FAIL : FMD_AGENT_RETIRE_DONE;
241 
242 	return (set_retnvl(mod, out, TOPO_METH_RETIRE_RET, rc));
243 }
244 
245 /* ARGSUSED */
246 int
cpu_unretire(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)247 cpu_unretire(topo_mod_t *mod, tnode_t *node, topo_version_t version,
248     nvlist_t *in, nvlist_t **out)
249 {
250 	struct cpu_walk_data swd;
251 	uint32_t rc;
252 
253 	if (version > TOPO_METH_UNRETIRE_VERSION)
254 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
255 
256 	if (walk_cpus(mod, &swd, node, ldom_fmri_unretire) == -1)
257 		return (-1);
258 
259 	rc = swd.fail > 0 ? FMD_AGENT_RETIRE_FAIL : FMD_AGENT_RETIRE_DONE;
260 
261 	return (set_retnvl(mod, out, TOPO_METH_UNRETIRE_RET, rc));
262 }
263 
264 /* ARGSUSED */
265 int
cpu_service_state(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)266 cpu_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
267     nvlist_t *in, nvlist_t **out)
268 {
269 	struct cpu_walk_data swd;
270 	uint32_t rc;
271 
272 	if (version > TOPO_METH_SERVICE_STATE_VERSION)
273 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
274 
275 	if (walk_cpus(mod, &swd, node, ldom_fmri_status) == -1)
276 		return (-1);
277 
278 	if (swd.fail > 0)
279 		rc = FMD_SERVICE_STATE_UNKNOWN;
280 	else if (swd.offline > 0)
281 		rc = swd.online > 0 ? FMD_SERVICE_STATE_DEGRADED :
282 		    FMD_SERVICE_STATE_UNUSABLE;
283 	else
284 		rc = FMD_SERVICE_STATE_OK;
285 
286 	return (set_retnvl(mod, out, TOPO_METH_SERVICE_STATE_RET, rc));
287 }
288 
289 /* ARGSUSED */
290 int
cpu_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)291 cpu_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
292     nvlist_t *in, nvlist_t **out)
293 {
294 	struct cpu_walk_data swd;
295 	uint32_t rc;
296 
297 	if (version > TOPO_METH_UNUSABLE_VERSION)
298 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
299 
300 	if (walk_cpus(mod, &swd, node, ldom_fmri_status) == -1)
301 		return (-1);
302 
303 	rc = (swd.offline > 0 && swd.fail + swd.online == 0) ? 1 : 0;
304 
305 	return (set_retnvl(mod, out, TOPO_METH_UNUSABLE_RET, rc));
306 }
307 
308 static nvlist_t *
mem_fmri_create(topo_mod_t * mod,char * serial,char * label)309 mem_fmri_create(topo_mod_t *mod, char *serial, char *label)
310 {
311 	int err;
312 	nvlist_t *fmri;
313 
314 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
315 		return (NULL);
316 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_MEM_SCHEME_VERSION);
317 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM);
318 	if (serial != NULL)
319 		err |= nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID,
320 		    &serial, 1);
321 	if (label != NULL)
322 		err |= nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, label);
323 	if (err != 0) {
324 		nvlist_free(fmri);
325 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
326 		return (NULL);
327 	}
328 
329 	return (fmri);
330 }
331 
332 /* Topo Methods */
333 static int
mem_asru_compute(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)334 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version,
335     nvlist_t *in, nvlist_t **out)
336 {
337 	nvlist_t *asru, *pargs, *args, *hcsp;
338 	int err;
339 	char *serial = NULL, *label = NULL;
340 	uint64_t pa, offset;
341 
342 	if (version > TOPO_METH_ASRU_COMPUTE_VERSION)
343 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
344 
345 	if (strcmp(topo_node_name(node), DIMM) != 0)
346 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
347 
348 	pargs = NULL;
349 
350 	if (nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0)
351 		(void) nvlist_lookup_string(pargs, FM_FMRI_HC_SERIAL_ID,
352 		    &serial);
353 	if (serial == NULL &&
354 	    nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) == 0)
355 		(void) nvlist_lookup_string(args, FM_FMRI_HC_SERIAL_ID,
356 		    &serial);
357 
358 	(void) topo_node_label(node, &label, &err);
359 
360 	asru = mem_fmri_create(mod, serial, label);
361 
362 	if (label != NULL)
363 		topo_mod_strfree(mod, label);
364 
365 	if (asru == NULL)
366 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
367 
368 	err = 0;
369 
370 	/*
371 	 * For a memory page, 'in' includes an hc-specific member which
372 	 * specifies physaddr and/or offset. Set them in asru as well.
373 	 */
374 	if (pargs && nvlist_lookup_nvlist(pargs,
375 	    FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
376 		if (nvlist_lookup_uint64(hcsp,
377 		    FM_FMRI_HC_SPECIFIC_PHYSADDR, &pa) == 0)
378 			err += nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR,
379 			    pa);
380 		if (nvlist_lookup_uint64(hcsp,
381 		    FM_FMRI_HC_SPECIFIC_OFFSET, &offset) == 0)
382 			err += nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET,
383 			    offset);
384 	}
385 
386 
387 	if (err != 0 || topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
388 		nvlist_free(asru);
389 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
390 	}
391 
392 	err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU);
393 	err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI);
394 	err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru);
395 	nvlist_free(asru);
396 
397 	if (err != 0) {
398 		nvlist_free(*out);
399 		*out = NULL;
400 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
401 	}
402 
403 	return (0);
404 }
405 
406 static boolean_t
is_page_fmri(nvlist_t * nvl)407 is_page_fmri(nvlist_t *nvl)
408 {
409 	nvlist_t *hcsp;
410 	uint64_t val;
411 
412 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) == 0 &&
413 	    (nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_OFFSET,
414 	    &val) == 0 ||
415 	    nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET,
416 	    &val) == 0 ||
417 	    nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_PHYSADDR,
418 	    &val) == 0 ||
419 	    nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR,
420 	    &val) == 0))
421 		return (B_TRUE);
422 
423 	return (B_FALSE);
424 }
425 
426 static int
dimm_page_service_state(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)427 dimm_page_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
428     nvlist_t *in, nvlist_t **out)
429 {
430 	uint32_t rc = FMD_SERVICE_STATE_OK;
431 	nvlist_t *asru;
432 	int err;
433 
434 	if (version > TOPO_METH_SERVICE_STATE_VERSION)
435 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
436 
437 	if (pi_lhp != NULL && is_page_fmri(in) &&
438 	    topo_node_asru(node, &asru, in, &err) == 0) {
439 		err = ldom_fmri_status(pi_lhp, asru);
440 
441 		if (err == 0 || err == EINVAL)
442 			rc = FMD_SERVICE_STATE_UNUSABLE;
443 		else if (err == EAGAIN)
444 			rc = FMD_SERVICE_STATE_ISOLATE_PENDING;
445 		nvlist_free(asru);
446 	}
447 
448 	return (set_retnvl(mod, out, TOPO_METH_SERVICE_STATE_RET, rc));
449 }
450 
451 static int
dimm_page_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)452 dimm_page_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
453     nvlist_t *in, nvlist_t **out)
454 {
455 	uint32_t rc = 0;
456 	nvlist_t *asru;
457 	int err;
458 
459 	if (version > TOPO_METH_UNUSABLE_VERSION)
460 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
461 
462 	if (pi_lhp != NULL && is_page_fmri(in) &&
463 	    topo_node_asru(node, &asru, in, &err) == 0) {
464 		err = ldom_fmri_status(pi_lhp, asru);
465 
466 		if (err == 0 || err == EINVAL)
467 			rc = 1;
468 		nvlist_free(asru);
469 	}
470 
471 	return (set_retnvl(mod, out, TOPO_METH_UNUSABLE_RET, rc));
472 }
473 
474 static int
dimm_page_retire(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)475 dimm_page_retire(topo_mod_t *mod, tnode_t *node, topo_version_t version,
476     nvlist_t *in, nvlist_t **out)
477 {
478 	uint32_t rc = FMD_AGENT_RETIRE_FAIL;
479 	nvlist_t *asru;
480 	int err;
481 
482 	if (version > TOPO_METH_RETIRE_VERSION)
483 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
484 
485 	if (pi_lhp != NULL && is_page_fmri(in) &&
486 	    topo_node_asru(node, &asru, in, &err) == 0) {
487 		err = ldom_fmri_retire(pi_lhp, asru);
488 
489 		if (err == 0 || err == EIO || err == EINVAL)
490 			rc = FMD_AGENT_RETIRE_DONE;
491 		else if (err == EAGAIN)
492 			rc = FMD_AGENT_RETIRE_ASYNC;
493 		nvlist_free(asru);
494 	}
495 
496 	return (set_retnvl(mod, out, TOPO_METH_RETIRE_RET, rc));
497 }
498 
499 static int
dimm_page_unretire(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)500 dimm_page_unretire(topo_mod_t *mod, tnode_t *node, topo_version_t version,
501     nvlist_t *in, nvlist_t **out)
502 {
503 	uint32_t rc = FMD_AGENT_RETIRE_FAIL;
504 	nvlist_t *asru;
505 	int err;
506 
507 	if (version > TOPO_METH_UNRETIRE_VERSION)
508 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
509 
510 	if (pi_lhp != NULL && is_page_fmri(in) &&
511 	    topo_node_asru(node, &asru, in, &err) == 0) {
512 		err = ldom_fmri_unretire(pi_lhp, asru);
513 
514 		if (err == 0 || err == EIO)
515 			rc = FMD_AGENT_RETIRE_DONE;
516 		nvlist_free(asru);
517 	}
518 
519 	return (set_retnvl(mod, out, TOPO_METH_UNRETIRE_RET, rc));
520 }
521