xref: /freebsd/sys/contrib/openzfs/cmd/zed/agents/fmd_serd.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License, Version 1.0 only
7  * (the "License").  You may not use this file except in compliance
8  * with the License.
9  *
10  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11  * or https://opensource.org/licenses/CDDL-1.0.
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 /*
24  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  *
27  * Copyright (c) 2016, Intel Corporation.
28  */
29 
30 #include <assert.h>
31 #include <stddef.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/list.h>
35 #include <sys/time.h>
36 
37 #include "fmd_api.h"
38 #include "fmd_serd.h"
39 #include "../zed_log.h"
40 
41 
42 #define	FMD_STR_BUCKETS		211
43 
44 
45 #ifdef SERD_ENG_DEBUG
46 #define	serd_log_msg(fmt, ...) \
47 	zed_log_msg(LOG_INFO, fmt, __VA_ARGS__)
48 #else
49 #define	serd_log_msg(fmt, ...)
50 #endif
51 
52 
53 /*
54  * SERD Engine Backend
55  */
56 
57 /*
58  * Compute the delta between events in nanoseconds.  To account for very old
59  * events which are replayed, we must handle the case where time is negative.
60  * We convert the hrtime_t's to unsigned 64-bit integers and then handle the
61  * case where 'old' is greater than 'new' (i.e. high-res time has wrapped).
62  */
63 static hrtime_t
fmd_event_delta(hrtime_t t1,hrtime_t t2)64 fmd_event_delta(hrtime_t t1, hrtime_t t2)
65 {
66 	uint64_t old = t1;
67 	uint64_t new = t2;
68 
69 	return (new >= old ? new - old : (UINT64_MAX - old) + new + 1);
70 }
71 
72 static fmd_serd_eng_t *
fmd_serd_eng_alloc(const char * name,uint64_t n,hrtime_t t)73 fmd_serd_eng_alloc(const char *name, uint64_t n, hrtime_t t)
74 {
75 	fmd_serd_eng_t *sgp;
76 
77 	sgp = malloc(sizeof (fmd_serd_eng_t));
78 	if (sgp == NULL) {
79 		perror("malloc");
80 		exit(EXIT_FAILURE);
81 	}
82 	memset(sgp, 0, sizeof (fmd_serd_eng_t));
83 
84 	sgp->sg_name = strdup(name);
85 	if (sgp->sg_name == NULL) {
86 		perror("strdup");
87 		exit(EXIT_FAILURE);
88 	}
89 
90 	sgp->sg_flags = FMD_SERD_DIRTY;
91 	sgp->sg_n = n;
92 	sgp->sg_t = t;
93 
94 	list_create(&sgp->sg_list, sizeof (fmd_serd_elem_t),
95 	    offsetof(fmd_serd_elem_t, se_list));
96 
97 	return (sgp);
98 }
99 
100 static void
fmd_serd_eng_free(fmd_serd_eng_t * sgp)101 fmd_serd_eng_free(fmd_serd_eng_t *sgp)
102 {
103 	fmd_serd_eng_reset(sgp);
104 	free(sgp->sg_name);
105 	list_destroy(&sgp->sg_list);
106 	free(sgp);
107 }
108 
109 /*
110  * sourced from fmd_string.c
111  */
112 static ulong_t
fmd_strhash(const char * key)113 fmd_strhash(const char *key)
114 {
115 	ulong_t g, h = 0;
116 	const char *p;
117 
118 	for (p = key; *p != '\0'; p++) {
119 		h = (h << 4) + *p;
120 
121 		if ((g = (h & 0xf0000000)) != 0) {
122 			h ^= (g >> 24);
123 			h ^= g;
124 		}
125 	}
126 
127 	return (h);
128 }
129 
130 void
fmd_serd_hash_create(fmd_serd_hash_t * shp)131 fmd_serd_hash_create(fmd_serd_hash_t *shp)
132 {
133 	shp->sh_hashlen = FMD_STR_BUCKETS;
134 	shp->sh_hash = calloc(shp->sh_hashlen, sizeof (void *));
135 	shp->sh_count = 0;
136 
137 	if (shp->sh_hash == NULL) {
138 		perror("calloc");
139 		exit(EXIT_FAILURE);
140 	}
141 
142 }
143 
144 void
fmd_serd_hash_destroy(fmd_serd_hash_t * shp)145 fmd_serd_hash_destroy(fmd_serd_hash_t *shp)
146 {
147 	fmd_serd_eng_t *sgp, *ngp;
148 	uint_t i;
149 
150 	for (i = 0; i < shp->sh_hashlen; i++) {
151 		for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = ngp) {
152 			ngp = sgp->sg_next;
153 			fmd_serd_eng_free(sgp);
154 		}
155 	}
156 
157 	free(shp->sh_hash);
158 	memset(shp, 0, sizeof (fmd_serd_hash_t));
159 }
160 
161 void
fmd_serd_hash_apply(fmd_serd_hash_t * shp,fmd_serd_eng_f * func,void * arg)162 fmd_serd_hash_apply(fmd_serd_hash_t *shp, fmd_serd_eng_f *func, void *arg)
163 {
164 	fmd_serd_eng_t *sgp;
165 	uint_t i;
166 
167 	for (i = 0; i < shp->sh_hashlen; i++) {
168 		for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next)
169 			func(sgp, arg);
170 	}
171 }
172 
173 fmd_serd_eng_t *
fmd_serd_eng_insert(fmd_serd_hash_t * shp,const char * name,uint_t n,hrtime_t t)174 fmd_serd_eng_insert(fmd_serd_hash_t *shp, const char *name,
175     uint_t n, hrtime_t t)
176 {
177 	uint_t h = fmd_strhash(name) % shp->sh_hashlen;
178 	fmd_serd_eng_t *sgp = fmd_serd_eng_alloc(name, n, t);
179 
180 	serd_log_msg("  SERD Engine: inserting  %s N %d T %llu",
181 	    name, (int)n, (long long unsigned)t);
182 
183 	sgp->sg_next = shp->sh_hash[h];
184 	shp->sh_hash[h] = sgp;
185 	shp->sh_count++;
186 
187 	return (sgp);
188 }
189 
190 fmd_serd_eng_t *
fmd_serd_eng_lookup(fmd_serd_hash_t * shp,const char * name)191 fmd_serd_eng_lookup(fmd_serd_hash_t *shp, const char *name)
192 {
193 	uint_t h = fmd_strhash(name) % shp->sh_hashlen;
194 	fmd_serd_eng_t *sgp;
195 
196 	for (sgp = shp->sh_hash[h]; sgp != NULL; sgp = sgp->sg_next) {
197 		if (strcmp(name, sgp->sg_name) == 0)
198 			return (sgp);
199 	}
200 
201 	return (NULL);
202 }
203 
204 void
fmd_serd_eng_delete(fmd_serd_hash_t * shp,const char * name)205 fmd_serd_eng_delete(fmd_serd_hash_t *shp, const char *name)
206 {
207 	uint_t h = fmd_strhash(name) % shp->sh_hashlen;
208 	fmd_serd_eng_t *sgp, **pp = &shp->sh_hash[h];
209 
210 	serd_log_msg("  SERD Engine: deleting %s", name);
211 
212 	for (sgp = *pp; sgp != NULL; sgp = sgp->sg_next) {
213 		if (strcmp(sgp->sg_name, name) != 0)
214 			pp = &sgp->sg_next;
215 		else
216 			break;
217 	}
218 
219 	if (sgp != NULL) {
220 		*pp = sgp->sg_next;
221 		fmd_serd_eng_free(sgp);
222 		assert(shp->sh_count != 0);
223 		shp->sh_count--;
224 	}
225 }
226 
227 static void
fmd_serd_eng_discard(fmd_serd_eng_t * sgp,fmd_serd_elem_t * sep)228 fmd_serd_eng_discard(fmd_serd_eng_t *sgp, fmd_serd_elem_t *sep)
229 {
230 	list_remove(&sgp->sg_list, sep);
231 	sgp->sg_count--;
232 
233 	serd_log_msg("  SERD Engine: discarding %s, %d remaining",
234 	    sgp->sg_name, (int)sgp->sg_count);
235 
236 	free(sep);
237 }
238 
239 int
fmd_serd_eng_record(fmd_serd_eng_t * sgp,hrtime_t hrt)240 fmd_serd_eng_record(fmd_serd_eng_t *sgp, hrtime_t hrt)
241 {
242 	fmd_serd_elem_t *sep, *oep;
243 
244 	/*
245 	 * If the fired flag is already set, return false and discard the
246 	 * event.  This means that the caller will only see the engine "fire"
247 	 * once until fmd_serd_eng_reset() is called.  The fmd_serd_eng_fired()
248 	 * function can also be used in combination with fmd_serd_eng_record().
249 	 */
250 	if (sgp->sg_flags & FMD_SERD_FIRED) {
251 		serd_log_msg("  SERD Engine: record %s already fired!",
252 		    sgp->sg_name);
253 		return (B_FALSE);
254 	}
255 
256 	while (sgp->sg_count >= sgp->sg_n)
257 		fmd_serd_eng_discard(sgp, list_tail(&sgp->sg_list));
258 
259 	sep = malloc(sizeof (fmd_serd_elem_t));
260 	if (sep == NULL) {
261 		perror("malloc");
262 		exit(EXIT_FAILURE);
263 	}
264 	sep->se_hrt = hrt;
265 
266 	list_insert_head(&sgp->sg_list, sep);
267 	sgp->sg_count++;
268 
269 	serd_log_msg("  SERD Engine: recording %s of %d (%llu)",
270 	    sgp->sg_name, (int)sgp->sg_count, (long long unsigned)hrt);
271 
272 	/*
273 	 * Pick up the oldest element pointer for comparison to 'sep'.  We must
274 	 * do this after adding 'sep' because 'oep' and 'sep' can be the same.
275 	 */
276 	oep = list_tail(&sgp->sg_list);
277 
278 	if (sgp->sg_count >= sgp->sg_n &&
279 	    fmd_event_delta(oep->se_hrt, sep->se_hrt) <= sgp->sg_t) {
280 		sgp->sg_flags |= FMD_SERD_FIRED | FMD_SERD_DIRTY;
281 		serd_log_msg("  SERD Engine: fired %s", sgp->sg_name);
282 		return (B_TRUE);
283 	}
284 
285 	sgp->sg_flags |= FMD_SERD_DIRTY;
286 	return (B_FALSE);
287 }
288 
289 int
fmd_serd_eng_fired(fmd_serd_eng_t * sgp)290 fmd_serd_eng_fired(fmd_serd_eng_t *sgp)
291 {
292 	return (sgp->sg_flags & FMD_SERD_FIRED);
293 }
294 
295 int
fmd_serd_eng_empty(fmd_serd_eng_t * sgp)296 fmd_serd_eng_empty(fmd_serd_eng_t *sgp)
297 {
298 	return (sgp->sg_count == 0);
299 }
300 
301 void
fmd_serd_eng_reset(fmd_serd_eng_t * sgp)302 fmd_serd_eng_reset(fmd_serd_eng_t *sgp)
303 {
304 	serd_log_msg("  SERD Engine: resetting %s", sgp->sg_name);
305 
306 	while (sgp->sg_count != 0)
307 		fmd_serd_eng_discard(sgp, list_head(&sgp->sg_list));
308 
309 	sgp->sg_flags &= ~FMD_SERD_FIRED;
310 	sgp->sg_flags |= FMD_SERD_DIRTY;
311 }
312 
313 void
fmd_serd_eng_gc(fmd_serd_eng_t * sgp,void * arg)314 fmd_serd_eng_gc(fmd_serd_eng_t *sgp, void *arg)
315 {
316 	(void) arg;
317 	fmd_serd_elem_t *sep, *nep;
318 	hrtime_t hrt;
319 
320 	if (sgp->sg_count == 0 || (sgp->sg_flags & FMD_SERD_FIRED))
321 		return; /* no garbage collection needed if empty or fired */
322 
323 	sep = list_head(&sgp->sg_list);
324 	if (sep == NULL)
325 		return;
326 
327 	hrt = sep->se_hrt - sgp->sg_t;
328 
329 	for (sep = list_head(&sgp->sg_list); sep != NULL; sep = nep) {
330 		if (sep->se_hrt >= hrt)
331 			break; /* sep and subsequent events are all within T */
332 
333 		nep = list_next(&sgp->sg_list, sep);
334 		fmd_serd_eng_discard(sgp, sep);
335 		sgp->sg_flags |= FMD_SERD_DIRTY;
336 	}
337 }
338