cma_main.c revision 9120:fe1f7d8cd967
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#include <cma.h>
28
29#include <unistd.h>
30#include <fcntl.h>
31#include <strings.h>
32#include <errno.h>
33#include <time.h>
34#include <fm/fmd_api.h>
35#include <sys/fm/protocol.h>
36#include <sys/systeminfo.h>
37#include <sys/utsname.h>
38
39#ifdef sun4v
40#include <sys/fm/ldom.h>
41
42static fmd_hdl_t *init_hdl;
43ldom_hdl_t *cma_lhp;
44#endif
45
46#ifdef i386
47boolean_t cma_is_native;
48#endif
49
50extern const char *fmd_fmri_get_platform();
51
52cma_t cma;
53
54cma_stats_t cma_stats = {
55	{ "cpu_flts", FMD_TYPE_UINT64, "cpu faults resolved" },
56	{ "cpu_repairs", FMD_TYPE_UINT64, "cpu faults repaired" },
57	{ "cpu_fails", FMD_TYPE_UINT64, "cpu faults unresolveable" },
58	{ "cpu_blfails", FMD_TYPE_UINT64, "failed cpu blacklists" },
59	{ "cpu_supp", FMD_TYPE_UINT64, "cpu offlines suppressed" },
60	{ "cpu_blsupp", FMD_TYPE_UINT64, "cpu blacklists suppressed" },
61	{ "page_flts", FMD_TYPE_UINT64, "page faults resolved" },
62	{ "page_repairs", FMD_TYPE_UINT64, "page faults repaired" },
63	{ "page_fails", FMD_TYPE_UINT64, "page faults unresolveable" },
64	{ "page_supp", FMD_TYPE_UINT64, "page retires suppressed" },
65	{ "page_nonent", FMD_TYPE_UINT64, "retires for non-existent fmris" },
66	{ "bad_flts", FMD_TYPE_UINT64, "invalid fault events received" },
67	{ "nop_flts", FMD_TYPE_UINT64, "inapplicable fault events received" },
68	{ "auto_flts", FMD_TYPE_UINT64, "auto-close faults received" }
69};
70
71typedef struct cma_subscriber {
72	const char *subr_class;
73	const char *subr_sname;
74	uint_t subr_svers;
75	int (*subr_func)(fmd_hdl_t *, nvlist_t *, nvlist_t *, const char *,
76	    boolean_t);
77} cma_subscriber_t;
78
79static const cma_subscriber_t cma_subrs[] = {
80#if defined(i386)
81	/*
82	 * On x86, the ASRUs are expected to be in hc scheme.  When
83	 * cpumem-retire wants to retire a cpu or mem page, it calls the
84	 * methods registered in the topo node to do that.  The topo
85	 * enumerator, which necessarily knows all the config info that
86	 * we'd ever need in deciding what/how to retire etc.  This takes
87	 * away much of that complexity from the agent into the entity
88	 * that knows all config/topo information.
89	 */
90	{ "fault.memory.page", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
91	    cma_page_retire },
92	{ "fault.memory.page_sb", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
93	    cma_page_retire },
94	{ "fault.memory.page_ck", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
95	    cma_page_retire },
96	{ "fault.memory.page_ue", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
97	    cma_page_retire },
98	{ "fault.memory.generic-x86.page_ce", FM_FMRI_SCHEME_HC,
99	    FM_HC_SCHEME_VERSION, cma_page_retire },
100	{ "fault.memory.generic-x86.page_ue", FM_FMRI_SCHEME_HC,
101	    FM_HC_SCHEME_VERSION, cma_page_retire },
102	{ "fault.memory.intel.page_ce", FM_FMRI_SCHEME_HC,
103	    FM_HC_SCHEME_VERSION, cma_page_retire },
104	{ "fault.memory.intel.page_ue", FM_FMRI_SCHEME_HC,
105	    FM_HC_SCHEME_VERSION, cma_page_retire },
106	{ "fault.memory.dimm", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
107	    NULL },
108	{ "fault.memory.dimm_sb", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
109	    NULL },
110	{ "fault.memory.dimm_ck", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
111	    NULL },
112	{ "fault.memory.dimm_ue", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
113	    NULL },
114	{ "fault.memory.generic-x86.dimm_ce", FM_FMRI_SCHEME_HC,
115	    FM_HC_SCHEME_VERSION, NULL },
116	{ "fault.memory.generic-x86.dimm_ue", FM_FMRI_SCHEME_HC,
117	    FM_HC_SCHEME_VERSION, NULL },
118	{ "fault.memory.intel.dimm_ce", FM_FMRI_SCHEME_HC,
119	    FM_HC_SCHEME_VERSION, NULL },
120	{ "fault.memory.intel.dimm_ue", FM_FMRI_SCHEME_HC,
121	    FM_HC_SCHEME_VERSION, NULL },
122	{ "fault.memory.intel.fbd.*", FM_FMRI_SCHEME_HC,
123	    FM_HC_SCHEME_VERSION, NULL },
124	{ "fault.memory.dimm_testfail", FM_FMRI_SCHEME_HC,
125	    FM_HC_SCHEME_VERSION, NULL },
126	{ "fault.memory.bank", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
127	    NULL },
128	{ "fault.memory.datapath", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
129	    NULL },
130	{ "fault.cpu.intel.quickpath.*", FM_FMRI_SCHEME_HC,
131	    FM_HC_SCHEME_VERSION, NULL },
132	{ "fault.cpu.generic-x86.mc", FM_FMRI_SCHEME_HC,
133	    FM_HC_SCHEME_VERSION, NULL },
134	{ "fault.cpu.intel.dma", FM_FMRI_SCHEME_HC,
135	    FM_HC_SCHEME_VERSION, NULL },
136	{ "fault.cpu.intel.dma", FM_FMRI_SCHEME_CPU,
137	    FM_CPU_SCHEME_VERSION, NULL },
138
139	/*
140	 * The ASRU for cpu faults are in cpu scheme on native and in hc
141	 * scheme on xpv.  So each cpu fault class needs to be listed twice.
142	 */
143
144	/*
145	 * The following faults do NOT retire a cpu thread,
146	 * and therefore must be intercepted before
147	 * the default "fault.cpu.*" dispatch to cma_cpu_hc_retire.
148	 */
149	{ "fault.cpu.amd.dramchannel", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
150	    NULL },
151	{ "fault.cpu.amd.dramchannel", FM_FMRI_SCHEME_CPU,
152	    FM_CPU_SCHEME_VERSION, NULL },
153	{ "fault.cpu.generic-x86.bus_interconnect_memory", FM_FMRI_SCHEME_HC,
154	    FM_HC_SCHEME_VERSION, NULL },
155	{ "fault.cpu.generic-x86.bus_interconnect_memory", FM_FMRI_SCHEME_CPU,
156	    FM_CPU_SCHEME_VERSION, NULL },
157	{ "fault.cpu.generic-x86.bus_interconnect_io", FM_FMRI_SCHEME_HC,
158	    FM_HC_SCHEME_VERSION, NULL },
159	{ "fault.cpu.generic-x86.bus_interconnect_io", FM_FMRI_SCHEME_CPU,
160	    FM_CPU_SCHEME_VERSION, NULL },
161	{ "fault.cpu.generic-x86.bus_interconnect", FM_FMRI_SCHEME_HC,
162	    FM_HC_SCHEME_VERSION, NULL },
163	{ "fault.cpu.generic-x86.bus_interconnect", FM_FMRI_SCHEME_CPU,
164	    FM_CPU_SCHEME_VERSION, NULL },
165	{ "fault.cpu.intel.bus_interconnect_memory", FM_FMRI_SCHEME_HC,
166	    FM_HC_SCHEME_VERSION, NULL },
167	{ "fault.cpu.intel.bus_interconnect_memory", FM_FMRI_SCHEME_CPU,
168	    FM_CPU_SCHEME_VERSION, NULL },
169	{ "fault.cpu.intel.bus_interconnect_io", FM_FMRI_SCHEME_HC,
170	    FM_HC_SCHEME_VERSION, NULL },
171	{ "fault.cpu.intel.bus_interconnect_io", FM_FMRI_SCHEME_CPU,
172	    FM_CPU_SCHEME_VERSION, NULL },
173	{ "fault.cpu.intel.bus_interconnect", FM_FMRI_SCHEME_HC,
174	    FM_HC_SCHEME_VERSION, NULL },
175	{ "fault.cpu.intel.bus_interconnect", FM_FMRI_SCHEME_CPU,
176	    FM_CPU_SCHEME_VERSION, NULL },
177	{ "fault.cpu.intel.nb.*", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
178	    NULL },
179	{ "fault.cpu.intel.nb.*", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
180	    NULL },
181	{ "fault.cpu.intel.dma", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
182	    NULL },
183	{ "fault.cpu.intel.dma", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
184	    NULL },
185	{ "fault.cpu.*", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
186	    cma_cpu_hc_retire },
187	{ "fault.cpu.*", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
188	    cma_cpu_hc_retire },
189#elif defined(sun4v)
190	/*
191	 * The following are PI sun4v faults
192	 */
193	{ "fault.memory.memlink", FM_FMRI_SCHEME_HC,
194	    FM_HC_SCHEME_VERSION, NULL },
195	{ "fault.memory.memlink-uc", FM_FMRI_SCHEME_HC,
196	    FM_HC_SCHEME_VERSION, NULL },
197	{ "fault.memory.memlink-failover", FM_FMRI_SCHEME_HC,
198	    FM_HC_SCHEME_VERSION, NULL },
199	{ "fault.memory.dimm-ue-imminent", FM_FMRI_SCHEME_HC,
200	    FM_HC_SCHEME_VERSION, NULL },
201	{ "fault.memory.dram-ue-imminent", FM_FMRI_SCHEME_HC,
202	    FM_HC_SCHEME_VERSION, NULL },
203	{ "fault.memory.dimm-page-retires-excessive", FM_FMRI_SCHEME_HC,
204	    FM_HC_SCHEME_VERSION, NULL },
205	{ "fault.memory.page", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
206	    cma_page_retire },
207	{ "fault.memory.dimm", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
208	    NULL },
209	{ "fault.memory.dimm_sb", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
210	    NULL },
211	{ "fault.memory.dimm_ck", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
212	    NULL },
213	{ "fault.memory.dimm_ue", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
214	    NULL },
215	{ "fault.memory.dimm-page-retires-excessive", FM_FMRI_SCHEME_MEM,
216	    FM_MEM_SCHEME_VERSION, NULL },
217	{ "fault.memory.dimm-ue-imminent", FM_FMRI_SCHEME_MEM,
218	    FM_MEM_SCHEME_VERSION, NULL },
219	{ "fault.memory.dram-ue-imminent", FM_FMRI_SCHEME_MEM,
220	    FM_MEM_SCHEME_VERSION, NULL },
221	{ "fault.memory.bank", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
222	    NULL },
223	{ "fault.memory.datapath", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
224	    NULL },
225	{ "fault.memory.link-c", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
226	    NULL },
227	{ "fault.memory.link-u", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
228	    NULL },
229	{ "fault.memory.link-f", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
230	    NULL },
231	{ "fault.memory.link-c", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
232	    NULL },
233	{ "fault.memory.link-u", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
234	    NULL },
235	{ "fault.memory.link-f", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
236	    NULL },
237
238	/*
239	 * The following ultraSPARC-T1/T2 faults do NOT retire a cpu thread,
240	 * and therefore must be intercepted before
241	 * the default "fault.cpu.*" dispatch to cma_cpu_hc_retire.
242	 */
243	{ "fault.cpu.*.l2cachedata", FM_FMRI_SCHEME_CPU,
244	    FM_CPU_SCHEME_VERSION, NULL },
245	{ "fault.cpu.*.l2cachetag", FM_FMRI_SCHEME_CPU,
246	    FM_CPU_SCHEME_VERSION, NULL },
247	{ "fault.cpu.*.l2cachectl", FM_FMRI_SCHEME_CPU,
248	    FM_CPU_SCHEME_VERSION, NULL },
249	{ "fault.cpu.*.l2data-c", FM_FMRI_SCHEME_CPU,
250	    FM_CPU_SCHEME_VERSION, NULL },
251	{ "fault.cpu.*.l2data-u", FM_FMRI_SCHEME_CPU,
252	    FM_CPU_SCHEME_VERSION, NULL },
253	{ "fault.cpu.*.mau", FM_FMRI_SCHEME_CPU,
254	    FM_CPU_SCHEME_VERSION, NULL },
255	{ "fault.cpu.*.lfu-u", FM_FMRI_SCHEME_CPU,
256	    FM_CPU_SCHEME_VERSION, NULL },
257	{ "fault.cpu.*.lfu-f", FM_FMRI_SCHEME_CPU,
258	    FM_CPU_SCHEME_VERSION, NULL },
259	{ "fault.cpu.*.lfu-p", FM_FMRI_SCHEME_CPU,
260	    FM_CPU_SCHEME_VERSION, NULL },
261	{ "fault.cpu.ultraSPARC-T1.freg", FM_FMRI_SCHEME_CPU,
262	    FM_CPU_SCHEME_VERSION, NULL },
263	{ "fault.cpu.ultraSPARC-T1.l2cachedata", FM_FMRI_SCHEME_CPU,
264	    FM_CPU_SCHEME_VERSION, NULL },
265	{ "fault.cpu.ultraSPARC-T1.l2cachetag", FM_FMRI_SCHEME_CPU,
266	    FM_CPU_SCHEME_VERSION, NULL },
267	{ "fault.cpu.ultraSPARC-T1.l2cachectl", FM_FMRI_SCHEME_CPU,
268	    FM_CPU_SCHEME_VERSION, NULL },
269	{ "fault.cpu.ultraSPARC-T1.mau", FM_FMRI_SCHEME_CPU,
270	    FM_CPU_SCHEME_VERSION, NULL },
271	{ "fault.cpu.ultraSPARC-T2plus.chip", FM_FMRI_SCHEME_HC,
272	    FM_HC_SCHEME_VERSION, NULL },
273	{ "fault.cpu.*", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION,
274	    cma_cpu_hc_retire },
275	{ "fault.cpu.*", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
276	    cma_cpu_hc_retire },
277#elif defined(opl)
278	{ "fault.memory.page", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
279	    cma_page_retire },
280	{ "fault.memory.dimm", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
281	    NULL },
282	{ "fault.memory.dimm-page-retires-excessive", FM_FMRI_SCHEME_MEM,
283	    FM_MEM_SCHEME_VERSION, NULL },
284	{ "fault.memory.dimm-ue-imminent", FM_FMRI_SCHEME_MEM,
285	    FM_MEM_SCHEME_VERSION, NULL },
286	{ "fault.memory.dram-ue-imminent", FM_FMRI_SCHEME_MEM,
287	    FM_MEM_SCHEME_VERSION, NULL },
288	{ "fault.memory.bank", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
289	    NULL },
290	{ "fault.cpu.SPARC64-VI.*", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
291	    cma_cpu_cpu_retire },
292	{ "fault.cpu.SPARC64-VII.*", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
293	    cma_cpu_cpu_retire },
294	{ "fault.chassis.SPARC-Enterprise.cpu.SPARC64-VI.core.se",
295		FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, cma_cpu_hc_retire },
296	{ "fault.chassis.SPARC-Enterprise.cpu.SPARC64-VI.core.se-offlinereq",
297		FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, cma_cpu_hc_retire },
298	{ "fault.chassis.SPARC-Enterprise.cpu.SPARC64-VI.core.ce",
299		FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, cma_cpu_hc_retire },
300	{ "fault.chassis.SPARC-Enterprise.cpu.SPARC64-VI.core.ce-offlinereq",
301		FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, cma_cpu_hc_retire },
302	{ "fault.chassis.SPARC-Enterprise.cpu.SPARC64-VII.core.se",
303		FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, cma_cpu_hc_retire },
304	{ "fault.chassis.SPARC-Enterprise.cpu.SPARC64-VII.core.se-offlinereq",
305		FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, cma_cpu_hc_retire },
306	{ "fault.chassis.SPARC-Enterprise.cpu.SPARC64-VII.core.ce",
307		FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, cma_cpu_hc_retire },
308	{ "fault.chassis.SPARC-Enterprise.cpu.SPARC64-VII.core.ce-offlinereq",
309		FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, cma_cpu_hc_retire },
310#else
311	/*
312	 * For platforms excluding i386, sun4v and opl.
313	 */
314	{ "fault.memory.page", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
315	    cma_page_retire },
316	{ "fault.memory.page_sb", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
317	    cma_page_retire },
318	{ "fault.memory.page_ck", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
319	    cma_page_retire },
320	{ "fault.memory.page_ue", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
321	    cma_page_retire },
322	{ "fault.memory.dimm", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
323	    NULL },
324	{ "fault.memory.dimm_sb", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
325	    NULL },
326	{ "fault.memory.dimm_ck", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
327	    NULL },
328	{ "fault.memory.dimm_ue", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
329	    NULL },
330	{ "fault.memory.dimm-page-retires-excessive", FM_FMRI_SCHEME_MEM,
331	    FM_MEM_SCHEME_VERSION, NULL },
332	{ "fault.memory.dimm-ue-imminent", FM_FMRI_SCHEME_MEM,
333	    FM_MEM_SCHEME_VERSION, NULL },
334	{ "fault.memory.dram-ue-imminent", FM_FMRI_SCHEME_MEM,
335	    FM_MEM_SCHEME_VERSION, NULL },
336	{ "fault.memory.dimm_testfail", FM_FMRI_SCHEME_MEM,
337	    FM_MEM_SCHEME_VERSION, NULL },
338	{ "fault.memory.bank", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
339	    NULL },
340	{ "fault.memory.datapath", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION,
341	    NULL },
342
343	/*
344	 * The following faults do NOT retire a cpu thread,
345	 * and therefore must be intercepted before
346	 * the default "fault.cpu.*" dispatch to cma_cpu_cpu_retire.
347	 */
348	{ "fault.cpu.ultraSPARC-IVplus.l2cachedata-line",
349	    FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
350	    cma_cache_way_retire },
351	{ "fault.cpu.ultraSPARC-IVplus.l3cachedata-line",
352	    FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
353	    cma_cache_way_retire },
354	{ "fault.cpu.ultraSPARC-IVplus.l2cachetag-line",
355	    FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
356	    cma_cache_way_retire },
357	{ "fault.cpu.ultraSPARC-IVplus.l3cachetag-line",
358	    FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
359	    cma_cache_way_retire },
360
361	/*
362	 * Default "fault.cpu.*" for "cpu" scheme ASRU dispatch.
363	 */
364	{ "fault.cpu.*", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION,
365	    cma_cpu_cpu_retire },
366#endif
367	{ NULL, NULL, 0, NULL }
368};
369
370static const cma_subscriber_t *
371nvl2subr(fmd_hdl_t *hdl, nvlist_t *nvl, nvlist_t **asrup)
372{
373	const cma_subscriber_t *sp;
374	nvlist_t *asru;
375	char *scheme;
376	uint8_t version;
377	boolean_t retire;
378
379	if (nvlist_lookup_boolean_value(nvl, FM_SUSPECT_RETIRE, &retire) == 0 &&
380	    retire == 0) {
381		fmd_hdl_debug(hdl, "cma_recv: retire suppressed");
382		return (NULL);
383	}
384
385	if (nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU, &asru) != 0 ||
386	    nvlist_lookup_string(asru, FM_FMRI_SCHEME, &scheme) != 0 ||
387	    nvlist_lookup_uint8(asru, FM_VERSION, &version) != 0) {
388		cma_stats.bad_flts.fmds_value.ui64++;
389		return (NULL);
390	}
391
392	for (sp = cma_subrs; sp->subr_class != NULL; sp++) {
393		if (fmd_nvl_class_match(hdl, nvl, sp->subr_class) &&
394		    strcmp(scheme, sp->subr_sname) == 0 &&
395		    version <= sp->subr_svers) {
396			*asrup = asru;
397			return (sp);
398		}
399	}
400
401	cma_stats.nop_flts.fmds_value.ui64++;
402	return (NULL);
403}
404
405static void
406cma_recv_list(fmd_hdl_t *hdl, nvlist_t *nvl, const char *class)
407{
408	char *uuid = NULL;
409	nvlist_t **nva;
410	uint_t nvc = 0;
411	uint_t keepopen;
412	int err = 0;
413	nvlist_t *asru = NULL;
414	uint32_t index;
415
416	err |= nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid);
417	err |= nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST,
418	    &nva, &nvc);
419	if (err != 0) {
420		cma_stats.bad_flts.fmds_value.ui64++;
421		return;
422	}
423
424	keepopen = nvc;
425	while (nvc-- != 0 && (strcmp(class, FM_LIST_SUSPECT_CLASS) != 0 ||
426	    !fmd_case_uuclosed(hdl, uuid))) {
427		nvlist_t *nvl = *nva++;
428		const cma_subscriber_t *subr;
429		int has_fault;
430
431		if ((subr = nvl2subr(hdl, nvl, &asru)) == NULL)
432			continue;
433
434		/*
435		 * A handler returns CMA_RA_SUCCESS to indicate that
436		 * from this suspects  point-of-view the case may be
437		 * closed, CMA_RA_FAILURE otherwise.
438		 * A handler must not close the case itself.
439		 */
440		if (subr->subr_func != NULL) {
441			has_fault = fmd_nvl_fmri_has_fault(hdl, asru,
442			    FMD_HAS_FAULT_ASRU, NULL);
443			if (strcmp(class, FM_LIST_SUSPECT_CLASS) == 0) {
444				if (has_fault == 1)
445					err = subr->subr_func(hdl, nvl, asru,
446					    uuid, 0);
447			} else {
448				if (has_fault == 0)
449					err = subr->subr_func(hdl, nvl, asru,
450					    uuid, 1);
451			}
452			if (err == CMA_RA_SUCCESS)
453				keepopen--;
454		}
455	}
456
457	/*
458	 * Do not close the case if we are handling cache faults.
459	 */
460	if (asru != NULL) {
461		if (nvlist_lookup_uint32(asru, FM_FMRI_CPU_CACHE_INDEX,
462		    &index) != 0) {
463			if (!keepopen && strcmp(class,
464			    FM_LIST_SUSPECT_CLASS) == 0) {
465				fmd_case_uuclose(hdl, uuid);
466			}
467		}
468	}
469
470	if (!keepopen && strcmp(class, FM_LIST_REPAIRED_CLASS) == 0)
471		fmd_case_uuresolved(hdl, uuid);
472}
473
474static void
475cma_recv_one(fmd_hdl_t *hdl, nvlist_t *nvl)
476{
477	const cma_subscriber_t *subr;
478	nvlist_t *asru;
479
480	if ((subr = nvl2subr(hdl, nvl, &asru)) == NULL)
481		return;
482
483	if (subr->subr_func != NULL) {
484		if (fmd_nvl_fmri_has_fault(hdl, asru,
485		    FMD_HAS_FAULT_ASRU, NULL) == 1)
486			(void) subr->subr_func(hdl, nvl, asru, NULL, 0);
487	}
488}
489
490/*ARGSUSED*/
491static void
492cma_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
493{
494	fmd_hdl_debug(hdl, "received %s\n", class);
495
496	if (strcmp(class, FM_LIST_RESOLVED_CLASS) == 0)
497		return;
498
499	if (strcmp(class, FM_LIST_SUSPECT_CLASS) == 0 ||
500	    strcmp(class, FM_LIST_REPAIRED_CLASS) == 0 ||
501	    strcmp(class, FM_LIST_UPDATED_CLASS) == 0)
502		cma_recv_list(hdl, nvl, class);
503	else
504		cma_recv_one(hdl, nvl);
505}
506
507/*ARGSUSED*/
508static void
509cma_timeout(fmd_hdl_t *hdl, id_t id, void *arg)
510{
511	if (id == cma.cma_page_timerid)
512		cma_page_retry(hdl);
513#ifdef sun4v
514	/*
515	 * cpu offline/online needs to be retried on sun4v because
516	 * ldom request can be asynchronous.
517	 */
518	else if (id == cma.cma_cpu_timerid)
519		cma_cpu_retry(hdl);
520#endif
521}
522
523#ifdef sun4v
524static void *
525cma_init_alloc(size_t size)
526{
527	return (fmd_hdl_alloc(init_hdl, size, FMD_SLEEP));
528}
529
530static void
531cma_init_free(void *addr, size_t size)
532{
533	fmd_hdl_free(init_hdl, addr, size);
534}
535#endif
536
537static const fmd_hdl_ops_t fmd_ops = {
538	cma_recv,	/* fmdo_recv */
539	cma_timeout,	/* fmdo_timeout */
540	NULL,		/* fmdo_close */
541	NULL,		/* fmdo_stats */
542	NULL,		/* fmdo_gc */
543};
544
545static const fmd_prop_t fmd_props[] = {
546	{ "cpu_tries", FMD_TYPE_UINT32, "10" },
547	{ "cpu_delay", FMD_TYPE_TIME, "1sec" },
548#ifdef sun4v
549	{ "cpu_ret_mindelay", FMD_TYPE_TIME, "5sec" },
550	{ "cpu_ret_maxdelay", FMD_TYPE_TIME, "5min" },
551#endif /* sun4v */
552	{ "cpu_offline_enable", FMD_TYPE_BOOL, "true" },
553	{ "cpu_online_enable", FMD_TYPE_BOOL, "true" },
554	{ "cpu_forced_offline", FMD_TYPE_BOOL, "true" },
555#ifdef opl
556	{ "cpu_blacklist_enable", FMD_TYPE_BOOL, "false" },
557	{ "cpu_unblacklist_enable", FMD_TYPE_BOOL, "false" },
558#else
559	{ "cpu_blacklist_enable", FMD_TYPE_BOOL, "true" },
560	{ "cpu_unblacklist_enable", FMD_TYPE_BOOL, "true" },
561#endif /* opl */
562	{ "page_ret_mindelay", FMD_TYPE_TIME, "1sec" },
563	{ "page_ret_maxdelay", FMD_TYPE_TIME, "5min" },
564	{ "page_retire_enable", FMD_TYPE_BOOL, "true" },
565	{ "page_unretire_enable", FMD_TYPE_BOOL, "true" },
566	{ NULL, 0, NULL }
567};
568
569static const fmd_hdl_info_t fmd_info = {
570	"CPU/Memory Retire Agent", CMA_VERSION, &fmd_ops, fmd_props
571};
572
573void
574_fmd_init(fmd_hdl_t *hdl)
575{
576	hrtime_t nsec;
577#ifdef i386
578	char buf[BUFSIZ];
579	const char *dom0 = "control_d";
580
581	/*
582	 * Abort the cpumem-retire module if Solaris is running under DomU.
583	 */
584	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) == -1)
585		return;
586
587	if (strncmp(buf, "i86pc", sizeof (buf)) == 0) {
588		cma_is_native = B_TRUE;
589	} else if (strncmp(buf, "i86xpv", sizeof (buf)) != 0) {
590		return;
591	} else {
592		int fd = open("/dev/xen/domcaps", O_RDONLY);
593
594		if (fd != -1) {
595			if (read(fd, buf, sizeof (buf)) <= 0 ||
596			    strncmp(buf, dom0, strlen(dom0)) != 0) {
597				(void) close(fd);
598				return;
599			}
600			(void) close(fd);
601		}
602		cma_is_native = B_FALSE;
603	}
604#endif /* i386 */
605
606	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
607		return; /* invalid data in configuration file */
608
609	fmd_hdl_subscribe(hdl, "fault.cpu.*");
610	fmd_hdl_subscribe(hdl, "fault.memory.*");
611#ifdef opl
612	fmd_hdl_subscribe(hdl, "fault.chassis.SPARC-Enterprise.cpu.*");
613#endif
614
615	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (cma_stats) /
616	    sizeof (fmd_stat_t), (fmd_stat_t *)&cma_stats);
617
618	cma.cma_cpu_tries = fmd_prop_get_int32(hdl, "cpu_tries");
619
620	nsec = fmd_prop_get_int64(hdl, "cpu_delay");
621	cma.cma_cpu_delay.tv_sec = nsec / NANOSEC;
622	cma.cma_cpu_delay.tv_nsec = nsec % NANOSEC;
623
624	cma.cma_page_mindelay = fmd_prop_get_int64(hdl, "page_ret_mindelay");
625	cma.cma_page_maxdelay = fmd_prop_get_int64(hdl, "page_ret_maxdelay");
626
627#ifdef sun4v
628	cma.cma_cpu_mindelay = fmd_prop_get_int64(hdl, "cpu_ret_mindelay");
629	cma.cma_cpu_maxdelay = fmd_prop_get_int64(hdl, "cpu_ret_maxdelay");
630#endif
631
632	cma.cma_cpu_dooffline = fmd_prop_get_int32(hdl, "cpu_offline_enable");
633	cma.cma_cpu_forcedoffline = fmd_prop_get_int32(hdl,
634	    "cpu_forced_offline");
635	cma.cma_cpu_doonline = fmd_prop_get_int32(hdl, "cpu_online_enable");
636	cma.cma_cpu_doblacklist = fmd_prop_get_int32(hdl,
637	    "cpu_blacklist_enable");
638	cma.cma_cpu_dounblacklist = fmd_prop_get_int32(hdl,
639	    "cpu_unblacklist_enable");
640	cma.cma_page_doretire = fmd_prop_get_int32(hdl, "page_retire_enable");
641	cma.cma_page_dounretire = fmd_prop_get_int32(hdl,
642	    "page_unretire_enable");
643
644	if (cma.cma_page_maxdelay < cma.cma_page_mindelay)
645		fmd_hdl_abort(hdl, "page retirement delays conflict\n");
646
647#ifdef sun4v
648	init_hdl = hdl;
649	cma_lhp = ldom_init(cma_init_alloc, cma_init_free);
650#endif
651}
652
653void
654_fmd_fini(fmd_hdl_t *hdl)
655{
656#ifdef sun4v
657	ldom_fini(cma_lhp);
658	cma_cpu_fini(hdl);
659#endif
660	cma_page_fini(hdl);
661}
662