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