15b50d3e8SMike Gerdts /* SPDX-License-Identifier: BSD-3-Clause 25b50d3e8SMike Gerdts * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. 35b50d3e8SMike Gerdts */ 45b50d3e8SMike Gerdts 55b50d3e8SMike Gerdts #include "spdk/stdinc.h" 65b50d3e8SMike Gerdts #include "spdk/string.h" 75b50d3e8SMike Gerdts #include "spdk/thread.h" 85b50d3e8SMike Gerdts #include "spdk/util.h" 95b50d3e8SMike Gerdts #include "spdk/log.h" 105b50d3e8SMike Gerdts 115b50d3e8SMike Gerdts struct spdk_deprecation { 127c1a27afSSlawomir Ptak const char tag[64]; 135b50d3e8SMike Gerdts const char desc[64]; 145b50d3e8SMike Gerdts const char remove[16]; 155b50d3e8SMike Gerdts TAILQ_ENTRY(spdk_deprecation) link; 165b50d3e8SMike Gerdts uint64_t hits; 175b50d3e8SMike Gerdts /* How often (nanoseconds) to log. */ 185b50d3e8SMike Gerdts uint64_t interval; 195b50d3e8SMike Gerdts /* How many messages were not logged due to rate limiting */ 205b50d3e8SMike Gerdts uint32_t deferred; 215b50d3e8SMike Gerdts /* CLOCK_MONOTONIC microseconds since g_deprecation_epoch when last warning was logged */ 225b50d3e8SMike Gerdts uint64_t last_log; 235b50d3e8SMike Gerdts }; 245b50d3e8SMike Gerdts 255b50d3e8SMike Gerdts static TAILQ_HEAD(, spdk_deprecation) g_deprecations = TAILQ_HEAD_INITIALIZER(g_deprecations); 26*f7b31b2bSPierre Lestringant static struct timespec g_deprecation_epoch; 275b50d3e8SMike Gerdts 285b50d3e8SMike Gerdts static void 295b50d3e8SMike Gerdts __attribute__((constructor)) 305b50d3e8SMike Gerdts deprecation_init(void) 315b50d3e8SMike Gerdts { 325b50d3e8SMike Gerdts clock_gettime(CLOCK_MONOTONIC, &g_deprecation_epoch); 335b50d3e8SMike Gerdts } 345b50d3e8SMike Gerdts 355b50d3e8SMike Gerdts static inline uint64_t 365b50d3e8SMike Gerdts get_ns_since_epoch(void) 375b50d3e8SMike Gerdts { 385b50d3e8SMike Gerdts struct timespec now; 395b50d3e8SMike Gerdts 405b50d3e8SMike Gerdts clock_gettime(CLOCK_MONOTONIC, &now); 415b50d3e8SMike Gerdts return (now.tv_sec - g_deprecation_epoch.tv_sec) * SPDK_SEC_TO_NSEC + 425b50d3e8SMike Gerdts now.tv_nsec - g_deprecation_epoch.tv_nsec; 435b50d3e8SMike Gerdts } 445b50d3e8SMike Gerdts 455b50d3e8SMike Gerdts int 465b50d3e8SMike Gerdts spdk_log_deprecation_register(const char *tag, const char *description, const char *remove_release, 475b50d3e8SMike Gerdts uint32_t rate_limit_seconds, struct spdk_deprecation **depp) 485b50d3e8SMike Gerdts { 495b50d3e8SMike Gerdts struct spdk_deprecation *dep; 505b50d3e8SMike Gerdts 515b50d3e8SMike Gerdts assert(strnlen(tag, sizeof(dep->tag)) < sizeof(dep->tag)); 525b50d3e8SMike Gerdts assert(strnlen(description, sizeof(dep->desc)) < sizeof(dep->desc)); 535b50d3e8SMike Gerdts assert(strnlen(remove_release, sizeof(dep->remove)) < sizeof(dep->remove)); 545b50d3e8SMike Gerdts 555b50d3e8SMike Gerdts dep = calloc(1, sizeof(*dep)); 565b50d3e8SMike Gerdts if (dep == NULL) { 575b50d3e8SMike Gerdts return -ENOMEM; 585b50d3e8SMike Gerdts } 595b50d3e8SMike Gerdts 605b50d3e8SMike Gerdts snprintf((char *)dep->tag, sizeof(dep->tag), "%s", tag); 615b50d3e8SMike Gerdts snprintf((char *)dep->desc, sizeof(dep->desc), "%s", description); 625b50d3e8SMike Gerdts snprintf((char *)dep->remove, sizeof(dep->remove), "%s", remove_release); 635b50d3e8SMike Gerdts dep->interval = rate_limit_seconds * SPDK_SEC_TO_NSEC; 645b50d3e8SMike Gerdts 655b50d3e8SMike Gerdts TAILQ_INSERT_TAIL(&g_deprecations, dep, link); 665b50d3e8SMike Gerdts *depp = dep; 675b50d3e8SMike Gerdts return 0; 685b50d3e8SMike Gerdts } 695b50d3e8SMike Gerdts 705b50d3e8SMike Gerdts /* 715b50d3e8SMike Gerdts * There is potential for races between pthreads leading to over or under reporting of times that 725b50d3e8SMike Gerdts * deprecated code was hit. If this function is called in a hot path where that is likely, we care 735b50d3e8SMike Gerdts * more about performance than accuracy of the error counts. The important thing is that at least 745b50d3e8SMike Gerdts * one of the racing updates the hits counter to non-zero and the warning is logged at least once. 755b50d3e8SMike Gerdts */ 765b50d3e8SMike Gerdts void 775b50d3e8SMike Gerdts spdk_log_deprecated(struct spdk_deprecation *dep, const char *file, uint32_t line, const char *func) 785b50d3e8SMike Gerdts { 795b50d3e8SMike Gerdts uint64_t now = get_ns_since_epoch(); 805b50d3e8SMike Gerdts 815b50d3e8SMike Gerdts if (dep == NULL) { 825b50d3e8SMike Gerdts SPDK_ERRLOG("NULL deprecation passed from %s:%u:%s\n", file, line, func); 835b50d3e8SMike Gerdts assert(false); 845b50d3e8SMike Gerdts return; 855b50d3e8SMike Gerdts } 865b50d3e8SMike Gerdts 875b50d3e8SMike Gerdts dep->hits++; 885b50d3e8SMike Gerdts 895b50d3e8SMike Gerdts if (dep->interval != 0) { 905b50d3e8SMike Gerdts if (dep->last_log != 0 && now < dep->last_log + dep->interval) { 915b50d3e8SMike Gerdts dep->deferred++; 925b50d3e8SMike Gerdts return; 935b50d3e8SMike Gerdts } 945b50d3e8SMike Gerdts } 955b50d3e8SMike Gerdts 965b50d3e8SMike Gerdts dep->last_log = now; 975b50d3e8SMike Gerdts 985b50d3e8SMike Gerdts spdk_log(SPDK_LOG_WARN, file, line, func, "%s: deprecated feature %s to be removed in %s\n", 995b50d3e8SMike Gerdts dep->tag, dep->desc, dep->remove); 1005b50d3e8SMike Gerdts if (dep->deferred != 0) { 1015b50d3e8SMike Gerdts SPDK_WARNLOG("%s: %u messages suppressed\n", dep->tag, dep->deferred); 1025b50d3e8SMike Gerdts dep->deferred = 0; 1035b50d3e8SMike Gerdts } 1045b50d3e8SMike Gerdts } 1055b50d3e8SMike Gerdts 1065b50d3e8SMike Gerdts int 1075b50d3e8SMike Gerdts spdk_log_for_each_deprecation(void *ctx, spdk_log_for_each_deprecation_fn fn) 1085b50d3e8SMike Gerdts { 1095b50d3e8SMike Gerdts struct spdk_deprecation *dep; 1105b50d3e8SMike Gerdts int rc = 0; 1115b50d3e8SMike Gerdts 1125b50d3e8SMike Gerdts TAILQ_FOREACH(dep, &g_deprecations, link) { 1135b50d3e8SMike Gerdts rc = fn(ctx, dep); 1145b50d3e8SMike Gerdts if (rc != 0) { 1155b50d3e8SMike Gerdts break; 1165b50d3e8SMike Gerdts } 1175b50d3e8SMike Gerdts } 1185b50d3e8SMike Gerdts 1195b50d3e8SMike Gerdts return rc; 1205b50d3e8SMike Gerdts } 1215b50d3e8SMike Gerdts 1225b50d3e8SMike Gerdts const char * 1235b50d3e8SMike Gerdts spdk_deprecation_get_tag(const struct spdk_deprecation *deprecation) 1245b50d3e8SMike Gerdts { 1255b50d3e8SMike Gerdts return deprecation->tag; 1265b50d3e8SMike Gerdts } 1275b50d3e8SMike Gerdts 1285b50d3e8SMike Gerdts const char * 1295b50d3e8SMike Gerdts spdk_deprecation_get_description(const struct spdk_deprecation *deprecation) 1305b50d3e8SMike Gerdts { 1315b50d3e8SMike Gerdts return deprecation->desc; 1325b50d3e8SMike Gerdts } 1335b50d3e8SMike Gerdts 1345b50d3e8SMike Gerdts const char * 1355b50d3e8SMike Gerdts spdk_deprecation_get_remove_release(const struct spdk_deprecation *deprecation) 1365b50d3e8SMike Gerdts { 1375b50d3e8SMike Gerdts return deprecation->remove; 1385b50d3e8SMike Gerdts } 1395b50d3e8SMike Gerdts 1405b50d3e8SMike Gerdts uint64_t 1415b50d3e8SMike Gerdts spdk_deprecation_get_hits(const struct spdk_deprecation *deprecation) 1425b50d3e8SMike Gerdts { 1435b50d3e8SMike Gerdts return deprecation->hits; 1445b50d3e8SMike Gerdts } 145