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