flush: only detect lack of flush support in one place
[zfs.git] / cmd / zed / agents / fmd_serd.c
blobf942e62b3f48a254cddf206504526d7ee8c88e04
1 /*
2 * CDDL HEADER START
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.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or https://opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
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]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 * Copyright (c) 2016, Intel Corporation.
29 #include <assert.h>
30 #include <stddef.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/list.h>
34 #include <sys/time.h>
36 #include "fmd_api.h"
37 #include "fmd_serd.h"
38 #include "../zed_log.h"
41 #define FMD_STR_BUCKETS 211
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
53 * SERD Engine Backend
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).
62 static hrtime_t
63 fmd_event_delta(hrtime_t t1, hrtime_t t2)
65 uint64_t old = t1;
66 uint64_t new = t2;
68 return (new >= old ? new - old : (UINT64_MAX - old) + new + 1);
71 static fmd_serd_eng_t *
72 fmd_serd_eng_alloc(const char *name, uint64_t n, hrtime_t t)
74 fmd_serd_eng_t *sgp;
76 sgp = malloc(sizeof (fmd_serd_eng_t));
77 if (sgp == NULL) {
78 perror("malloc");
79 exit(EXIT_FAILURE);
81 memset(sgp, 0, sizeof (fmd_serd_eng_t));
83 sgp->sg_name = strdup(name);
84 if (sgp->sg_name == NULL) {
85 perror("strdup");
86 exit(EXIT_FAILURE);
89 sgp->sg_flags = FMD_SERD_DIRTY;
90 sgp->sg_n = n;
91 sgp->sg_t = t;
93 list_create(&sgp->sg_list, sizeof (fmd_serd_elem_t),
94 offsetof(fmd_serd_elem_t, se_list));
96 return (sgp);
99 static void
100 fmd_serd_eng_free(fmd_serd_eng_t *sgp)
102 fmd_serd_eng_reset(sgp);
103 free(sgp->sg_name);
104 list_destroy(&sgp->sg_list);
105 free(sgp);
109 * sourced from fmd_string.c
111 static ulong_t
112 fmd_strhash(const char *key)
114 ulong_t g, h = 0;
115 const char *p;
117 for (p = key; *p != '\0'; p++) {
118 h = (h << 4) + *p;
120 if ((g = (h & 0xf0000000)) != 0) {
121 h ^= (g >> 24);
122 h ^= g;
126 return (h);
129 void
130 fmd_serd_hash_create(fmd_serd_hash_t *shp)
132 shp->sh_hashlen = FMD_STR_BUCKETS;
133 shp->sh_hash = calloc(shp->sh_hashlen, sizeof (void *));
134 shp->sh_count = 0;
136 if (shp->sh_hash == NULL) {
137 perror("calloc");
138 exit(EXIT_FAILURE);
143 void
144 fmd_serd_hash_destroy(fmd_serd_hash_t *shp)
146 fmd_serd_eng_t *sgp, *ngp;
147 uint_t i;
149 for (i = 0; i < shp->sh_hashlen; i++) {
150 for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = ngp) {
151 ngp = sgp->sg_next;
152 fmd_serd_eng_free(sgp);
156 free(shp->sh_hash);
157 memset(shp, 0, sizeof (fmd_serd_hash_t));
160 void
161 fmd_serd_hash_apply(fmd_serd_hash_t *shp, fmd_serd_eng_f *func, void *arg)
163 fmd_serd_eng_t *sgp;
164 uint_t i;
166 for (i = 0; i < shp->sh_hashlen; i++) {
167 for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next)
168 func(sgp, arg);
172 fmd_serd_eng_t *
173 fmd_serd_eng_insert(fmd_serd_hash_t *shp, const char *name,
174 uint_t n, hrtime_t t)
176 uint_t h = fmd_strhash(name) % shp->sh_hashlen;
177 fmd_serd_eng_t *sgp = fmd_serd_eng_alloc(name, n, t);
179 serd_log_msg(" SERD Engine: inserting %s N %d T %llu",
180 name, (int)n, (long long unsigned)t);
182 sgp->sg_next = shp->sh_hash[h];
183 shp->sh_hash[h] = sgp;
184 shp->sh_count++;
186 return (sgp);
189 fmd_serd_eng_t *
190 fmd_serd_eng_lookup(fmd_serd_hash_t *shp, const char *name)
192 uint_t h = fmd_strhash(name) % shp->sh_hashlen;
193 fmd_serd_eng_t *sgp;
195 for (sgp = shp->sh_hash[h]; sgp != NULL; sgp = sgp->sg_next) {
196 if (strcmp(name, sgp->sg_name) == 0)
197 return (sgp);
200 return (NULL);
203 void
204 fmd_serd_eng_delete(fmd_serd_hash_t *shp, const char *name)
206 uint_t h = fmd_strhash(name) % shp->sh_hashlen;
207 fmd_serd_eng_t *sgp, **pp = &shp->sh_hash[h];
209 serd_log_msg(" SERD Engine: deleting %s", name);
211 for (sgp = *pp; sgp != NULL; sgp = sgp->sg_next) {
212 if (strcmp(sgp->sg_name, name) != 0)
213 pp = &sgp->sg_next;
214 else
215 break;
218 if (sgp != NULL) {
219 *pp = sgp->sg_next;
220 fmd_serd_eng_free(sgp);
221 assert(shp->sh_count != 0);
222 shp->sh_count--;
226 static void
227 fmd_serd_eng_discard(fmd_serd_eng_t *sgp, fmd_serd_elem_t *sep)
229 list_remove(&sgp->sg_list, sep);
230 sgp->sg_count--;
232 serd_log_msg(" SERD Engine: discarding %s, %d remaining",
233 sgp->sg_name, (int)sgp->sg_count);
235 free(sep);
239 fmd_serd_eng_record(fmd_serd_eng_t *sgp, hrtime_t hrt)
241 fmd_serd_elem_t *sep, *oep;
244 * If the fired flag is already set, return false and discard the
245 * event. This means that the caller will only see the engine "fire"
246 * once until fmd_serd_eng_reset() is called. The fmd_serd_eng_fired()
247 * function can also be used in combination with fmd_serd_eng_record().
249 if (sgp->sg_flags & FMD_SERD_FIRED) {
250 serd_log_msg(" SERD Engine: record %s already fired!",
251 sgp->sg_name);
252 return (B_FALSE);
255 while (sgp->sg_count >= sgp->sg_n)
256 fmd_serd_eng_discard(sgp, list_tail(&sgp->sg_list));
258 sep = malloc(sizeof (fmd_serd_elem_t));
259 if (sep == NULL) {
260 perror("malloc");
261 exit(EXIT_FAILURE);
263 sep->se_hrt = hrt;
265 list_insert_head(&sgp->sg_list, sep);
266 sgp->sg_count++;
268 serd_log_msg(" SERD Engine: recording %s of %d (%llu)",
269 sgp->sg_name, (int)sgp->sg_count, (long long unsigned)hrt);
272 * Pick up the oldest element pointer for comparison to 'sep'. We must
273 * do this after adding 'sep' because 'oep' and 'sep' can be the same.
275 oep = list_tail(&sgp->sg_list);
277 if (sgp->sg_count >= sgp->sg_n &&
278 fmd_event_delta(oep->se_hrt, sep->se_hrt) <= sgp->sg_t) {
279 sgp->sg_flags |= FMD_SERD_FIRED | FMD_SERD_DIRTY;
280 serd_log_msg(" SERD Engine: fired %s", sgp->sg_name);
281 return (B_TRUE);
284 sgp->sg_flags |= FMD_SERD_DIRTY;
285 return (B_FALSE);
289 fmd_serd_eng_fired(fmd_serd_eng_t *sgp)
291 return (sgp->sg_flags & FMD_SERD_FIRED);
295 fmd_serd_eng_empty(fmd_serd_eng_t *sgp)
297 return (sgp->sg_count == 0);
300 void
301 fmd_serd_eng_reset(fmd_serd_eng_t *sgp)
303 serd_log_msg(" SERD Engine: resetting %s", sgp->sg_name);
305 while (sgp->sg_count != 0)
306 fmd_serd_eng_discard(sgp, list_head(&sgp->sg_list));
308 sgp->sg_flags &= ~FMD_SERD_FIRED;
309 sgp->sg_flags |= FMD_SERD_DIRTY;
312 void
313 fmd_serd_eng_gc(fmd_serd_eng_t *sgp, void *arg)
315 (void) arg;
316 fmd_serd_elem_t *sep, *nep;
317 hrtime_t hrt;
319 if (sgp->sg_count == 0 || (sgp->sg_flags & FMD_SERD_FIRED))
320 return; /* no garbage collection needed if empty or fired */
322 sep = list_head(&sgp->sg_list);
323 if (sep == NULL)
324 return;
326 hrt = sep->se_hrt - sgp->sg_t;
328 for (sep = list_head(&sgp->sg_list); sep != NULL; sep = nep) {
329 if (sep->se_hrt >= hrt)
330 break; /* sep and subsequent events are all within T */
332 nep = list_next(&sgp->sg_list, sep);
333 fmd_serd_eng_discard(sgp, sep);
334 sgp->sg_flags |= FMD_SERD_DIRTY;