]> granicus.if.org Git - zfs/blob - cmd/zed/agents/fmd_serd.c
systemd encryption key support
[zfs] / cmd / zed / agents / fmd_serd.c
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 }