xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_serd.c (revision bb9b6b3f59b8820022416cea99b49c50fef6e391)
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  * Copyright 2004 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 <fmd_alloc.h>
30 #include <fmd_string.h>
31 #include <fmd_subr.h>
32 #include <fmd_api.h>
33 #include <fmd_serd.h>
34 #include <fmd.h>
35 
36 static fmd_serd_eng_t *
37 fmd_serd_eng_alloc(const char *name, uint64_t n, hrtime_t t)
38 {
39 	fmd_serd_eng_t *sgp = fmd_zalloc(sizeof (fmd_serd_eng_t), FMD_SLEEP);
40 
41 	sgp->sg_name = fmd_strdup(name, FMD_SLEEP);
42 	sgp->sg_flags = FMD_SERD_DIRTY;
43 	sgp->sg_n = n;
44 	sgp->sg_t = t;
45 
46 	return (sgp);
47 }
48 
49 static void
50 fmd_serd_eng_free(fmd_serd_eng_t *sgp)
51 {
52 	fmd_serd_eng_reset(sgp);
53 	fmd_strfree(sgp->sg_name);
54 	fmd_free(sgp, sizeof (fmd_serd_eng_t));
55 }
56 
57 void
58 fmd_serd_hash_create(fmd_serd_hash_t *shp)
59 {
60 	shp->sh_hashlen = fmd.d_str_buckets;
61 	shp->sh_hash = fmd_zalloc(sizeof (void *) * shp->sh_hashlen, FMD_SLEEP);
62 	shp->sh_count = 0;
63 }
64 
65 void
66 fmd_serd_hash_destroy(fmd_serd_hash_t *shp)
67 {
68 	fmd_serd_eng_t *sgp, *ngp;
69 	uint_t i;
70 
71 	for (i = 0; i < shp->sh_hashlen; i++) {
72 		for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = ngp) {
73 			ngp = sgp->sg_next;
74 			fmd_serd_eng_free(sgp);
75 		}
76 	}
77 
78 	fmd_free(shp->sh_hash, sizeof (void *) * shp->sh_hashlen);
79 	bzero(shp, sizeof (fmd_serd_hash_t));
80 }
81 
82 void
83 fmd_serd_hash_apply(fmd_serd_hash_t *shp, fmd_serd_eng_f *func, void *arg)
84 {
85 	fmd_serd_eng_t *sgp;
86 	uint_t i;
87 
88 	for (i = 0; i < shp->sh_hashlen; i++) {
89 		for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next)
90 			func(sgp, arg);
91 	}
92 }
93 
94 uint_t
95 fmd_serd_hash_count(fmd_serd_hash_t *shp)
96 {
97 	return (shp->sh_count);
98 }
99 
100 int
101 fmd_serd_hash_contains(fmd_serd_hash_t *shp, fmd_event_t *ep)
102 {
103 	fmd_serd_eng_t *sgp;
104 	uint_t i;
105 
106 	for (i = 0; i < shp->sh_hashlen; i++) {
107 		for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next) {
108 			if (fmd_serd_eng_contains(sgp, ep)) {
109 				fmd_event_transition(ep, FMD_EVS_ACCEPTED);
110 				return (1);
111 			}
112 		}
113 	}
114 
115 	return (0);
116 }
117 
118 fmd_serd_eng_t *
119 fmd_serd_eng_insert(fmd_serd_hash_t *shp,
120     const char *name, uint_t n, hrtime_t t)
121 {
122 	uint_t h = fmd_strhash(name) % shp->sh_hashlen;
123 	fmd_serd_eng_t *sgp = fmd_serd_eng_alloc(name, n, t);
124 
125 	sgp->sg_next = shp->sh_hash[h];
126 	shp->sh_hash[h] = sgp;
127 	shp->sh_count++;
128 
129 	return (sgp);
130 }
131 
132 fmd_serd_eng_t *
133 fmd_serd_eng_lookup(fmd_serd_hash_t *shp, const char *name)
134 {
135 	uint_t h = fmd_strhash(name) % shp->sh_hashlen;
136 	fmd_serd_eng_t *sgp;
137 
138 	for (sgp = shp->sh_hash[h]; sgp != NULL; sgp = sgp->sg_next) {
139 		if (strcmp(name, sgp->sg_name) == 0)
140 			return (sgp);
141 	}
142 
143 	return (NULL);
144 }
145 
146 void
147 fmd_serd_eng_delete(fmd_serd_hash_t *shp, const char *name)
148 {
149 	uint_t h = fmd_strhash(name) % shp->sh_hashlen;
150 	fmd_serd_eng_t *sgp, **pp = &shp->sh_hash[h];
151 
152 	for (sgp = *pp; sgp != NULL; sgp = sgp->sg_next) {
153 		if (strcmp(sgp->sg_name, name) != 0)
154 			pp = &sgp->sg_next;
155 		else
156 			break;
157 	}
158 
159 	if (sgp != NULL) {
160 		*pp = sgp->sg_next;
161 		fmd_serd_eng_free(sgp);
162 		ASSERT(shp->sh_count != 0);
163 		shp->sh_count--;
164 	}
165 }
166 
167 static void
168 fmd_serd_eng_discard(fmd_serd_eng_t *sgp, fmd_serd_elem_t *sep)
169 {
170 	fmd_list_delete(&sgp->sg_list, sep);
171 	sgp->sg_count--;
172 
173 	fmd_event_rele(sep->se_event);
174 	fmd_free(sep, sizeof (fmd_serd_elem_t));
175 }
176 
177 int
178 fmd_serd_eng_contains(fmd_serd_eng_t *sgp, fmd_event_t *ep)
179 {
180 	fmd_serd_elem_t *sep;
181 
182 	for (sep = fmd_list_next(&sgp->sg_list);
183 	    sep != NULL; sep = fmd_list_next(sep)) {
184 		if (fmd_event_equal(sep->se_event, ep))
185 			return (1);
186 	}
187 
188 	return (0);
189 }
190 
191 int
192 fmd_serd_eng_record(fmd_serd_eng_t *sgp, fmd_event_t *ep)
193 {
194 	fmd_serd_elem_t *sep, *oep;
195 
196 	/*
197 	 * If the fired flag is already set, return false and discard the
198 	 * event.  This means that the caller will only see the engine "fire"
199 	 * once until fmd_serd_eng_reset() is called.  The fmd_serd_eng_fired()
200 	 * function can also be used in combination with fmd_serd_eng_record().
201 	 */
202 	if (sgp->sg_flags & FMD_SERD_FIRED)
203 		return (FMD_B_FALSE);
204 
205 	while (sgp->sg_count > sgp->sg_n)
206 		fmd_serd_eng_discard(sgp, fmd_list_next(&sgp->sg_list));
207 
208 	fmd_event_hold(ep);
209 	fmd_event_transition(ep, FMD_EVS_ACCEPTED);
210 
211 	sep = fmd_alloc(sizeof (fmd_serd_elem_t), FMD_SLEEP);
212 	sep->se_event = ep;
213 
214 	fmd_list_append(&sgp->sg_list, sep);
215 	sgp->sg_count++;
216 
217 	/*
218 	 * Pick up the oldest element pointer for comparison to 'sep'.  We must
219 	 * do this after adding 'sep' because 'oep' and 'sep' can be the same.
220 	 */
221 	oep = fmd_list_next(&sgp->sg_list);
222 
223 	if (sgp->sg_count > sgp->sg_n &&
224 	    fmd_event_delta(oep->se_event, sep->se_event) <= sgp->sg_t) {
225 		sgp->sg_flags |= FMD_SERD_FIRED | FMD_SERD_DIRTY;
226 		return (FMD_B_TRUE);
227 	}
228 
229 	sgp->sg_flags |= FMD_SERD_DIRTY;
230 	return (FMD_B_FALSE);
231 }
232 
233 int
234 fmd_serd_eng_fired(fmd_serd_eng_t *sgp)
235 {
236 	return (sgp->sg_flags & FMD_SERD_FIRED);
237 }
238 
239 int
240 fmd_serd_eng_empty(fmd_serd_eng_t *sgp)
241 {
242 	return (sgp->sg_count == 0);
243 }
244 
245 void
246 fmd_serd_eng_reset(fmd_serd_eng_t *sgp)
247 {
248 	while (sgp->sg_count != 0)
249 		fmd_serd_eng_discard(sgp, fmd_list_next(&sgp->sg_list));
250 
251 	sgp->sg_flags &= ~FMD_SERD_FIRED;
252 	sgp->sg_flags |= FMD_SERD_DIRTY;
253 }
254 
255 void
256 fmd_serd_eng_gc(fmd_serd_eng_t *sgp)
257 {
258 	fmd_serd_elem_t *sep, *nep;
259 	hrtime_t hrt;
260 
261 	if (sgp->sg_count == 0 || (sgp->sg_flags & FMD_SERD_FIRED))
262 		return; /* no garbage collection needed if empty or fired */
263 
264 	sep = fmd_list_prev(&sgp->sg_list);
265 	hrt = fmd_event_hrtime(sep->se_event) - sgp->sg_t;
266 
267 	for (sep = fmd_list_next(&sgp->sg_list); sep != NULL; sep = nep) {
268 		if (fmd_event_hrtime(sep->se_event) >= hrt)
269 			break; /* sep and subsequent events are all within T */
270 
271 		nep = fmd_list_next(sep);
272 		fmd_serd_eng_discard(sgp, sep);
273 		sgp->sg_flags |= FMD_SERD_DIRTY;
274 	}
275 }
276 
277 void
278 fmd_serd_eng_commit(fmd_serd_eng_t *sgp)
279 {
280 	fmd_serd_elem_t *sep;
281 
282 	if (!(sgp->sg_flags & FMD_SERD_DIRTY))
283 		return; /* engine has not changed since last commit */
284 
285 	for (sep = fmd_list_next(&sgp->sg_list); sep != NULL;
286 	    sep = fmd_list_next(sep))
287 		fmd_event_commit(sep->se_event);
288 
289 	sgp->sg_flags &= ~FMD_SERD_DIRTY;
290 }
291 
292 void
293 fmd_serd_eng_clrdirty(fmd_serd_eng_t *sgp)
294 {
295 	sgp->sg_flags &= ~FMD_SERD_DIRTY;
296 }
297