xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_self.c (revision f498645a3eecf2ddd304b4ea9c7f1b4c155ff79e)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/fm/protocol.h>
31 
32 #include <fmd_api.h>
33 #include <fmd_subr.h>
34 #include <fmd_string.h>
35 #include <fmd_protocol.h>
36 #include <fmd_module.h>
37 #include <fmd_error.h>
38 
39 static struct {
40 	fmd_stat_t nosub;
41 	fmd_stat_t module;
42 } self_stats = {
43 	{ "nosub", FMD_TYPE_UINT64, "event classes with no subscribers seen" },
44 	{ "module", FMD_TYPE_UINT64, "error events received from fmd modules" },
45 };
46 
47 typedef struct self_case {
48 	enum { SC_CLASS, SC_MODULE } sc_kind;
49 	char *sc_name;
50 } self_case_t;
51 
52 static self_case_t *
53 self_case_create(fmd_hdl_t *hdl, int kind, const char *name)
54 {
55 	self_case_t *scp = fmd_hdl_alloc(hdl, sizeof (self_case_t), FMD_SLEEP);
56 
57 	scp->sc_kind = kind;
58 	scp->sc_name = fmd_hdl_strdup(hdl, name, FMD_SLEEP);
59 
60 	return (scp);
61 }
62 
63 static void
64 self_case_destroy(fmd_hdl_t *hdl, self_case_t *scp)
65 {
66 	fmd_hdl_strfree(hdl, scp->sc_name);
67 	fmd_hdl_free(hdl, scp, sizeof (self_case_t));
68 }
69 
70 static fmd_case_t *
71 self_case_lookup(fmd_hdl_t *hdl, int kind, const char *name)
72 {
73 	fmd_case_t *cp = NULL;
74 
75 	while ((cp = fmd_case_next(hdl, cp)) != NULL) {
76 		self_case_t *scp = fmd_case_getspecific(hdl, cp);
77 		if (scp->sc_kind == kind && strcmp(scp->sc_name, name) == 0)
78 			break;
79 	}
80 
81 	return (cp);
82 }
83 
84 /*ARGSUSED*/
85 static void
86 self_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
87 {
88 	fmd_case_t *cp;
89 	nvlist_t *flt, *mod;
90 	char *name;
91 	int err = 0;
92 
93 	/*
94 	 * If we get an error report from another fmd module, then create a
95 	 * case for the module and add the ereport to it.  The error is either
96 	 * from fmd_hdl_error() or from fmd_api_error().  If it is the latter,
97 	 * fmd_module_error() will send another event of class EFMD_MOD_FAIL
98 	 * when the module has failed, at which point we can solve the case.
99 	 * We can also close the case on EFMD_MOD_CONF (bad config file).
100 	 */
101 	if (strcmp(class, fmd_errclass(EFMD_MODULE)) == 0 &&
102 	    nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &mod) == 0 &&
103 	    nvlist_lookup_string(mod, FM_FMRI_FMD_NAME, &name) == 0) {
104 
105 		if ((cp = self_case_lookup(hdl, SC_MODULE, name)) == NULL) {
106 			cp = fmd_case_open(hdl,
107 			    self_case_create(hdl, SC_MODULE, name));
108 		}
109 
110 		fmd_case_add_ereport(hdl, cp, ep);
111 		self_stats.module.fmds_value.ui64++;
112 		(void) nvlist_lookup_int32(nvl, FMD_ERR_MOD_ERRNO, &err);
113 
114 		if (err != EFMD_MOD_FAIL && err != EFMD_MOD_CONF)
115 			return; /* module is still active, so keep case open */
116 
117 		if (fmd_case_solved(hdl, cp))
118 			return; /* case is already closed but error in _fini */
119 
120 		class = err == EFMD_MOD_FAIL ? FMD_FLT_MOD : FMD_FLT_CONF;
121 		flt = fmd_protocol_fault(class, 100, mod, NULL, NULL);
122 
123 		fmd_case_add_suspect(hdl, cp, flt);
124 		fmd_case_solve(hdl, cp);
125 
126 		return;
127 	}
128 
129 	/*
130 	 * If we get an I/O DDI ereport, drop it for now until the I/O DE is
131 	 * implemented and integrated.  Existing drivers in O/N have bugs that
132 	 * will trigger these and we don't want this producing FMD_FLT_NOSUB.
133 	 */
134 	if (strncmp(class, "ereport.io.ddi.", strlen("ereport.io.ddi.")) == 0)
135 		return; /* if we got a DDI ereport, drop it for now */
136 
137 	/*
138 	 * If we get any other type of event then it is of a class for which
139 	 * there are no subscribers.  Some of these correspond to internal fmd
140 	 * errors, which we ignore.  Otherwise we keep one case per class and
141 	 * use it to produce a message indicating that something is awry.
142 	 */
143 	if (strcmp(class, FM_LIST_SUSPECT_CLASS) == 0 ||
144 	    strcmp(class, FM_LIST_ISOLATED_CLASS) == 0 ||
145 	    strcmp(class, FM_LIST_REPAIRED_CLASS) == 0)
146 		return; /* if no agents are present just drop list.suspect */
147 
148 	if (strncmp(class, FMD_ERR_CLASS, FMD_ERR_CLASS_LEN) == 0)
149 		return; /* if fmd itself produced the error just drop it */
150 
151 	if (strncmp(class, FMD_RSRC_CLASS, FMD_RSRC_CLASS_LEN) == 0)
152 		return; /* if fmd itself produced the event just drop it */
153 
154 	if (self_case_lookup(hdl, SC_CLASS, class) != NULL)
155 		return; /* case is already open against this class */
156 
157 	cp = fmd_case_open(hdl, self_case_create(hdl, SC_CLASS, class));
158 	fmd_case_add_ereport(hdl, cp, ep);
159 	self_stats.nosub.fmds_value.ui64++;
160 
161 	flt = fmd_protocol_fault(FMD_FLT_NOSUB, 100, NULL, NULL, NULL);
162 	fmd_case_add_suspect(hdl, cp, flt);
163 	fmd_case_solve(hdl, cp);
164 }
165 
166 static void
167 self_close(fmd_hdl_t *hdl, fmd_case_t *cp)
168 {
169 	self_case_destroy(hdl, fmd_case_getspecific(hdl, cp));
170 }
171 
172 static const fmd_hdl_ops_t self_ops = {
173 	self_recv,	/* fmdo_recv */
174 	NULL,		/* fmdo_timeout */
175 	self_close,	/* fmdo_close */
176 	NULL,		/* fmdo_stats */
177 	NULL,		/* fmdo_gc */
178 };
179 
180 void
181 self_init(fmd_hdl_t *hdl)
182 {
183 	fmd_module_t *mp = (fmd_module_t *)hdl; /* see below */
184 
185 	fmd_hdl_info_t info = {
186 	    "Fault Manager Self-Diagnosis", "1.0", &self_ops, NULL
187 	};
188 
189 	/*
190 	 * Unlike other modules, fmd-self-diagnosis has some special needs that
191 	 * fall outside of what we want in the module API.  Manually disable
192 	 * checkpointing for this module by tweaking the mod_stats values.
193 	 * The self-diagnosis world relates to fmd's running state and modules
194 	 * which all change when it restarts, so don't bother w/ checkpointing.
195 	 */
196 	(void) pthread_mutex_lock(&mp->mod_stats_lock);
197 	mp->mod_stats->ms_ckpt_save.fmds_value.bool = FMD_B_FALSE;
198 	mp->mod_stats->ms_ckpt_restore.fmds_value.bool = FMD_B_FALSE;
199 	(void) pthread_mutex_unlock(&mp->mod_stats_lock);
200 
201 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &info) != 0)
202 		return; /* failed to register with fmd */
203 
204 	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (self_stats) /
205 	    sizeof (fmd_stat_t), (fmd_stat_t *)&self_stats);
206 }
207 
208 void
209 self_fini(fmd_hdl_t *hdl)
210 {
211 	fmd_case_t *cp = NULL;
212 
213 	while ((cp = fmd_case_next(hdl, cp)) != NULL)
214 		self_case_destroy(hdl, fmd_case_getspecific(hdl, cp));
215 }
216