Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/cmd/zed/agents/fmd_serd.c
48529 views
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
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 *
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
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
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
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
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
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 *
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 *
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
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
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
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
290
fmd_serd_eng_fired(fmd_serd_eng_t *sgp)
291
{
292
return (sgp->sg_flags & FMD_SERD_FIRED);
293
}
294
295
int
296
fmd_serd_eng_empty(fmd_serd_eng_t *sgp)
297
{
298
return (sgp->sg_count == 0);
299
}
300
301
void
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
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
339