Path: blob/main/sys/contrib/openzfs/cmd/zed/agents/fmd_serd.c
48529 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License, Version 1.0 only6* (the "License"). You may not use this file except in compliance7* with the License.8*9* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE10* or https://opensource.org/licenses/CDDL-1.0.11* See the License for the specific language governing permissions12* and limitations under the License.13*14* When distributing Covered Code, include this CDDL HEADER in each15* file and include the License file at usr/src/OPENSOLARIS.LICENSE.16* If applicable, add the following below this CDDL HEADER, with the17* fields enclosed by brackets "[]" replaced with your own identifying18* information: Portions Copyright [yyyy] [name of copyright owner]19*20* CDDL HEADER END21*/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*/2829#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>3536#include "fmd_api.h"37#include "fmd_serd.h"38#include "../zed_log.h"394041#define FMD_STR_BUCKETS 211424344#ifdef SERD_ENG_DEBUG45#define serd_log_msg(fmt, ...) \46zed_log_msg(LOG_INFO, fmt, __VA_ARGS__)47#else48#define serd_log_msg(fmt, ...)49#endif505152/*53* SERD Engine Backend54*/5556/*57* Compute the delta between events in nanoseconds. To account for very old58* 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 the60* case where 'old' is greater than 'new' (i.e. high-res time has wrapped).61*/62static hrtime_t63fmd_event_delta(hrtime_t t1, hrtime_t t2)64{65uint64_t old = t1;66uint64_t new = t2;6768return (new >= old ? new - old : (UINT64_MAX - old) + new + 1);69}7071static fmd_serd_eng_t *72fmd_serd_eng_alloc(const char *name, uint64_t n, hrtime_t t)73{74fmd_serd_eng_t *sgp;7576sgp = malloc(sizeof (fmd_serd_eng_t));77if (sgp == NULL) {78perror("malloc");79exit(EXIT_FAILURE);80}81memset(sgp, 0, sizeof (fmd_serd_eng_t));8283sgp->sg_name = strdup(name);84if (sgp->sg_name == NULL) {85perror("strdup");86exit(EXIT_FAILURE);87}8889sgp->sg_flags = FMD_SERD_DIRTY;90sgp->sg_n = n;91sgp->sg_t = t;9293list_create(&sgp->sg_list, sizeof (fmd_serd_elem_t),94offsetof(fmd_serd_elem_t, se_list));9596return (sgp);97}9899static void100fmd_serd_eng_free(fmd_serd_eng_t *sgp)101{102fmd_serd_eng_reset(sgp);103free(sgp->sg_name);104list_destroy(&sgp->sg_list);105free(sgp);106}107108/*109* sourced from fmd_string.c110*/111static ulong_t112fmd_strhash(const char *key)113{114ulong_t g, h = 0;115const char *p;116117for (p = key; *p != '\0'; p++) {118h = (h << 4) + *p;119120if ((g = (h & 0xf0000000)) != 0) {121h ^= (g >> 24);122h ^= g;123}124}125126return (h);127}128129void130fmd_serd_hash_create(fmd_serd_hash_t *shp)131{132shp->sh_hashlen = FMD_STR_BUCKETS;133shp->sh_hash = calloc(shp->sh_hashlen, sizeof (void *));134shp->sh_count = 0;135136if (shp->sh_hash == NULL) {137perror("calloc");138exit(EXIT_FAILURE);139}140141}142143void144fmd_serd_hash_destroy(fmd_serd_hash_t *shp)145{146fmd_serd_eng_t *sgp, *ngp;147uint_t i;148149for (i = 0; i < shp->sh_hashlen; i++) {150for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = ngp) {151ngp = sgp->sg_next;152fmd_serd_eng_free(sgp);153}154}155156free(shp->sh_hash);157memset(shp, 0, sizeof (fmd_serd_hash_t));158}159160void161fmd_serd_hash_apply(fmd_serd_hash_t *shp, fmd_serd_eng_f *func, void *arg)162{163fmd_serd_eng_t *sgp;164uint_t i;165166for (i = 0; i < shp->sh_hashlen; i++) {167for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next)168func(sgp, arg);169}170}171172fmd_serd_eng_t *173fmd_serd_eng_insert(fmd_serd_hash_t *shp, const char *name,174uint_t n, hrtime_t t)175{176uint_t h = fmd_strhash(name) % shp->sh_hashlen;177fmd_serd_eng_t *sgp = fmd_serd_eng_alloc(name, n, t);178179serd_log_msg(" SERD Engine: inserting %s N %d T %llu",180name, (int)n, (long long unsigned)t);181182sgp->sg_next = shp->sh_hash[h];183shp->sh_hash[h] = sgp;184shp->sh_count++;185186return (sgp);187}188189fmd_serd_eng_t *190fmd_serd_eng_lookup(fmd_serd_hash_t *shp, const char *name)191{192uint_t h = fmd_strhash(name) % shp->sh_hashlen;193fmd_serd_eng_t *sgp;194195for (sgp = shp->sh_hash[h]; sgp != NULL; sgp = sgp->sg_next) {196if (strcmp(name, sgp->sg_name) == 0)197return (sgp);198}199200return (NULL);201}202203void204fmd_serd_eng_delete(fmd_serd_hash_t *shp, const char *name)205{206uint_t h = fmd_strhash(name) % shp->sh_hashlen;207fmd_serd_eng_t *sgp, **pp = &shp->sh_hash[h];208209serd_log_msg(" SERD Engine: deleting %s", name);210211for (sgp = *pp; sgp != NULL; sgp = sgp->sg_next) {212if (strcmp(sgp->sg_name, name) != 0)213pp = &sgp->sg_next;214else215break;216}217218if (sgp != NULL) {219*pp = sgp->sg_next;220fmd_serd_eng_free(sgp);221assert(shp->sh_count != 0);222shp->sh_count--;223}224}225226static void227fmd_serd_eng_discard(fmd_serd_eng_t *sgp, fmd_serd_elem_t *sep)228{229list_remove(&sgp->sg_list, sep);230sgp->sg_count--;231232serd_log_msg(" SERD Engine: discarding %s, %d remaining",233sgp->sg_name, (int)sgp->sg_count);234235free(sep);236}237238int239fmd_serd_eng_record(fmd_serd_eng_t *sgp, hrtime_t hrt)240{241fmd_serd_elem_t *sep, *oep;242243/*244* If the fired flag is already set, return false and discard the245* 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*/249if (sgp->sg_flags & FMD_SERD_FIRED) {250serd_log_msg(" SERD Engine: record %s already fired!",251sgp->sg_name);252return (B_FALSE);253}254255while (sgp->sg_count >= sgp->sg_n)256fmd_serd_eng_discard(sgp, list_tail(&sgp->sg_list));257258sep = malloc(sizeof (fmd_serd_elem_t));259if (sep == NULL) {260perror("malloc");261exit(EXIT_FAILURE);262}263sep->se_hrt = hrt;264265list_insert_head(&sgp->sg_list, sep);266sgp->sg_count++;267268serd_log_msg(" SERD Engine: recording %s of %d (%llu)",269sgp->sg_name, (int)sgp->sg_count, (long long unsigned)hrt);270271/*272* Pick up the oldest element pointer for comparison to 'sep'. We must273* do this after adding 'sep' because 'oep' and 'sep' can be the same.274*/275oep = list_tail(&sgp->sg_list);276277if (sgp->sg_count >= sgp->sg_n &&278fmd_event_delta(oep->se_hrt, sep->se_hrt) <= sgp->sg_t) {279sgp->sg_flags |= FMD_SERD_FIRED | FMD_SERD_DIRTY;280serd_log_msg(" SERD Engine: fired %s", sgp->sg_name);281return (B_TRUE);282}283284sgp->sg_flags |= FMD_SERD_DIRTY;285return (B_FALSE);286}287288int289fmd_serd_eng_fired(fmd_serd_eng_t *sgp)290{291return (sgp->sg_flags & FMD_SERD_FIRED);292}293294int295fmd_serd_eng_empty(fmd_serd_eng_t *sgp)296{297return (sgp->sg_count == 0);298}299300void301fmd_serd_eng_reset(fmd_serd_eng_t *sgp)302{303serd_log_msg(" SERD Engine: resetting %s", sgp->sg_name);304305while (sgp->sg_count != 0)306fmd_serd_eng_discard(sgp, list_head(&sgp->sg_list));307308sgp->sg_flags &= ~FMD_SERD_FIRED;309sgp->sg_flags |= FMD_SERD_DIRTY;310}311312void313fmd_serd_eng_gc(fmd_serd_eng_t *sgp, void *arg)314{315(void) arg;316fmd_serd_elem_t *sep, *nep;317hrtime_t hrt;318319if (sgp->sg_count == 0 || (sgp->sg_flags & FMD_SERD_FIRED))320return; /* no garbage collection needed if empty or fired */321322sep = list_head(&sgp->sg_list);323if (sep == NULL)324return;325326hrt = sep->se_hrt - sgp->sg_t;327328for (sep = list_head(&sgp->sg_list); sep != NULL; sep = nep) {329if (sep->se_hrt >= hrt)330break; /* sep and subsequent events are all within T */331332nep = list_next(&sgp->sg_list, sep);333fmd_serd_eng_discard(sgp, sep);334sgp->sg_flags |= FMD_SERD_DIRTY;335}336}337338339