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 https://opensource.org/licenses/CDDL-1.0. 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 <string.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 */ 62static hrtime_t 63fmd_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 71static fmd_serd_eng_t * 72fmd_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 if (sgp == NULL) { 78 perror("malloc"); 79 exit(EXIT_FAILURE); 80 } 81 memset(sgp, 0, sizeof (fmd_serd_eng_t)); 82 83 sgp->sg_name = strdup(name); 84 if (sgp->sg_name == NULL) { 85 perror("strdup"); 86 exit(EXIT_FAILURE); 87 } 88 89 sgp->sg_flags = FMD_SERD_DIRTY; 90 sgp->sg_n = n; 91 sgp->sg_t = t; 92 93 list_create(&sgp->sg_list, sizeof (fmd_serd_elem_t), 94 offsetof(fmd_serd_elem_t, se_list)); 95 96 return (sgp); 97} 98 99static void 100fmd_serd_eng_free(fmd_serd_eng_t *sgp) 101{ 102 fmd_serd_eng_reset(sgp); 103 free(sgp->sg_name); 104 list_destroy(&sgp->sg_list); 105 free(sgp); 106} 107 108/* 109 * sourced from fmd_string.c 110 */ 111static ulong_t 112fmd_strhash(const char *key) 113{ 114 ulong_t g, h = 0; 115 const char *p; 116 117 for (p = key; *p != '\0'; p++) { 118 h = (h << 4) + *p; 119 120 if ((g = (h & 0xf0000000)) != 0) { 121 h ^= (g >> 24); 122 h ^= g; 123 } 124 } 125 126 return (h); 127} 128 129void 130fmd_serd_hash_create(fmd_serd_hash_t *shp) 131{ 132 shp->sh_hashlen = FMD_STR_BUCKETS; 133 shp->sh_hash = calloc(shp->sh_hashlen, sizeof (void *)); 134 shp->sh_count = 0; 135 136 if (shp->sh_hash == NULL) { 137 perror("calloc"); 138 exit(EXIT_FAILURE); 139 } 140 141} 142 143void 144fmd_serd_hash_destroy(fmd_serd_hash_t *shp) 145{ 146 fmd_serd_eng_t *sgp, *ngp; 147 uint_t i; 148 149 for (i = 0; i < shp->sh_hashlen; i++) { 150 for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = ngp) { 151 ngp = sgp->sg_next; 152 fmd_serd_eng_free(sgp); 153 } 154 } 155 156 free(shp->sh_hash); 157 memset(shp, 0, sizeof (fmd_serd_hash_t)); 158} 159 160void 161fmd_serd_hash_apply(fmd_serd_hash_t *shp, fmd_serd_eng_f *func, void *arg) 162{ 163 fmd_serd_eng_t *sgp; 164 uint_t i; 165 166 for (i = 0; i < shp->sh_hashlen; i++) { 167 for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next) 168 func(sgp, arg); 169 } 170} 171 172fmd_serd_eng_t * 173fmd_serd_eng_insert(fmd_serd_hash_t *shp, const char *name, 174 uint_t n, hrtime_t t) 175{ 176 uint_t h = fmd_strhash(name) % shp->sh_hashlen; 177 fmd_serd_eng_t *sgp = fmd_serd_eng_alloc(name, n, t); 178 179 serd_log_msg(" SERD Engine: inserting %s N %d T %llu", 180 name, (int)n, (long long unsigned)t); 181 182 sgp->sg_next = shp->sh_hash[h]; 183 shp->sh_hash[h] = sgp; 184 shp->sh_count++; 185 186 return (sgp); 187} 188 189fmd_serd_eng_t * 190fmd_serd_eng_lookup(fmd_serd_hash_t *shp, const char *name) 191{ 192 uint_t h = fmd_strhash(name) % shp->sh_hashlen; 193 fmd_serd_eng_t *sgp; 194 195 for (sgp = shp->sh_hash[h]; sgp != NULL; sgp = sgp->sg_next) { 196 if (strcmp(name, sgp->sg_name) == 0) 197 return (sgp); 198 } 199 200 return (NULL); 201} 202 203void 204fmd_serd_eng_delete(fmd_serd_hash_t *shp, const char *name) 205{ 206 uint_t h = fmd_strhash(name) % shp->sh_hashlen; 207 fmd_serd_eng_t *sgp, **pp = &shp->sh_hash[h]; 208 209 serd_log_msg(" SERD Engine: deleting %s", name); 210 211 for (sgp = *pp; sgp != NULL; sgp = sgp->sg_next) { 212 if (strcmp(sgp->sg_name, name) != 0) 213 pp = &sgp->sg_next; 214 else 215 break; 216 } 217 218 if (sgp != NULL) { 219 *pp = sgp->sg_next; 220 fmd_serd_eng_free(sgp); 221 assert(shp->sh_count != 0); 222 shp->sh_count--; 223 } 224} 225 226static void 227fmd_serd_eng_discard(fmd_serd_eng_t *sgp, fmd_serd_elem_t *sep) 228{ 229 list_remove(&sgp->sg_list, sep); 230 sgp->sg_count--; 231 232 serd_log_msg(" SERD Engine: discarding %s, %d remaining", 233 sgp->sg_name, (int)sgp->sg_count); 234 235 free(sep); 236} 237 238int 239fmd_serd_eng_record(fmd_serd_eng_t *sgp, hrtime_t hrt) 240{ 241 fmd_serd_elem_t *sep, *oep; 242 243 /* 244 * If the fired flag is already set, return false and discard the 245 * event. This means that the caller will only see the engine "fire" 246 * once until fmd_serd_eng_reset() is called. The fmd_serd_eng_fired() 247 * function can also be used in combination with fmd_serd_eng_record(). 248 */ 249 if (sgp->sg_flags & FMD_SERD_FIRED) { 250 serd_log_msg(" SERD Engine: record %s already fired!", 251 sgp->sg_name); 252 return (B_FALSE); 253 } 254 255 while (sgp->sg_count >= sgp->sg_n) 256 fmd_serd_eng_discard(sgp, list_tail(&sgp->sg_list)); 257 258 sep = malloc(sizeof (fmd_serd_elem_t)); 259 if (sep == NULL) { 260 perror("malloc"); 261 exit(EXIT_FAILURE); 262 } 263 sep->se_hrt = hrt; 264 265 list_insert_head(&sgp->sg_list, sep); 266 sgp->sg_count++; 267 268 serd_log_msg(" SERD Engine: recording %s of %d (%llu)", 269 sgp->sg_name, (int)sgp->sg_count, (long long unsigned)hrt); 270 271 /* 272 * Pick up the oldest element pointer for comparison to 'sep'. We must 273 * do this after adding 'sep' because 'oep' and 'sep' can be the same. 274 */ 275 oep = list_tail(&sgp->sg_list); 276 277 if (sgp->sg_count >= sgp->sg_n && 278 fmd_event_delta(oep->se_hrt, sep->se_hrt) <= sgp->sg_t) { 279 sgp->sg_flags |= FMD_SERD_FIRED | FMD_SERD_DIRTY; 280 serd_log_msg(" SERD Engine: fired %s", sgp->sg_name); 281 return (B_TRUE); 282 } 283 284 sgp->sg_flags |= FMD_SERD_DIRTY; 285 return (B_FALSE); 286} 287 288int 289fmd_serd_eng_fired(fmd_serd_eng_t *sgp) 290{ 291 return (sgp->sg_flags & FMD_SERD_FIRED); 292} 293 294int 295fmd_serd_eng_empty(fmd_serd_eng_t *sgp) 296{ 297 return (sgp->sg_count == 0); 298} 299 300void 301fmd_serd_eng_reset(fmd_serd_eng_t *sgp) 302{ 303 serd_log_msg(" SERD Engine: resetting %s", sgp->sg_name); 304 305 while (sgp->sg_count != 0) 306 fmd_serd_eng_discard(sgp, list_head(&sgp->sg_list)); 307 308 sgp->sg_flags &= ~FMD_SERD_FIRED; 309 sgp->sg_flags |= FMD_SERD_DIRTY; 310} 311 312void 313fmd_serd_eng_gc(fmd_serd_eng_t *sgp, void *arg) 314{ 315 (void) arg; 316 fmd_serd_elem_t *sep, *nep; 317 hrtime_t hrt; 318 319 if (sgp->sg_count == 0 || (sgp->sg_flags & FMD_SERD_FIRED)) 320 return; /* no garbage collection needed if empty or fired */ 321 322 sep = list_head(&sgp->sg_list); 323 if (sep == NULL) 324 return; 325 326 hrt = sep->se_hrt - sgp->sg_t; 327 328 for (sep = list_head(&sgp->sg_list); sep != NULL; sep = nep) { 329 if (sep->se_hrt >= hrt) 330 break; /* sep and subsequent events are all within T */ 331 332 nep = list_next(&sgp->sg_list, sep); 333 fmd_serd_eng_discard(sgp, sep); 334 sgp->sg_flags |= FMD_SERD_DIRTY; 335 } 336} 337