diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:18 +0000 |
commit | 5da14042f70711ea5cf66e034699730335462f66 (patch) | |
tree | 0f6354ccac934ed87a2d555f45be4c831cf92f4a /src/collectors/proc.plugin/proc_net_dev.c | |
parent | Releasing debian version 1.44.3-2. (diff) | |
download | netdata-5da14042f70711ea5cf66e034699730335462f66.tar.xz netdata-5da14042f70711ea5cf66e034699730335462f66.zip |
Merging upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/collectors/proc.plugin/proc_net_dev.c (renamed from collectors/proc.plugin/proc_net_dev.c) | 386 |
1 files changed, 109 insertions, 277 deletions
diff --git a/collectors/proc.plugin/proc_net_dev.c b/src/collectors/proc.plugin/proc_net_dev.c index b39f39683..d29bb7a72 100644 --- a/collectors/proc.plugin/proc_net_dev.c +++ b/src/collectors/proc.plugin/proc_net_dev.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "plugin_proc.h" +#include "proc_net_dev_renames.h" #define PLUGIN_PROC_MODULE_NETDEV_NAME "/proc/net/dev" #define CONFIG_SECTION_PLUGIN_PROC_NETDEV "plugin:" PLUGIN_PROC_CONFIG_NAME ":" PLUGIN_PROC_MODULE_NETDEV_NAME @@ -11,10 +12,7 @@ #define READ_RETRY_PERIOD 60 // seconds -void cgroup_netdev_reset_all(void); -void cgroup_netdev_release(const DICTIONARY_ITEM *link); -const void *cgroup_netdev_dup(const DICTIONARY_ITEM *link); -void cgroup_netdev_add_bandwidth(const DICTIONARY_ITEM *link, NETDATA_DOUBLE received, NETDATA_DOUBLE sent); +time_t double_linked_device_collect_delay_secs = 120; enum { NETDEV_DUPLEX_UNKNOWN, @@ -22,8 +20,7 @@ enum { NETDEV_DUPLEX_FULL }; -static const char *get_duplex_string(int duplex) -{ +static const char *get_duplex_string(int duplex) { switch (duplex) { case NETDEV_DUPLEX_FULL: return "full"; @@ -44,8 +41,7 @@ enum { NETDEV_OPERSTATE_UP }; -static inline int get_operstate(char *operstate) -{ +static inline int get_operstate(char *operstate) { // As defined in https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-net if (!strcmp(operstate, "up")) return NETDEV_OPERSTATE_UP; @@ -63,8 +59,7 @@ static inline int get_operstate(char *operstate) return NETDEV_OPERSTATE_UNKNOWN; } -static const char *get_operstate_string(int operstate) -{ +static const char *get_operstate_string(int operstate) { switch (operstate) { case NETDEV_OPERSTATE_UP: return "up"; @@ -92,12 +87,12 @@ static struct netdev { size_t len; // flags - int virtual; - int configured; + bool virtual; + bool configured; int enabled; - int updated; - + bool updated; bool function_ready; + bool double_linked; // iflink != ifindex time_t discover_time; @@ -242,7 +237,7 @@ static struct netdev { RRDDIM *rd_mtu; char *filename_speed; - const RRDSETVAR_ACQUIRED *chart_var_speed; + const RRDVAR_ACQUIRED *chart_var_speed; char *filename_duplex; char *filename_operstate; @@ -251,10 +246,8 @@ static struct netdev { const DICTIONARY_ITEM *cgroup_netdev_link; - struct netdev *next; -} *netdev_root = NULL, *netdev_last_used = NULL; - -static size_t netdev_added = 0, netdev_found = 0; + struct netdev *prev, *next; +} *netdev_root = NULL; // ---------------------------------------------------------------------------- @@ -378,133 +371,21 @@ static void netdev_free(struct netdev *d) { freez((void *)d->filename_carrier); freez((void *)d->filename_mtu); freez((void *)d); - netdev_added--; } -// ---------------------------------------------------------------------------- -// netdev renames - -static struct netdev_rename { - const char *host_device; - uint32_t hash; - - const char *container_device; - const char *container_name; - const char *ctx_prefix; - - RRDLABELS *chart_labels; - - int processed; - - const DICTIONARY_ITEM *cgroup_netdev_link; - - struct netdev_rename *next; -} *netdev_rename_root = NULL; - -static int netdev_pending_renames = 0; -static netdata_mutex_t netdev_rename_mutex = NETDATA_MUTEX_INITIALIZER; -static netdata_mutex_t netdev_dev_mutex = NETDATA_MUTEX_INITIALIZER; - -static struct netdev_rename *netdev_rename_find(const char *host_device, uint32_t hash) { - struct netdev_rename *r; +static netdata_mutex_t netdev_mutex = NETDATA_MUTEX_INITIALIZER; - for(r = netdev_rename_root; r ; r = r->next) - if(r->hash == hash && !strcmp(host_device, r->host_device)) - return r; - - return NULL; -} - -// other threads can call this function to register a rename to a netdev -void netdev_rename_device_add( - const char *host_device, - const char *container_device, - const char *container_name, - RRDLABELS *labels, - const char *ctx_prefix, - const DICTIONARY_ITEM *cgroup_netdev_link) -{ - netdata_mutex_lock(&netdev_rename_mutex); - - uint32_t hash = simple_hash(host_device); - struct netdev_rename *r = netdev_rename_find(host_device, hash); - if(!r) { - r = callocz(1, sizeof(struct netdev_rename)); - r->host_device = strdupz(host_device); - r->container_device = strdupz(container_device); - r->container_name = strdupz(container_name); - r->ctx_prefix = strdupz(ctx_prefix); - r->chart_labels = rrdlabels_create(); - rrdlabels_migrate_to_these(r->chart_labels, labels); - r->hash = hash; - r->next = netdev_rename_root; - r->processed = 0; - r->cgroup_netdev_link = cgroup_netdev_link; - - netdev_rename_root = r; - netdev_pending_renames++; - collector_info("CGROUP: registered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name); - } - else { - if(strcmp(r->container_device, container_device) != 0 || strcmp(r->container_name, container_name) != 0) { - freez((void *) r->container_device); - freez((void *) r->container_name); - - r->container_device = strdupz(container_device); - r->container_name = strdupz(container_name); - - rrdlabels_migrate_to_these(r->chart_labels, labels); - - r->processed = 0; - r->cgroup_netdev_link = cgroup_netdev_link; - - netdev_pending_renames++; - collector_info("CGROUP: altered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name); - } - } - - netdata_mutex_unlock(&netdev_rename_mutex); -} - -// other threads can call this function to delete a rename to a netdev -void netdev_rename_device_del(const char *host_device) { - netdata_mutex_lock(&netdev_rename_mutex); - - struct netdev_rename *r, *last = NULL; - - uint32_t hash = simple_hash(host_device); - for(r = netdev_rename_root; r ; last = r, r = r->next) { - if (r->hash == hash && !strcmp(host_device, r->host_device)) { - if (netdev_rename_root == r) - netdev_rename_root = r->next; - else if (last) - last->next = r->next; - - if(!r->processed) - netdev_pending_renames--; - - collector_info("CGROUP: unregistered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name); - - freez((void *) r->host_device); - freez((void *) r->container_name); - freez((void *) r->container_device); - freez((void *) r->ctx_prefix); - rrdlabels_destroy(r->chart_labels); - cgroup_netdev_release(r->cgroup_netdev_link); - freez((void *) r); - break; - } - } - - netdata_mutex_unlock(&netdev_rename_mutex); -} +// ---------------------------------------------------------------------------- -static inline void netdev_rename_cgroup(struct netdev *d, struct netdev_rename *r) { - collector_info("CGROUP: renaming network interface '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name); +static inline void netdev_rename(struct netdev *d, struct rename_task *r) { + collector_info("CGROUP: renaming network interface '%s' as '%s' under '%s'", d->name, r->container_device, r->container_name); netdev_charts_release(d); netdev_free_chart_strings(d); + + cgroup_netdev_release(d->cgroup_netdev_link); d->cgroup_netdev_link = cgroup_netdev_dup(r->cgroup_netdev_link); + d->discover_time = 0; char buffer[RRD_ID_LENGTH_MAX + 1]; @@ -581,42 +462,18 @@ static inline void netdev_rename_cgroup(struct netdev *d, struct netdev_rename * d->flipped = 1; } -static inline void netdev_rename(struct netdev *d) { - struct netdev_rename *r = netdev_rename_find(d->name, d->hash); - if(unlikely(r && !r->processed)) { - netdev_rename_cgroup(d, r); - r->processed = 1; - d->discover_time = 0; - netdev_pending_renames--; +static void netdev_rename_this_device(struct netdev *d) { + const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(netdev_renames, d->name); + if(item) { + struct rename_task *r = dictionary_acquired_item_value(item); + netdev_rename(d, r); + dictionary_acquired_item_release(netdev_renames, item); } } -static inline void netdev_rename_lock(struct netdev *d) { - netdata_mutex_lock(&netdev_rename_mutex); - netdev_rename(d); - netdata_mutex_unlock(&netdev_rename_mutex); -} - -static inline void netdev_rename_all_lock(void) { - netdata_mutex_lock(&netdev_rename_mutex); - - struct netdev *d; - for(d = netdev_root; d ; d = d->next) - netdev_rename(d); - - netdev_pending_renames = 0; - netdata_mutex_unlock(&netdev_rename_mutex); -} - // ---------------------------------------------------------------------------- -int netdev_function_net_interfaces(BUFFER *wb, int timeout __maybe_unused, const char *function __maybe_unused, - void *collector_data __maybe_unused, - rrd_function_result_callback_t result_cb, void *result_cb_data, - rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data, - rrd_function_register_canceller_cb_t register_canceller_cb __maybe_unused, - void *register_canceller_cb_data __maybe_unused) { - +int netdev_function_net_interfaces(BUFFER *wb, const char *function __maybe_unused) { buffer_flush(wb); wb->content_type = CT_APPLICATION_JSON; buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT); @@ -625,6 +482,7 @@ int netdev_function_net_interfaces(BUFFER *wb, int timeout __maybe_unused, const buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK); buffer_json_member_add_string(wb, "type", "table"); buffer_json_member_add_time_t(wb, "update_every", 1); + buffer_json_member_add_boolean(wb, "has_history", false); buffer_json_member_add_string(wb, "help", RRDFUNCTIONS_NETDEV_HELP); buffer_json_member_add_array(wb, "data"); @@ -637,11 +495,11 @@ int netdev_function_net_interfaces(BUFFER *wb, int timeout __maybe_unused, const double max_drops_rx = 0.0; double max_drops_tx = 0.0; - netdata_mutex_lock(&netdev_dev_mutex); + netdata_mutex_lock(&netdev_mutex); RRDDIM *rd = NULL; - for (struct netdev *d = netdev_root; d != netdev_last_used; d = d->next) { + for (struct netdev *d = netdev_root; d ; d = d->next) { if (unlikely(!d->function_ready)) continue; @@ -701,7 +559,7 @@ int netdev_function_net_interfaces(BUFFER *wb, int timeout __maybe_unused, const buffer_json_array_close(wb); } - netdata_mutex_unlock(&netdev_dev_mutex); + netdata_mutex_unlock(&netdev_mutex); buffer_json_array_close(wb); // data buffer_json_member_add_object(wb, "columns"); @@ -910,49 +768,26 @@ int netdev_function_net_interfaces(BUFFER *wb, int timeout __maybe_unused, const buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1); buffer_json_finalize(wb); - int response = HTTP_RESP_OK; - if(is_cancelled_cb && is_cancelled_cb(is_cancelled_cb_data)) { - buffer_flush(wb); - response = HTTP_RESP_CLIENT_CLOSED_REQUEST; - } - - if(result_cb) - result_cb(wb, response, result_cb_data); - - return response; + return HTTP_RESP_OK; } // netdev data collection static void netdev_cleanup() { - if(likely(netdev_found == netdev_added)) return; - - netdev_added = 0; - struct netdev *d = netdev_root, *last = NULL; + struct netdev *d = netdev_root; while(d) { if(unlikely(!d->updated)) { - // collector_info("Removing network device '%s', linked after '%s'", d->name, last?last->name:"ROOT"); - - if(netdev_last_used == d) - netdev_last_used = last; - - struct netdev *t = d; + struct netdev *next = d->next; // keep the next, to continue; - if(d == netdev_root || !last) - netdev_root = d = d->next; - - else - last->next = d = d->next; + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(netdev_root, d, prev, next); - t->next = NULL; - netdev_free(t); - } - else { - netdev_added++; - last = d; - d->updated = 0; - d = d->next; + netdev_free(d); + d = next; + continue; } + + d->updated = false; + d = d->next; } } @@ -962,19 +797,9 @@ static struct netdev *get_netdev(const char *name) { uint32_t hash = simple_hash(name); // search it, from the last position to the end - for(d = netdev_last_used ; d ; d = d->next) { - if(unlikely(hash == d->hash && !strcmp(name, d->name))) { - netdev_last_used = d->next; - return d; - } - } - - // search it from the beginning to the last position we used - for(d = netdev_root ; d != netdev_last_used ; d = d->next) { - if(unlikely(hash == d->hash && !strcmp(name, d->name))) { - netdev_last_used = d->next; + for(d = netdev_root ; d ; d = d->next) { + if(unlikely(hash == d->hash && !strcmp(name, d->name))) return d; - } } // create a new one @@ -984,6 +809,7 @@ static struct netdev *get_netdev(const char *name) { d->len = strlen(d->name); d->chart_labels = rrdlabels_create(); d->function_ready = false; + d->double_linked = false; d->chart_type_net_bytes = strdupz("net"); d->chart_type_net_compressed = strdupz("net_compressed"); @@ -1027,23 +853,26 @@ static struct netdev *get_netdev(const char *name) { d->chart_family = strdupz(d->name); d->priority = NETDATA_CHART_PRIO_FIRST_NET_IFACE; - netdev_rename_lock(d); - - netdev_added++; - - // link it to the end - if(netdev_root) { - struct netdev *e; - for(e = netdev_root; e->next ; e = e->next) ; - e->next = d; - } - else - netdev_root = d; + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(netdev_root, d, prev, next); return d; } -#define NETDEV_VIRTUAL_COLLECT_DELAY 15 // 1 full run of the cgroups discovery thread (10 secs by default) +static bool is_iface_double_linked(struct netdev *d) { + char filename[FILENAME_MAX + 1]; + unsigned long long iflink = 0; + unsigned long long ifindex = 0; + + snprintfz(filename, FILENAME_MAX, "%s/sys/class/net/%s/iflink", netdata_configured_host_prefix, d->name); + if (read_single_number_file(filename, &iflink)) + return false; + + snprintfz(filename, FILENAME_MAX, "%s/sys/class/net/%s/ifindex", netdata_configured_host_prefix, d->name); + if (read_single_number_file(filename, &ifindex)) + return false; + + return iflink != ifindex; +} int do_proc_net_dev(int update_every, usec_t dt) { (void)dt; @@ -1102,6 +931,8 @@ int do_proc_net_dev(int update_every, usec_t dt) { disabled_list = simple_pattern_create( config_get(CONFIG_SECTION_PLUGIN_PROC_NETDEV, "disable by default interfaces matching", "lo fireqos* *-ifb fwpr* fwbr* fwln*"), NULL, SIMPLE_PATTERN_EXACT, true); + + netdev_renames_init(); } if(unlikely(!ff)) { @@ -1112,12 +943,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { ff = procfile_readall(ff); if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - // rename all the devices, if we have pending renames - if(unlikely(netdev_pending_renames)) - netdev_rename_all_lock(); - - netdev_found = 0; - kernel_uint_t system_rbytes = 0; kernel_uint_t system_tbytes = 0; @@ -1133,14 +958,13 @@ int do_proc_net_dev(int update_every, usec_t dt) { if(name[len - 1] == ':') name[len - 1] = '\0'; struct netdev *d = get_netdev(name); - d->updated = 1; - netdev_found++; + d->updated = true; if(unlikely(!d->configured)) { - // this is the first time we see this interface + // the first time we see this interface // remember we configured it - d->configured = 1; + d->configured = true; d->discover_time = now; d->enabled = enable_new_interfaces; @@ -1151,12 +975,12 @@ int do_proc_net_dev(int update_every, usec_t dt) { char buf[FILENAME_MAX + 1]; snprintfz(buf, FILENAME_MAX, path_to_sys_devices_virtual_net, d->name); - d->virtual = likely(access(buf, R_OK) == 0) ? 1 : 0; + d->virtual = likely(access(buf, R_OK) == 0) ? true : false; // At least on Proxmox inside LXC: eth0 is virtual. // Virtual interfaces are not taken into account in system.net calculations if (inside_lxc_container && d->virtual && strncmp(d->name, "eth", 3) == 0) - d->virtual = 0; + d->virtual = false; if (d->virtual) rrdlabels_add(d->chart_labels, "interface_type", "virtual", RRDLABEL_SRC_AUTO); @@ -1193,6 +1017,8 @@ int do_proc_net_dev(int update_every, usec_t dt) { if(d->enabled == CONFIG_BOOLEAN_NO) continue; + d->double_linked = is_iface_double_linked(d); + d->do_bandwidth = do_bandwidth; d->do_packets = do_packets; d->do_errors = do_errors; @@ -1235,13 +1061,15 @@ int do_proc_net_dev(int update_every, usec_t dt) { if(unlikely(!d->enabled)) continue; + if(!d->cgroup_netdev_link) + netdev_rename_this_device(d); + // See https://github.com/netdata/netdata/issues/15206 // This is necessary to prevent the creation of charts for virtual interfaces that will later be // recreated as container interfaces (create container) or // rediscovered and recreated only to be deleted almost immediately (stop/remove container) - if (d->virtual && (now - d->discover_time < NETDEV_VIRTUAL_COLLECT_DELAY)) { + if (d->double_linked && d->virtual && (now - d->discover_time < double_linked_device_collect_delay_secs)) continue; - } if(likely(d->do_bandwidth != CONFIG_BOOLEAN_NO || !d->virtual)) { d->rbytes = str2kernel_uint_t(procfile_lineword(ff, l, 1)); @@ -1313,7 +1141,7 @@ int do_proc_net_dev(int update_every, usec_t dt) { now_monotonic_sec() - d->duplex_file_lost_time > READ_RETRY_PERIOD)) { char buffer[STATE_LENGTH_MAX + 1]; - if (read_file(d->filename_duplex, buffer, STATE_LENGTH_MAX)) { + if (read_txt_file(d->filename_duplex, buffer, sizeof(buffer))) { if (d->duplex_file_exists) collector_error("Cannot refresh interface %s duplex state by reading '%s'.", d->name, d->filename_duplex); d->duplex_file_exists = 0; @@ -1337,7 +1165,7 @@ int do_proc_net_dev(int update_every, usec_t dt) { if(d->do_operstate != CONFIG_BOOLEAN_NO && d->filename_operstate) { char buffer[STATE_LENGTH_MAX + 1], *trimmed_buffer; - if (read_file(d->filename_operstate, buffer, STATE_LENGTH_MAX)) { + if (read_txt_file(d->filename_operstate, buffer, sizeof(buffer))) { collector_error( "Cannot refresh %s operstate by reading '%s'. Will not update its status anymore.", d->name, d->filename_operstate); @@ -1414,20 +1242,20 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->flipped ? d->rd_tbytes->collector.last_stored_value : -d->rd_rbytes->collector.last_stored_value, d->flipped ? -d->rd_rbytes->collector.last_stored_value : d->rd_tbytes->collector.last_stored_value); - // update the interface speed - if(d->filename_speed) { - if(unlikely(!d->chart_var_speed)) { - d->chart_var_speed = - rrdsetvar_custom_chart_variable_add_and_acquire(d->st_bandwidth, "nic_speed_max"); - if(!d->chart_var_speed) { - collector_error( - "Cannot create interface %s chart variable 'nic_speed_max'. Will not update its speed anymore.", - d->name); - freez(d->filename_speed); - d->filename_speed = NULL; - } + if(unlikely(!d->chart_var_speed)) { + d->chart_var_speed = rrdvar_chart_variable_add_and_acquire(d->st_bandwidth, "nic_speed_max"); + if(!d->chart_var_speed) { + collector_error( + "Cannot create interface %s chart variable 'nic_speed_max'. Will not update its speed anymore.", + d->name); + } + else { + rrdvar_chart_variable_set(d->st_bandwidth, d->chart_var_speed, NAN); } + } + // update the interface speed + if(d->filename_speed) { if (d->filename_speed && d->chart_var_speed) { int ret = 0; @@ -1473,7 +1301,7 @@ int do_proc_net_dev(int update_every, usec_t dt) { rrdset_done(d->st_speed); } - rrdsetvar_custom_chart_variable_set( + rrdvar_chart_variable_set( d->st_bandwidth, d->chart_var_speed, (NETDATA_DOUBLE)d->speed * KILOBITS_IN_A_MEGABIT); if (d->speed) { @@ -1924,32 +1752,36 @@ void *netdev_main(void *ptr) worker_register("NETDEV"); worker_register_job_name(0, "netdev"); - netdata_thread_cleanup_push(netdev_main_cleanup, ptr); + if (getenv("KUBERNETES_SERVICE_HOST") != NULL && getenv("KUBERNETES_SERVICE_PORT") != NULL) + double_linked_device_collect_delay_secs = 300; - rrd_collector_started(); - rrd_function_add(localhost, NULL, "network-interfaces", 10, RRDFUNCTIONS_NETDEV_HELP, true, netdev_function_net_interfaces, NULL); + rrd_function_add_inline(localhost, NULL, "network-interfaces", 10, + RRDFUNCTIONS_PRIORITY_DEFAULT, RRDFUNCTIONS_NETDEV_HELP, + "top", HTTP_ACCESS_ANONYMOUS_DATA, + netdev_function_net_interfaces); - usec_t step = localhost->rrd_update_every * USEC_PER_SEC; - heartbeat_t hb; - heartbeat_init(&hb); + netdata_thread_cleanup_push(netdev_main_cleanup, ptr) { + usec_t step = localhost->rrd_update_every * USEC_PER_SEC; + heartbeat_t hb; + heartbeat_init(&hb); - while (service_running(SERVICE_COLLECTORS)) { - worker_is_idle(); - usec_t hb_dt = heartbeat_next(&hb, step); + while (service_running(SERVICE_COLLECTORS)) { + worker_is_idle(); + usec_t hb_dt = heartbeat_next(&hb, step); - if (unlikely(!service_running(SERVICE_COLLECTORS))) - break; + if (unlikely(!service_running(SERVICE_COLLECTORS))) + break; - cgroup_netdev_reset_all(); + cgroup_netdev_reset_all(); - worker_is_busy(0); + worker_is_busy(0); - netdata_mutex_lock(&netdev_dev_mutex); - if(do_proc_net_dev(localhost->rrd_update_every, hb_dt)) - break; - netdata_mutex_unlock(&netdev_dev_mutex); + netdata_mutex_lock(&netdev_mutex); + if (do_proc_net_dev(localhost->rrd_update_every, hb_dt)) + break; + netdata_mutex_unlock(&netdev_mutex); + } } - netdata_thread_cleanup_pop(1); return NULL; |