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
36static fmd_serd_eng_t *
37fmd_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
49static void
50fmd_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
57void
58fmd_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
65void
66fmd_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
82void
83fmd_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
94uint_t
95fmd_serd_hash_count(fmd_serd_hash_t *shp)
96{
97	return (shp->sh_count);
98}
99
100int
101fmd_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
118fmd_serd_eng_t *
119fmd_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
132fmd_serd_eng_t *
133fmd_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
146void
147fmd_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
167static void
168fmd_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
177int
178fmd_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
191int
192fmd_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
233int
234fmd_serd_eng_fired(fmd_serd_eng_t *sgp)
235{
236	return (sgp->sg_flags & FMD_SERD_FIRED);
237}
238
239int
240fmd_serd_eng_empty(fmd_serd_eng_t *sgp)
241{
242	return (sgp->sg_count == 0);
243}
244
245void
246fmd_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
255void
256fmd_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
277void
278fmd_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
292void
293fmd_serd_eng_clrdirty(fmd_serd_eng_t *sgp)
294{
295	sgp->sg_flags &= ~FMD_SERD_DIRTY;
296}
297