diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/event.c | 23 | ||||
| -rw-r--r-- | lib/frrevent.h | 2 | ||||
| -rw-r--r-- | lib/frrscript.c | 10 | ||||
| -rw-r--r-- | lib/frrscript.h | 2 | ||||
| -rw-r--r-- | lib/if.c | 6 | ||||
| -rw-r--r-- | lib/if.h | 2 | ||||
| -rw-r--r-- | lib/libfrr.c | 41 | ||||
| -rw-r--r-- | lib/libospf.h | 5 | ||||
| -rw-r--r-- | lib/log.c | 2 | ||||
| -rw-r--r-- | lib/memory.c | 103 | ||||
| -rw-r--r-- | lib/memory.h | 8 | ||||
| -rw-r--r-- | lib/routemap.c | 39 | ||||
| -rw-r--r-- | lib/routemap.h | 2 | ||||
| -rw-r--r-- | lib/sigevent.c | 14 | ||||
| -rw-r--r-- | lib/sockunion.c | 26 | ||||
| -rw-r--r-- | lib/sockunion.h | 1 | ||||
| -rw-r--r-- | lib/wheel.c | 13 | ||||
| -rw-r--r-- | lib/wheel.h | 1 |
18 files changed, 179 insertions, 121 deletions
diff --git a/lib/event.c b/lib/event.c index c573923f51..cfe8c3adc0 100644 --- a/lib/event.c +++ b/lib/event.c @@ -669,24 +669,6 @@ static void thread_array_free(struct event_loop *m, struct event **thread_array) XFREE(MTYPE_EVENT_POLL, thread_array); } -/* - * event_master_free_unused - * - * As threads are finished with they are put on the - * unuse list for later reuse. - * If we are shutting down, Free up unused threads - * So we can see if we forget to shut anything off - */ -void event_master_free_unused(struct event_loop *m) -{ - frr_with_mutex (&m->mtx) { - struct event *t; - - while ((t = event_list_pop(&m->unuse))) - thread_free(m, t); - } -} - /* Stop thread scheduler. */ void event_master_free(struct event_loop *m) { @@ -793,7 +775,6 @@ static struct event *thread_get(struct event_loop *m, uint8_t type, thread = XCALLOC(MTYPE_THREAD, sizeof(struct event)); /* mutex only needs to be initialized at struct creation. */ pthread_mutex_init(&thread->mtx, NULL); - m->alloc++; } thread->type = type; @@ -832,10 +813,6 @@ static struct event *thread_get(struct event_loop *m, uint8_t type, static void thread_free(struct event_loop *master, struct event *thread) { - /* Update statistics. */ - assert(master->alloc > 0); - master->alloc--; - /* Free allocated resources. */ pthread_mutex_destroy(&thread->mtx); XFREE(MTYPE_THREAD, thread); diff --git a/lib/frrevent.h b/lib/frrevent.h index 94640a76b7..44776b29a7 100644 --- a/lib/frrevent.h +++ b/lib/frrevent.h @@ -85,7 +85,6 @@ struct event_loop { int io_pipe[2]; int fd_limit; struct fd_handler handler; - unsigned long alloc; long selectpoll_timeout; bool spin; bool handle_signals; @@ -227,7 +226,6 @@ static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b) extern struct event_loop *event_master_create(const char *name); void event_master_set_name(struct event_loop *master, const char *name); extern void event_master_free(struct event_loop *m); -extern void event_master_free_unused(struct event_loop *m); extern void _event_add_read_write(const struct xref_eventsched *xref, struct event_loop *master, diff --git a/lib/frrscript.c b/lib/frrscript.c index dbae31b666..06460b014d 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -27,6 +27,16 @@ struct frrscript_names_head frrscript_names_hash; void _lua_decode_noop(lua_State *L, ...) {} +void frrscript_names_config_write(struct vty *vty) +{ + struct frrscript_names_entry *lua_script_entry; + + frr_each (frrscript_names, &frrscript_names_hash, lua_script_entry) + if (lua_script_entry->script_name[0] != '\0') + vty_out(vty, "zebra on-rib-process script %s\n", + lua_script_entry->script_name); +} + /* * Wrapper for frrscript_names_add * Use this to register hook calls when a daemon starts up diff --git a/lib/frrscript.h b/lib/frrscript.h index ce313a1b63..75ac53c609 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -44,6 +44,8 @@ struct frrscript_names_entry { extern struct frrscript_names_head frrscript_names_hash; +extern void frrscript_names_config_write(struct vty *vty); + int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1, const struct frrscript_names_entry *snhe2); uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe); @@ -1002,12 +1002,6 @@ void if_terminate(struct vrf *vrf) while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) { ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name); - - if (ifp->node) { - ifp->node->info = NULL; - route_unlock_node(ifp->node); - ifp->node = NULL; - } if_delete(&ifp); } } @@ -295,8 +295,6 @@ struct interface { struct if_data stats; #endif /* HAVE_NET_RT_IFLIST */ - struct route_node *node; - struct vrf *vrf; /* diff --git a/lib/libfrr.c b/lib/libfrr.c index 313fe99fd3..d1a9f0b1cb 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -1239,10 +1239,6 @@ void frr_early_fini(void) void frr_fini(void) { - FILE *fp; - char filename[128]; - int have_leftovers = 0; - hook_call(frr_fini); vty_terminate(); @@ -1263,32 +1259,27 @@ void frr_fini(void) event_master_free(master); master = NULL; zlog_tls_buffer_fini(); - zlog_fini(); + + if (0) { + /* this is intentionally disabled. zlog remains running until + * exit(), so even the very last item done during shutdown can + * have its zlog() messages written out. + * + * Yes this causes memory leaks. They are explicitly marked + * with DEFINE_MGROUP_ACTIVEATEXIT, which is only used for + * log target memory allocations, and excluded from leak + * reporting at shutdown. This is strongly preferable over + * just discarding error messages at shutdown. + */ + zlog_fini(); + } + /* frrmod_init -> nothing needed / hooks */ rcu_shutdown(); frrmod_terminate(); - /* also log memstats to stderr when stderr goes to a file*/ - if (debug_memstats_at_exit || !isatty(STDERR_FILENO)) - have_leftovers = log_memstats(stderr, di->name); - - /* in case we decide at runtime that we want exit-memstats for - * a daemon - * (only do this if we actually have something to print though) - */ - if (!debug_memstats_at_exit || !have_leftovers) - return; - - snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu", - di->name, (unsigned long long)getpid(), - (unsigned long long)time(NULL)); - - fp = fopen(filename, "w"); - if (fp) { - log_memstats(fp, di->name); - fclose(fp); - } + log_memstats(di->name, debug_memstats_at_exit); } struct json_object *frr_daemon_state_load(void) diff --git a/lib/libospf.h b/lib/libospf.h index 8a208beb3c..5becc1594e 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -27,8 +27,9 @@ extern "C" { #else #define OSPF_LS_REFRESH_TIME 1800 #endif -#define OSPF_MIN_LS_INTERVAL 5000 /* msec */ -#define OSPF_MIN_LS_ARRIVAL 1000 /* in milliseconds */ +#define OSPF_MIN_LS_INTERVAL 5000 /* milliseconds */ +#define OSPF_MIN_LS_ARRIVAL 1000 /* milliseconds */ +#define OSPF_MIN_LS_ARRIVAL_MAX 5000 /* milliseconds */ #define OSPF_LSA_INITIAL_AGE 0 /* useful for debug */ #define OSPF_LSA_MAXAGE 3600 #define OSPF_CHECK_AGE 300 @@ -306,7 +306,7 @@ void memory_oom(size_t size, const char *name) "out of memory: failed to allocate %zu bytes for %s object", size, name); zlog_backtrace(LOG_CRIT); - log_memstats(stderr, "log"); + log_memstats(zlog_progname, true); abort(); } diff --git a/lib/memory.c b/lib/memory.c index ac39516edd..9da46bdb89 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -148,35 +148,108 @@ int qmem_walk(qmem_walk_fn *func, void *arg) } struct exit_dump_args { - FILE *fp; - const char *prefix; + const char *daemon_name; + bool do_log; + bool do_file; + bool do_stderr; int error; + FILE *fp; + struct memgroup *last_mg; }; +static void qmem_exit_fopen(struct exit_dump_args *eda) +{ + char filename[128]; + + if (eda->fp || !eda->do_file || !eda->daemon_name) + return; + + snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu", eda->daemon_name, + (unsigned long long)getpid(), (unsigned long long)time(NULL)); + eda->fp = fopen(filename, "w"); + + if (!eda->fp) { + zlog_err("failed to open memstats dump file %pSQq: %m", filename); + /* don't try opening file over and over again */ + eda->do_file = false; + } +} + static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt) { struct exit_dump_args *eda = arg; + const char *prefix = eda->daemon_name ?: "NONE"; + char size[32]; + + if (!mt) + /* iterator calls mg=X, mt=NULL first */ + return 0; - if (!mt) { - fprintf(eda->fp, - "%s: showing active allocations in memory group %s\n", - eda->prefix, mg->name); + if (!mt->n_alloc) + return 0; - } else if (mt->n_alloc) { - char size[32]; - if (!mg->active_at_exit) - eda->error++; + if (mt->size != SIZE_VAR) snprintf(size, sizeof(size), "%10zu", mt->size); - fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", - eda->prefix, mt->name, mt->n_alloc, - mt->size == SIZE_VAR ? "(variably sized)" : size); + else + snprintf(size, sizeof(size), "(variably sized)"); + + if (mg->active_at_exit) { + /* not an error - this memgroup has allocations remain active + * at exit. Only printed to zlog_debug. + */ + if (!eda->do_log) + return 0; + + if (eda->last_mg != mg) { + zlog_debug("showing active allocations in memory group %s (not an error)", + mg->name); + eda->last_mg = mg; + } + zlog_debug("memstats: %-30s: %6zu * %s", mt->name, mt->n_alloc, size); + return 0; } + + eda->error++; + if (eda->do_file) + qmem_exit_fopen(eda); + + if (eda->last_mg != mg) { + if (eda->do_log) + zlog_warn("showing active allocations in memory group %s", mg->name); + if (eda->do_stderr) + fprintf(stderr, "%s: showing active allocations in memory group %s\n", + prefix, mg->name); + if (eda->fp) + fprintf(eda->fp, "%s: showing active allocations in memory group %s\n", + prefix, mg->name); + eda->last_mg = mg; + } + + if (eda->do_log) + zlog_warn("memstats: %-30s: %6zu * %s", mt->name, mt->n_alloc, size); + if (eda->do_stderr) + fprintf(stderr, "%s: memstats: %-30s: %6zu * %s\n", prefix, mt->name, mt->n_alloc, + size); + if (eda->fp) + fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", prefix, mt->name, mt->n_alloc, + size); return 0; } -int log_memstats(FILE *fp, const char *prefix) +int log_memstats(const char *daemon_name, bool enabled) { - struct exit_dump_args eda = {.fp = fp, .prefix = prefix, .error = 0}; + struct exit_dump_args eda = { + .daemon_name = daemon_name, + .do_log = enabled, + .do_file = enabled, + .do_stderr = enabled || !isatty(STDERR_FILENO), + .error = 0, + }; + qmem_walk(qmem_exit_walker, &eda); + if (eda.fp) + fclose(eda.fp); + if (eda.error && eda.do_log) + zlog_warn("exiting with %d leaked MTYPEs", eda.error); return eda.error; } diff --git a/lib/memory.h b/lib/memory.h index 8e8c61da04..8658018832 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -67,6 +67,8 @@ struct memgroup { * but MGROUP_* aren't. */ +/* clang-format off */ + #define DECLARE_MGROUP(name) extern struct memgroup _mg_##name #define _DEFINE_MGROUP(mname, desc, ...) \ struct memgroup _mg_##mname _DATA_SECTION("mgroups") = { \ @@ -75,6 +77,7 @@ struct memgroup { .next = NULL, \ .insert = NULL, \ .ref = NULL, \ + __VA_ARGS__ \ }; \ static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \ static void _mginit_##mname(void) \ @@ -136,6 +139,8 @@ struct memgroup { DEFINE_MTYPE_ATTR(group, name, static, desc) \ /* end */ +/* clang-format on */ + DECLARE_MGROUP(LIB); DECLARE_MTYPE(TMP); DECLARE_MTYPE(TMP_TTABLE); @@ -176,8 +181,7 @@ static inline size_t mtype_stats_alloc(struct memtype *mt) * last value from qmem_walk_fn. */ typedef int qmem_walk_fn(void *arg, struct memgroup *mg, struct memtype *mt); extern int qmem_walk(qmem_walk_fn *func, void *arg); -extern int log_memstats(FILE *fp, const char *); -#define log_memstats_stderr(prefix) log_memstats(stderr, prefix) +extern int log_memstats(const char *daemon_name, bool enabled); extern __attribute__((__noreturn__)) void memory_oom(size_t size, const char *name); diff --git a/lib/routemap.c b/lib/routemap.c index ea917ebd8c..120731fa61 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -941,11 +941,12 @@ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map, json_object_boolean_add(json_rmap, "processedChange", map->to_be_processed); json_object_object_add(json_rmap, "rules", json_rules); + json_object_int_add(json_rmap, "cpuTimeMS", map->cputime / 1000); } else { vty_out(vty, "route-map: %s Invoked: %" PRIu64 - " Optimization: %s Processed Change: %s\n", - map->name, map->applied - map->applied_clear, + " (%zu milliseconds total) Optimization: %s Processed Change: %s\n", + map->name, map->applied - map->applied_clear, map->cputime / 1000, map->optimization_disabled ? "disabled" : "enabled", map->to_be_processed ? "true" : "false"); } @@ -967,6 +968,7 @@ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map, json_object_int_add(json_rule, "invoked", index->applied - index->applied_clear); + json_object_int_add(json_rule, "cpuTimeMS", index->cputime / 1000); /* Description */ if (index->description) @@ -1018,9 +1020,10 @@ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map, json_object_string_add(json_rule, "action", action); } else { - vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n", + vty_out(vty, + " %s, sequence %d Invoked %" PRIu64 " (%zu milliseconds total)\n", route_map_type_str(index->type), index->pref, - index->applied - index->applied_clear); + index->applied - index->applied_clear, index->cputime / 1000); /* Description */ if (index->description) @@ -2548,6 +2551,9 @@ route_map_result_t route_map_apply_ext(struct route_map *map, struct route_map_index *index = NULL; struct route_map_rule *set = NULL; bool skip_match_clause = false; + RUSAGE_T mbefore, mafter; + RUSAGE_T ibefore, iafter; + unsigned long cputime; if (recursion > RMAP_RECURSION_LIMIT) { if (map) @@ -2570,6 +2576,9 @@ route_map_result_t route_map_apply_ext(struct route_map *map, map->applied++; + GETRUSAGE(&mbefore); + ibefore = mbefore; + if (prefix->family == AF_EVPN) { index = map->head; } else { @@ -2580,6 +2589,12 @@ route_map_result_t route_map_apply_ext(struct route_map *map, if (index) { index->applied++; + + GETRUSAGE(&iafter); + event_consumed_time(&iafter, &ibefore, &cputime); + index->cputime += cputime; + ibefore = iafter; + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug( "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s", @@ -2718,6 +2733,10 @@ route_map_result_t route_map_apply_ext(struct route_map *map, goto route_map_apply_end; } } + GETRUSAGE(&iafter); + event_consumed_time(&iafter, &ibefore, &cputime); + index->cputime += cputime; + ibefore = iafter; } route_map_apply_end: @@ -2733,6 +2752,13 @@ route_map_apply_end: *pref = 65536; } + if (map) { + GETRUSAGE(&mbefore); + GETRUSAGE(&mafter); + event_consumed_time(&mafter, &mbefore, &cputime); + map->cputime += cputime; + } + return (ret); } @@ -3090,8 +3116,11 @@ static void clear_route_map_helper(struct route_map *map) struct route_map_index *index; map->applied_clear = map->applied; - for (index = map->head; index; index = index->next) + map->cputime = 0; + for (index = map->head; index; index = index->next) { index->applied_clear = index->applied; + index->cputime = 0; + } } DEFPY (rmap_clear_counters, diff --git a/lib/routemap.h b/lib/routemap.h index ef9b3cb160..e0f738502b 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -176,6 +176,7 @@ struct route_map_index { /* Keep track how many times we've try to apply */ uint64_t applied; uint64_t applied_clear; + size_t cputime; /* List of match/sets contexts. */ TAILQ_HEAD(, routemap_hook_context) rhclist; @@ -210,6 +211,7 @@ struct route_map { /* How many times have we applied this route-map */ uint64_t applied; uint64_t applied_clear; + size_t cputime; /* Counter to track active usage of this route-map */ uint16_t use_count; diff --git a/lib/sigevent.c b/lib/sigevent.c index 3e69f280da..7c465bfcec 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -237,8 +237,18 @@ core_handler(int signo, siginfo_t *siginfo, void *context) zlog_signal(signo, "aborting...", siginfo, pc); - /* dump memory stats on core */ - log_memstats(stderr, "core_handler"); + /* there used to be a log_memstats() call here, to dump MTYPE counters + * on a coredump. This is not possible since log_memstats is not + * AS-Safe, as it calls fopen(), fprintf(), and cousins. This can + * lead to a deadlock depending on where we crashed - very much not a + * good thing if the process just hangs there after a crash. + * + * The alarm(1) above tries to alleviate this, but that's really a + * last resort recovery. Stick with AS-safe calls here. + * + * If the fprintf() calls are removed from log_memstats(), this can be + * added back in, since writing to log with zlog_sigsafe() is AS-safe. + */ /* * This is a buffer flush because FRR is going down diff --git a/lib/sockunion.c b/lib/sockunion.c index c37ab1d6dd..7acb5004db 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -403,8 +403,7 @@ int sockunion_same(const union sockunion *su1, const union sockunion *su2) sizeof(struct in_addr)); break; case AF_INET6: - ret = memcmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, - sizeof(struct in6_addr)); + ret = IPV6_ADDR_CMP(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); if ((ret == 0) && IN6_IS_ADDR_LINKLOCAL(&su1->sin6.sin6_addr)) { /* compare interface indices */ if (su1->sin6.sin6_scope_id && su2->sin6.sin6_scope_id) @@ -588,23 +587,6 @@ static void __attribute__((unused)) sockunion_print(const union sockunion *su) } } -int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2) -{ - unsigned int i; - const uint8_t *p1, *p2; - - p1 = (const uint8_t *)addr1; - p2 = (const uint8_t *)addr2; - - for (i = 0; i < sizeof(struct in6_addr); i++) { - if (p1[i] > p2[i]) - return 1; - else if (p1[i] < p2[i]) - return -1; - } - return 0; -} - int sockunion_cmp(const union sockunion *su1, const union sockunion *su2) { if (su1->sa.sa_family > su2->sa.sa_family) @@ -621,7 +603,8 @@ int sockunion_cmp(const union sockunion *su1, const union sockunion *su2) return -1; } if (su1->sa.sa_family == AF_INET6) - return in6addr_cmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); + return IPV6_ADDR_CMP(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); + return 0; } @@ -727,8 +710,7 @@ int sockunion_is_null(const union sockunion *su) case AF_INET: return (su->sin.sin_addr.s_addr == 0); case AF_INET6: - return !memcmp(su->sin6.sin6_addr.s6_addr, null_s6_addr, - sizeof(null_s6_addr)); + return !IPV6_ADDR_CMP(su->sin6.sin6_addr.s6_addr, null_s6_addr); default: return 0; } diff --git a/lib/sockunion.h b/lib/sockunion.h index 146651225c..5152e70a23 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -93,7 +93,6 @@ enum connect_result { connect_error, connect_success, connect_in_progress }; /* Prototypes. */ extern int str2sockunion(const char *, union sockunion *); extern const char *sockunion2str(const union sockunion *, char *, size_t); -int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2); extern int sockunion_cmp(const union sockunion *, const union sockunion *); extern int sockunion_same(const union sockunion *, const union sockunion *); extern unsigned int sockunion_hash(const union sockunion *); diff --git a/lib/wheel.c b/lib/wheel.c index 2520e81d49..3e90693b45 100644 --- a/lib/wheel.c +++ b/lib/wheel.c @@ -18,7 +18,7 @@ static int debug_timer_wheel = 0; static void wheel_timer_thread(struct event *t); -static void wheel_timer_thread_helper(struct event *t) +static void wheel_timer_thread(struct event *t) { struct listnode *node, *nextnode; unsigned long long curr_slot; @@ -51,15 +51,6 @@ static void wheel_timer_thread_helper(struct event *t) wheel->nexttime * slots_to_skip, &wheel->timer); } -static void wheel_timer_thread(struct event *t) -{ - struct timer_wheel *wheel; - - wheel = EVENT_ARG(t); - - event_execute(wheel->master, wheel_timer_thread_helper, wheel, 0, NULL); -} - struct timer_wheel *wheel_init(struct event_loop *master, int period, size_t slots, unsigned int (*slot_key)(const void *), @@ -70,7 +61,6 @@ struct timer_wheel *wheel_init(struct event_loop *master, int period, wheel = XCALLOC(MTYPE_TIMER_WHEEL, sizeof(struct timer_wheel)); - wheel->name = XSTRDUP(MTYPE_TIMER_WHEEL, run_name); wheel->slot_key = slot_key; wheel->slot_run = slot_run; @@ -101,7 +91,6 @@ void wheel_delete(struct timer_wheel *wheel) EVENT_OFF(wheel->timer); XFREE(MTYPE_TIMER_WHEEL_LIST, wheel->wheel_slot_lists); - XFREE(MTYPE_TIMER_WHEEL, wheel->name); XFREE(MTYPE_TIMER_WHEEL, wheel); } diff --git a/lib/wheel.h b/lib/wheel.h index 0d9ac10020..a8fa2f9564 100644 --- a/lib/wheel.h +++ b/lib/wheel.h @@ -12,7 +12,6 @@ extern "C" { #endif struct timer_wheel { - char *name; struct event_loop *master; int slots; long long curr_slot; |
