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