]>
Commit | Line | Data |
---|---|---|
cae5b340 AX |
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 | * Copyright (c) 2016, Intel Corporation. | |
27 | */ | |
28 | ||
29 | #include <assert.h> | |
30 | #include <stddef.h> | |
31 | #include <stdlib.h> | |
32 | #include <strings.h> | |
33 | #include <sys/list.h> | |
34 | #include <sys/time.h> | |
35 | ||
36 | #include "fmd_api.h" | |
37 | #include "fmd_serd.h" | |
38 | #include "../zed_log.h" | |
39 | ||
40 | ||
41 | #define FMD_STR_BUCKETS 211 | |
42 | ||
43 | ||
44 | #ifdef SERD_ENG_DEBUG | |
45 | #define serd_log_msg(fmt, ...) \ | |
46 | zed_log_msg(LOG_INFO, fmt, __VA_ARGS__) | |
47 | #else | |
48 | #define serd_log_msg(fmt, ...) | |
49 | #endif | |
50 | ||
51 | ||
52 | /* | |
53 | * SERD Engine Backend | |
54 | */ | |
55 | ||
56 | /* | |
57 | * Compute the delta between events in nanoseconds. To account for very old | |
58 | * events which are replayed, we must handle the case where time is negative. | |
59 | * We convert the hrtime_t's to unsigned 64-bit integers and then handle the | |
60 | * case where 'old' is greater than 'new' (i.e. high-res time has wrapped). | |
61 | */ | |
62 | static hrtime_t | |
63 | fmd_event_delta(hrtime_t t1, hrtime_t t2) | |
64 | { | |
65 | uint64_t old = t1; | |
66 | uint64_t new = t2; | |
67 | ||
68 | return (new >= old ? new - old : (UINT64_MAX - old) + new + 1); | |
69 | } | |
70 | ||
71 | static fmd_serd_eng_t * | |
72 | fmd_serd_eng_alloc(const char *name, uint64_t n, hrtime_t t) | |
73 | { | |
74 | fmd_serd_eng_t *sgp; | |
75 | ||
76 | sgp = malloc(sizeof (fmd_serd_eng_t)); | |
77 | bzero(sgp, sizeof (fmd_serd_eng_t)); | |
78 | ||
79 | sgp->sg_name = strdup(name); | |
80 | sgp->sg_flags = FMD_SERD_DIRTY; | |
81 | sgp->sg_n = n; | |
82 | sgp->sg_t = t; | |
83 | ||
84 | list_create(&sgp->sg_list, sizeof (fmd_serd_elem_t), | |
85 | offsetof(fmd_serd_elem_t, se_list)); | |
86 | ||
87 | return (sgp); | |
88 | } | |
89 | ||
90 | static void | |
91 | fmd_serd_eng_free(fmd_serd_eng_t *sgp) | |
92 | { | |
93 | fmd_serd_eng_reset(sgp); | |
94 | free(sgp->sg_name); | |
95 | list_destroy(&sgp->sg_list); | |
96 | free(sgp); | |
97 | } | |
98 | ||
99 | /* | |
100 | * sourced from fmd_string.c | |
101 | */ | |
102 | static ulong_t | |
103 | fmd_strhash(const char *key) | |
104 | { | |
105 | ulong_t g, h = 0; | |
106 | const char *p; | |
107 | ||
108 | for (p = key; *p != '\0'; p++) { | |
109 | h = (h << 4) + *p; | |
110 | ||
111 | if ((g = (h & 0xf0000000)) != 0) { | |
112 | h ^= (g >> 24); | |
113 | h ^= g; | |
114 | } | |
115 | } | |
116 | ||
117 | return (h); | |
118 | } | |
119 | ||
120 | void | |
121 | fmd_serd_hash_create(fmd_serd_hash_t *shp) | |
122 | { | |
123 | shp->sh_hashlen = FMD_STR_BUCKETS; | |
124 | shp->sh_hash = calloc(shp->sh_hashlen, sizeof (void *)); | |
125 | shp->sh_count = 0; | |
126 | } | |
127 | ||
128 | void | |
129 | fmd_serd_hash_destroy(fmd_serd_hash_t *shp) | |
130 | { | |
131 | fmd_serd_eng_t *sgp, *ngp; | |
132 | uint_t i; | |
133 | ||
134 | for (i = 0; i < shp->sh_hashlen; i++) { | |
135 | for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = ngp) { | |
136 | ngp = sgp->sg_next; | |
137 | fmd_serd_eng_free(sgp); | |
138 | } | |
139 | } | |
140 | ||
141 | free(shp->sh_hash); | |
142 | bzero(shp, sizeof (fmd_serd_hash_t)); | |
143 | } | |
144 | ||
145 | void | |
146 | fmd_serd_hash_apply(fmd_serd_hash_t *shp, fmd_serd_eng_f *func, void *arg) | |
147 | { | |
148 | fmd_serd_eng_t *sgp; | |
149 | uint_t i; | |
150 | ||
151 | for (i = 0; i < shp->sh_hashlen; i++) { | |
152 | for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next) | |
153 | func(sgp, arg); | |
154 | } | |
155 | } | |
156 | ||
157 | fmd_serd_eng_t * | |
158 | fmd_serd_eng_insert(fmd_serd_hash_t *shp, const char *name, | |
159 | uint_t n, hrtime_t t) | |
160 | { | |
161 | uint_t h = fmd_strhash(name) % shp->sh_hashlen; | |
162 | fmd_serd_eng_t *sgp = fmd_serd_eng_alloc(name, n, t); | |
163 | ||
164 | serd_log_msg(" SERD Engine: inserting %s N %d T %llu", | |
165 | name, (int)n, (long long unsigned)t); | |
166 | ||
167 | sgp->sg_next = shp->sh_hash[h]; | |
168 | shp->sh_hash[h] = sgp; | |
169 | shp->sh_count++; | |
170 | ||
171 | return (sgp); | |
172 | } | |
173 | ||
174 | fmd_serd_eng_t * | |
175 | fmd_serd_eng_lookup(fmd_serd_hash_t *shp, const char *name) | |
176 | { | |
177 | uint_t h = fmd_strhash(name) % shp->sh_hashlen; | |
178 | fmd_serd_eng_t *sgp; | |
179 | ||
180 | for (sgp = shp->sh_hash[h]; sgp != NULL; sgp = sgp->sg_next) { | |
181 | if (strcmp(name, sgp->sg_name) == 0) | |
182 | return (sgp); | |
183 | } | |
184 | ||
185 | return (NULL); | |
186 | } | |
187 | ||
188 | void | |
189 | fmd_serd_eng_delete(fmd_serd_hash_t *shp, const char *name) | |
190 | { | |
191 | uint_t h = fmd_strhash(name) % shp->sh_hashlen; | |
192 | fmd_serd_eng_t *sgp, **pp = &shp->sh_hash[h]; | |
193 | ||
194 | serd_log_msg(" SERD Engine: deleting %s", name); | |
195 | ||
196 | for (sgp = *pp; sgp != NULL; sgp = sgp->sg_next) { | |
197 | if (strcmp(sgp->sg_name, name) != 0) | |
198 | pp = &sgp->sg_next; | |
199 | else | |
200 | break; | |
201 | } | |
202 | ||
203 | if (sgp != NULL) { | |
204 | *pp = sgp->sg_next; | |
205 | fmd_serd_eng_free(sgp); | |
206 | assert(shp->sh_count != 0); | |
207 | shp->sh_count--; | |
208 | } | |
209 | } | |
210 | ||
211 | static void | |
212 | fmd_serd_eng_discard(fmd_serd_eng_t *sgp, fmd_serd_elem_t *sep) | |
213 | { | |
214 | list_remove(&sgp->sg_list, sep); | |
215 | sgp->sg_count--; | |
216 | ||
217 | serd_log_msg(" SERD Engine: discarding %s, %d remaining", | |
218 | sgp->sg_name, (int)sgp->sg_count); | |
219 | ||
220 | free(sep); | |
221 | } | |
222 | ||
223 | int | |
224 | fmd_serd_eng_record(fmd_serd_eng_t *sgp, hrtime_t hrt) | |
225 | { | |
226 | fmd_serd_elem_t *sep, *oep; | |
227 | ||
228 | /* | |
229 | * If the fired flag is already set, return false and discard the | |
230 | * event. This means that the caller will only see the engine "fire" | |
231 | * once until fmd_serd_eng_reset() is called. The fmd_serd_eng_fired() | |
232 | * function can also be used in combination with fmd_serd_eng_record(). | |
233 | */ | |
234 | if (sgp->sg_flags & FMD_SERD_FIRED) { | |
235 | serd_log_msg(" SERD Engine: record %s already fired!", | |
236 | sgp->sg_name); | |
237 | return (FMD_B_FALSE); | |
238 | } | |
239 | ||
240 | while (sgp->sg_count >= sgp->sg_n) | |
241 | fmd_serd_eng_discard(sgp, list_tail(&sgp->sg_list)); | |
242 | ||
243 | sep = malloc(sizeof (fmd_serd_elem_t)); | |
244 | sep->se_hrt = hrt; | |
245 | ||
246 | list_insert_head(&sgp->sg_list, sep); | |
247 | sgp->sg_count++; | |
248 | ||
249 | serd_log_msg(" SERD Engine: recording %s of %d (%llu)", | |
250 | sgp->sg_name, (int)sgp->sg_count, (long long unsigned)hrt); | |
251 | ||
252 | /* | |
253 | * Pick up the oldest element pointer for comparison to 'sep'. We must | |
254 | * do this after adding 'sep' because 'oep' and 'sep' can be the same. | |
255 | */ | |
256 | oep = list_tail(&sgp->sg_list); | |
257 | ||
258 | if (sgp->sg_count >= sgp->sg_n && | |
259 | fmd_event_delta(oep->se_hrt, sep->se_hrt) <= sgp->sg_t) { | |
260 | sgp->sg_flags |= FMD_SERD_FIRED | FMD_SERD_DIRTY; | |
261 | serd_log_msg(" SERD Engine: fired %s", sgp->sg_name); | |
262 | return (FMD_B_TRUE); | |
263 | } | |
264 | ||
265 | sgp->sg_flags |= FMD_SERD_DIRTY; | |
266 | return (FMD_B_FALSE); | |
267 | } | |
268 | ||
269 | int | |
270 | fmd_serd_eng_fired(fmd_serd_eng_t *sgp) | |
271 | { | |
272 | return (sgp->sg_flags & FMD_SERD_FIRED); | |
273 | } | |
274 | ||
275 | int | |
276 | fmd_serd_eng_empty(fmd_serd_eng_t *sgp) | |
277 | { | |
278 | return (sgp->sg_count == 0); | |
279 | } | |
280 | ||
281 | void | |
282 | fmd_serd_eng_reset(fmd_serd_eng_t *sgp) | |
283 | { | |
284 | serd_log_msg(" SERD Engine: reseting %s", sgp->sg_name); | |
285 | ||
286 | while (sgp->sg_count != 0) | |
287 | fmd_serd_eng_discard(sgp, list_head(&sgp->sg_list)); | |
288 | ||
289 | sgp->sg_flags &= ~FMD_SERD_FIRED; | |
290 | sgp->sg_flags |= FMD_SERD_DIRTY; | |
291 | } | |
292 | ||
293 | void | |
294 | fmd_serd_eng_gc(fmd_serd_eng_t *sgp) | |
295 | { | |
296 | fmd_serd_elem_t *sep, *nep; | |
297 | hrtime_t hrt; | |
298 | ||
299 | if (sgp->sg_count == 0 || (sgp->sg_flags & FMD_SERD_FIRED)) | |
300 | return; /* no garbage collection needed if empty or fired */ | |
301 | ||
302 | sep = list_head(&sgp->sg_list); | |
303 | if (sep == NULL) | |
304 | return; | |
305 | ||
306 | hrt = sep->se_hrt - sgp->sg_t; | |
307 | ||
308 | for (sep = list_head(&sgp->sg_list); sep != NULL; sep = nep) { | |
309 | if (sep->se_hrt >= hrt) | |
310 | break; /* sep and subsequent events are all within T */ | |
311 | ||
312 | nep = list_next(&sgp->sg_list, sep); | |
313 | fmd_serd_eng_discard(sgp, sep); | |
314 | sgp->sg_flags |= FMD_SERD_DIRTY; | |
315 | } | |
316 | } |