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: resetting %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 } 317