summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_aspath.c37
-rw-r--r--bgpd/bgp_aspath.h1
-rw-r--r--bgpd/bgp_fsm.c10
-rw-r--r--bgpd/bgp_fsm.h1
-rw-r--r--bgpd/bgp_packet.c60
-rw-r--r--bgpd/bgp_route.c22
-rw-r--r--bgpd/bgpd.h2
-rw-r--r--lib/libfrr.c41
-rw-r--r--lib/log.c2
-rw-r--r--lib/memory.c103
-rw-r--r--lib/memory.h8
-rw-r--r--lib/sigevent.c14
-rw-r--r--pimd/pim_autorp.c5
-rw-r--r--tests/isisd/test_fuzz_isis_tlv.c2
-rw-r--r--tests/isisd/test_isis_spf.c2
-rw-r--r--tests/lib/cli/common_cli.c2
-rw-r--r--tests/lib/northbound/test_oper_data.c2
-rw-r--r--tests/lib/test_typelist.c2
-rw-r--r--tests/lib/test_zmq.c2
-rw-r--r--zebra/kernel_netlink.c2
20 files changed, 224 insertions, 96 deletions
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index 4c1615a5c6..a86b42e250 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -297,6 +297,8 @@ static struct aspath *aspath_new(enum asnotation_mode asnotation)
as = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath));
as->asnotation = asnotation;
+ as->count = 0;
+
return as;
}
@@ -400,6 +402,11 @@ unsigned int aspath_count_confeds(struct aspath *aspath)
unsigned int aspath_count_hops(const struct aspath *aspath)
{
+ return aspath->count;
+}
+
+static unsigned int aspath_count_hops_internal(const struct aspath *aspath)
+{
int count = 0;
struct assegment *seg = aspath->segments;
@@ -708,6 +715,7 @@ struct aspath *aspath_dup(struct aspath *aspath)
else
new->str[0] = '\0';
+ new->count = aspath->count;
return new;
}
@@ -729,6 +737,7 @@ static void *aspath_hash_alloc(void *arg)
new->str_len = aspath->str_len;
new->json = aspath->json;
new->asnotation = aspath->asnotation;
+ new->count = aspath->count;
return new;
}
@@ -856,6 +865,8 @@ struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit,
if (assegments_parse(s, length, &as.segments, use32bit) < 0)
return NULL;
+ as.count = aspath_count_hops_internal(&as);
+
/* If already same aspath exist then return it. */
find = hash_get(ashash, &as, aspath_hash_alloc);
@@ -1032,7 +1043,7 @@ static struct assegment *aspath_aggregate_as_set_add(struct aspath *aspath,
asset->as[asset->length - 1] = as;
}
-
+ aspath->count = aspath_count_hops_internal(aspath);
return asset;
}
@@ -1113,6 +1124,8 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2)
assegment_normalise(aspath->segments);
aspath_str_update(aspath, false);
+ aspath->count = aspath_count_hops_internal(aspath);
+
return aspath;
}
@@ -1268,6 +1281,7 @@ struct aspath *aspath_replace_regex_asn(struct aspath *aspath,
}
aspath_str_update(new, false);
+ new->count = aspath_count_hops_internal(new);
return new;
}
@@ -1293,6 +1307,8 @@ struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
}
aspath_str_update(new, false);
+ new->count = aspath_count_hops_internal(new);
+
return new;
}
@@ -1315,6 +1331,8 @@ struct aspath *aspath_replace_all_asn(struct aspath *aspath, as_t our_asn)
}
aspath_str_update(new, false);
+ new->count = aspath_count_hops_internal(new);
+
return new;
}
@@ -1341,6 +1359,8 @@ struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn,
}
aspath_str_update(new, false);
+ new->count = aspath_count_hops_internal(new);
+
return new;
}
@@ -1413,6 +1433,7 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath, as_t peer_asn)
if (!aspath->refcnt)
aspath_free(aspath);
aspath_str_update(new, false);
+ new->count = aspath_count_hops_internal(new);
return new;
}
@@ -1469,6 +1490,7 @@ static struct aspath *aspath_merge(struct aspath *as1, struct aspath *as2)
last->next = as2->segments;
as2->segments = new;
aspath_str_update(as2, false);
+ as2->count = aspath_count_hops_internal(as2);
return as2;
}
@@ -1486,6 +1508,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2)
if (as2->segments == NULL) {
as2->segments = assegment_dup_all(as1->segments);
aspath_str_update(as2, false);
+ as2->count = aspath_count_hops_internal(as2);
return as2;
}
@@ -1506,6 +1529,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2)
if (!as2->segments) {
as2->segments = assegment_dup_all(as1->segments);
aspath_str_update(as2, false);
+ as2->count = aspath_count_hops_internal(as2);
return as2;
}
@@ -1551,6 +1575,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2)
* the inbetween AS_SEQUENCE of seg2 in the process
*/
aspath_str_update(as2, false);
+ as2->count = aspath_count_hops_internal(as2);
return as2;
} else {
/* AS_SET merge code is needed at here. */
@@ -1662,6 +1687,7 @@ struct aspath *aspath_filter_exclude(struct aspath *source,
lastseg = newseg;
}
aspath_str_update(newpath, false);
+ newpath->count = aspath_count_hops_internal(newpath);
/* We are happy returning even an empty AS_PATH, because the
* administrator
* might expect this very behaviour. There's a mean to avoid this, if
@@ -1680,6 +1706,7 @@ struct aspath *aspath_filter_exclude_all(struct aspath *source)
newpath = aspath_new(source->asnotation);
aspath_str_update(newpath, false);
+ newpath->count = aspath_count_hops_internal(newpath);
/* We are happy returning even an empty AS_PATH, because the
* administrator
* might expect this very behaviour. There's a mean to avoid this, if
@@ -1767,6 +1794,7 @@ struct aspath *aspath_filter_exclude_acl(struct aspath *source,
aspath_str_update(source, false);
+ source->count = aspath_count_hops_internal(source);
/* We are happy returning even an empty AS_PATH, because the
* administrator
* might expect this very behaviour. There's a mean to avoid this, if
@@ -1805,6 +1833,7 @@ static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno,
}
aspath_str_update(aspath, false);
+ aspath->count = aspath_count_hops_internal(aspath);
return aspath;
}
@@ -1896,6 +1925,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath,
if (!hops) {
newpath = aspath_dup(as4path);
aspath_str_update(newpath, false);
+ /* dup sets the count properly */
return newpath;
}
@@ -1957,6 +1987,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath,
aspath_free(newpath);
mergedpath->segments = assegment_normalise(mergedpath->segments);
aspath_str_update(mergedpath, false);
+ mergedpath->count = aspath_count_hops_internal(mergedpath);
if (BGP_DEBUG(as4, AS4))
zlog_debug("[AS4] result of synthesizing is %s",
@@ -2027,8 +2058,10 @@ struct aspath *aspath_delete_confed_seq(struct aspath *aspath)
seg = next;
}
- if (removed_confed_segment)
+ if (removed_confed_segment) {
aspath_str_update(aspath, false);
+ aspath->count = aspath_count_hops_internal(aspath);
+ }
return aspath;
}
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index f7e57fd66d..46202fd34a 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -59,6 +59,7 @@ struct aspath {
and AS path regular expression match. */
char *str;
unsigned short str_len;
+ uint32_t count;
/* AS notation used by string expression of AS path */
enum asnotation_mode asnotation;
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 567af5bb75..b3518ac696 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -178,6 +178,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
EVENT_OFF(going_away->t_delayopen);
EVENT_OFF(going_away->t_connect_check_r);
EVENT_OFF(going_away->t_connect_check_w);
+ EVENT_OFF(going_away->t_stop_with_notify);
EVENT_OFF(keeper->t_routeadv);
EVENT_OFF(keeper->t_connect);
EVENT_OFF(keeper->t_delayopen);
@@ -1475,6 +1476,8 @@ enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection)
EVENT_OFF(connection->t_connect_check_r);
EVENT_OFF(connection->t_connect_check_w);
+ EVENT_OFF(connection->t_stop_with_notify);
+
/* Stop all timers. */
EVENT_OFF(connection->t_start);
EVENT_OFF(connection->t_connect);
@@ -3032,3 +3035,10 @@ void bgp_peer_gr_flags_update(struct peer *peer)
}
}
}
+
+void bgp_event_stop_with_notify(struct event *event)
+{
+ struct peer_connection *connection = EVENT_ARG(event);
+
+ bgp_stop_with_notify(connection, BGP_NOTIFY_SEND_HOLD_ERR, 0);
+}
diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
index 85c488962f..013c60ce23 100644
--- a/bgpd/bgp_fsm.h
+++ b/bgpd/bgp_fsm.h
@@ -109,6 +109,7 @@ enum bgp_fsm_state_progress {
extern void bgp_fsm_nht_update(struct peer_connection *connection,
struct peer *peer, bool has_valid_nexthops);
extern void bgp_event(struct event *event);
+extern void bgp_event_stop_with_notify(struct event *event);
extern int bgp_event_update(struct peer_connection *connection,
enum bgp_fsm_events event);
extern enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection);
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 6b116db107..0523a4b02b 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -122,42 +122,38 @@ static void bgp_packet_add(struct peer_connection *connection,
peer->last_sendq_ok = monotime(NULL);
stream_fifo_push(connection->obuf, s);
+ }
- delta = monotime(NULL) - peer->last_sendq_ok;
+ delta = monotime(NULL) - peer->last_sendq_ok;
- if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
- holdtime = atomic_load_explicit(&peer->holdtime,
- memory_order_relaxed);
- else
- holdtime = peer->bgp->default_holdtime;
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+ holdtime = atomic_load_explicit(&peer->holdtime, memory_order_relaxed);
+ else
+ holdtime = peer->bgp->default_holdtime;
- sendholdtime = holdtime * 2;
+ sendholdtime = holdtime * 2;
- /* Note that when we're here, we're adding some packet to the
- * OutQ. That includes keepalives when there is nothing to
- * do, so there's a guarantee we pass by here once in a while.
- *
- * That implies there is no need to go set up another separate
- * timer that ticks down SendHoldTime, as we'll be here sooner
- * or later anyway and will see the checks below failing.
- */
- if (!holdtime) {
- /* no holdtime, do nothing. */
- } else if (delta > sendholdtime) {
- flog_err(
- EC_BGP_SENDQ_STUCK_PROPER,
- "%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session",
- peer, sendholdtime);
- bgp_stop_with_notify(connection,
- BGP_NOTIFY_SEND_HOLD_ERR, 0);
- } else if (delta > (intmax_t)holdtime &&
- monotime(NULL) - peer->last_sendq_warn > 5) {
- flog_warn(
- EC_BGP_SENDQ_STUCK_WARN,
- "%pBP has not made any SendQ progress for 1 holdtime (%us), peer overloaded?",
- peer, holdtime);
- peer->last_sendq_warn = monotime(NULL);
- }
+ /* Note that when we're here, we're adding some packet to the
+ * OutQ. That includes keepalives when there is nothing to
+ * do, so there's a guarantee we pass by here once in a while.
+ *
+ * That implies there is no need to go set up another separate
+ * timer that ticks down SendHoldTime, as we'll be here sooner
+ * or later anyway and will see the checks below failing.
+ */
+ if (!holdtime) {
+ /* no holdtime, do nothing. */
+ } else if (delta > sendholdtime) {
+ flog_err(EC_BGP_SENDQ_STUCK_PROPER,
+ "%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session",
+ peer, sendholdtime);
+ event_add_event(bm->master, bgp_event_stop_with_notify, connection, 0,
+ &connection->t_stop_with_notify);
+ } else if (delta > (intmax_t)holdtime && monotime(NULL) - peer->last_sendq_warn > 5) {
+ flog_warn(EC_BGP_SENDQ_STUCK_WARN,
+ "%pBP has not made any SendQ progress for 1 holdtime (%us), peer overloaded?",
+ peer, holdtime);
+ peer->last_sendq_warn = monotime(NULL);
}
}
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 70da39ee8b..6fa1505998 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1133,9 +1133,9 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
/* 4. AS path length check. */
if (!CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE)) {
int exist_hops = aspath_count_hops(existattr->aspath);
- int exist_confeds = aspath_count_confeds(existattr->aspath);
if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED)) {
+ int exist_confeds = aspath_count_confeds(existattr->aspath);
int aspath_hops;
aspath_hops = aspath_count_hops(newattr->aspath);
@@ -4676,10 +4676,12 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
* will not be interned. In which case, it is ok to update the
* attr->evpn_overlay, so that, this can be stored in adj_in.
*/
- if ((afi == AFI_L2VPN) && evpn)
- bgp_attr_set_evpn_overlay(attr, evpn);
- else
- evpn_overlay_free(evpn);
+ if (evpn) {
+ if (afi == AFI_L2VPN)
+ bgp_attr_set_evpn_overlay(attr, evpn);
+ else
+ evpn_overlay_free(evpn);
+ }
bgp_adj_in_set(dest, peer, attr, addpath_id, &bgp_labels);
}
@@ -4855,10 +4857,12 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
* attr->evpn_overlay with evpn directly. Instead memcpy
* evpn to new_atr.evpn_overlay before it is interned.
*/
- if (soft_reconfig && (afi == AFI_L2VPN) && evpn)
- bgp_attr_set_evpn_overlay(&new_attr, evpn);
- else
- evpn_overlay_free(evpn);
+ if (soft_reconfig && evpn) {
+ if (afi == AFI_L2VPN)
+ bgp_attr_set_evpn_overlay(&new_attr, evpn);
+ else
+ evpn_overlay_free(evpn);
+ }
/* Apply incoming route-map.
* NB: new_attr may now contain newly allocated values from route-map
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 852efdf19d..5ffed544a5 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1223,6 +1223,8 @@ struct peer_connection {
struct event *t_process_packet;
struct event *t_process_packet_error;
+ struct event *t_stop_with_notify;
+
union sockunion su;
#define BGP_CONNECTION_SU_UNSPEC(connection) \
(connection->su.sa.sa_family == AF_UNSPEC)
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/log.c b/lib/log.c
index 04b789b5da..2b049cebe4 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -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/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/pimd/pim_autorp.c b/pimd/pim_autorp.c
index 3fc30347a6..d36b792e39 100644
--- a/pimd/pim_autorp.c
+++ b/pimd/pim_autorp.c
@@ -241,8 +241,11 @@ static bool pim_autorp_add_rp(struct pim_autorp *autorp, pim_addr rpaddr,
{
struct pim_autorp_rp *rp;
struct pim_autorp_rp *trp = NULL;
+ int ret;
- if (pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP)) {
+ ret = pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP);
+ /* There may not be a path to the RP right now, but that doesn't mean it failed to add the RP */
+ if (ret != PIM_SUCCESS && ret != PIM_RP_NO_PATH) {
zlog_err("%s: Failed to add new RP addr=%pI4, grp=%pFX, grplist=%s",
__func__, &rpaddr, &grp,
(listname ? listname : "NONE"));
diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c
index 627ccfee6f..a3acd0786f 100644
--- a/tests/isisd/test_fuzz_isis_tlv.c
+++ b/tests/isisd/test_fuzz_isis_tlv.c
@@ -22,7 +22,7 @@ static bool atexit_registered;
static void show_meminfo_at_exit(void)
{
- log_memstats(stderr, "isis fuzztest");
+ log_memstats(NULL, true);
}
static int comp_line(const void *p1, const void *p2)
diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c
index e5a8f7a513..9c1ce3d193 100644
--- a/tests/isisd/test_isis_spf.c
+++ b/tests/isisd/test_isis_spf.c
@@ -475,7 +475,7 @@ static void vty_do_exit(int isexit)
yang_terminate();
event_master_free(master);
- log_memstats(stderr, "test-isis-spf");
+ log_memstats(NULL, true);
if (!isexit)
exit(0);
}
diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c
index 342a91cc79..5c23a71258 100644
--- a/tests/lib/cli/common_cli.c
+++ b/tests/lib/cli/common_cli.c
@@ -43,7 +43,7 @@ static void vty_do_exit(int isexit)
yang_terminate();
event_master_free(master);
- log_memstats(stderr, "testcli");
+ log_memstats(NULL, true);
if (!isexit)
exit(0);
}
diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c
index fdc9e53ca3..3d700d8a19 100644
--- a/tests/lib/northbound/test_oper_data.c
+++ b/tests/lib/northbound/test_oper_data.c
@@ -427,7 +427,7 @@ static void vty_do_exit(int isexit)
yang_terminate();
event_master_free(master);
- log_memstats(stderr, "test-nb-oper-data");
+ log_memstats(NULL, true);
if (!isexit)
exit(0);
}
diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c
index 070a304335..3ce6683ae5 100644
--- a/tests/lib/test_typelist.c
+++ b/tests/lib/test_typelist.c
@@ -156,6 +156,6 @@ int main(int argc, char **argv)
test_ATOMSORT_UNIQ();
test_ATOMSORT_NONUNIQ();
- log_memstats_stderr("test: ");
+ log_memstats(NULL, true);
return 0;
}
diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c
index 2cd9d47cb4..5cb518d81a 100644
--- a/tests/lib/test_zmq.c
+++ b/tests/lib/test_zmq.c
@@ -285,7 +285,7 @@ static void run_server(int syncfd)
zmq_close(zmqsock);
frrzmq_finish();
event_master_free(master);
- log_memstats_stderr("test");
+ log_memstats(NULL, true);
}
int main(void)
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 2148d131ec..0c607dfa67 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -932,7 +932,7 @@ static int netlink_recv_msg(struct nlsock *nl, struct msghdr *msg)
} while (status == -1 && errno == EINTR);
if (status == -1) {
- if (errno == EWOULDBLOCK || errno == EAGAIN)
+ if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EMSGSIZE)
return 0;
flog_err(EC_ZEBRA_RECVMSG_OVERRUN, "%s recvmsg overrun: %s",
nl->name, safe_strerror(errno));