@cryptotaxi247 / netdata-1 / commits / 77a0551f9

eBPF bug fixes (#14869)

thiagoftsm committed Apr 11, 2023 at 12:01 UTC 77a0551f92c3a7b921fc276a5c39de4badd2cecd
10 files changed +172 -53
collectors/ebpf.plugin/ebpf.c
+2 -1
@@ -1820,8 +1820,9 @@ void set_global_variables()
1820 ebpf_configured_log_dir = LOG_DIR;
1821
1822 ebpf_nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN);
1823 - if (ebpf_nprocs > NETDATA_MAX_PROCESSOR) {
1823 + if (ebpf_nprocs < 0) {
1824 ebpf_nprocs = NETDATA_MAX_PROCESSOR;
1825 + error("Cannot identify number of process, using default value %d", ebpf_nprocs);
1826 }
1827
1828 isrh = get_redhat_release();
collectors/ebpf.plugin/ebpf_hardirq.c
+145 -34
@@ -129,11 +129,54 @@ static hardirq_static_val_t hardirq_static_vals[] = {
129 // thread will write to netdata agent.
130 static avl_tree_lock hardirq_pub;
131
132 -// tmp store for dynamic hard IRQ values we get from a per-CPU eBPF map.
133 -static hardirq_ebpf_val_t *hardirq_ebpf_vals = NULL;
132 +/*****************************************************************
133 + *
134 + * ARAL SECTION
135 + *
136 + *****************************************************************/
137 +
138 +// ARAL vectors used to speed up processing
139 +ARAL *ebpf_aral_hardirq = NULL;
140
135 -// tmp store for static hard IRQ values we get from a per-CPU eBPF map.
136 -static hardirq_ebpf_static_val_t *hardirq_ebpf_static_vals = NULL;
141 +/**
142 + * eBPF hardirq Aral init
143 + *
144 + * Initiallize array allocator that will be used when integration with apps is enabled.
145 + */
146 +static inline void ebpf_hardirq_aral_init()
147 +{
148 + ebpf_aral_hardirq = ebpf_allocate_pid_aral(NETDATA_EBPF_HARDIRQ_ARAL_NAME, sizeof(hardirq_val_t));
149 +}
150 +
151 +/**
152 + * eBPF hardirq get
153 + *
154 + * Get a hardirq_val_t entry to be used with a specific IRQ.
155 + *
156 + * @return it returns the address on success.
157 + */
158 +hardirq_val_t *ebpf_hardirq_get(void)
159 +{
160 + hardirq_val_t *target = aral_mallocz(ebpf_aral_hardirq);
161 + memset(target, 0, sizeof(hardirq_val_t));
162 + return target;
163 +}
164 +
165 +/**
166 + * eBPF hardirq release
167 + *
168 + * @param stat Release a target after usage.
169 + */
170 +void ebpf_hardirq_release(hardirq_val_t *stat)
171 +{
172 + aral_freez(ebpf_aral_hardirq, stat);
173 +}
174 +
175 +/*****************************************************************
176 + *
177 + * EXIT FUNCTIONS
178 + *
179 + *****************************************************************/
180
181 /**
182 * Hardirq Free
@@ -151,9 +194,6 @@ static void ebpf_hardirq_free(ebpf_module_t *em)
194 for (int i = 0; hardirq_tracepoints[i].class != NULL; i++) {
195 ebpf_disable_tracepoint(&hardirq_tracepoints[i]);
196 }
154 - freez(hardirq_ebpf_vals);
155 - freez(hardirq_ebpf_static_vals);
156 -
197 pthread_mutex_lock(&ebpf_exit_cleanup);
198 em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED;
199 pthread_mutex_unlock(&ebpf_exit_cleanup);
@@ -200,8 +240,82 @@ static int hardirq_val_cmp(void *a, void *b)
240 }
241 }
242
203 -static void hardirq_read_latency_map(int mapfd)
243 +/**
244 + * Parse interrupts
245 + *
246 + * Parse /proc/interrupts to get names used in metrics
247 + *
248 + * @param irq_name vector to store data.
249 + * @param irq irq value
250 + *
251 + * @return It returns 0 on success and -1 otherwise
252 + */
253 +static int hardirq_parse_interrupts(char *irq_name, int irq)
254 {
255 + static procfile *ff = NULL;
256 + static int cpus = -1;
257 + if(unlikely(!ff)) {
258 + char filename[FILENAME_MAX + 1];
259 + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/interrupts");
260 + ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
261 + }
262 + if(unlikely(!ff))
263 + return -1;
264 +
265 + ff = procfile_readall(ff);
266 + if(unlikely(!ff))
267 + return -1; // we return 0, so that we will retry to open it next time
268 +
269 + size_t words = procfile_linewords(ff, 0);
270 + if(unlikely(cpus == -1)) {
271 + uint32_t w;
272 + cpus = 0;
273 + for(w = 0; w < words ; w++) {
274 + if(likely(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0))
275 + cpus++;
276 + }
277 + }
278 +
279 + size_t lines = procfile_lines(ff), l;
280 + if(unlikely(!lines)) {
281 + collector_error("Cannot read /proc/interrupts, zero lines reported.");
282 + return -1;
283 + }
284 +
285 + for(l = 1; l < lines ;l++) {
286 + words = procfile_linewords(ff, l);
287 + if(unlikely(!words)) continue;
288 + const char *id = procfile_lineword(ff, l, 0);
289 + if (!isdigit(id[0]))
290 + continue;
291 +
292 + int cmp = str2i(id);
293 + if (cmp != irq)
294 + continue;
295 +
296 + if(unlikely((uint32_t)(cpus + 2) < words)) {
297 + const char *name = procfile_lineword(ff, l, words - 1);
298 + // On some motherboards IRQ can have the same name, so we append IRQ id to differentiate.
299 + snprintfz(irq_name, NETDATA_HARDIRQ_NAME_LEN - 1, "%d_%s", irq, name);
300 + }
301 + }
302 +
303 + return 0;
304 +}
305 +
306 +/**
307 + * Read Latency MAP
308 + *
309 + * Read data from kernel ring to user ring.
310 + *
311 + * @param mapfd hash map id.
312 + *
313 + * @return it returns 0 on success and -1 otherwise
314 + */
315 +static int hardirq_read_latency_map(int mapfd)
316 +{
317 + hardirq_ebpf_static_val_t hardirq_ebpf_vals[ebpf_nprocs + 1];
318 +
319 hardirq_ebpf_key_t key = {};
320 hardirq_ebpf_key_t next_key = {};
321 hardirq_val_t search_v = {};
@@ -234,7 +348,7 @@ static void hardirq_read_latency_map(int mapfd)
348 if (unlikely(v == NULL)) {
349 // latency/name can only be added reliably at a later time.
350 // when they're added, only then will we AVL insert.
237 - v = callocz(1, sizeof(hardirq_val_t));
351 + v = ebpf_hardirq_get();
352 v->irq = key.irq;
353 v->dim_exists = false;
354
@@ -246,22 +360,10 @@ static void hardirq_read_latency_map(int mapfd)
360 // 2. the name is unfortunately *not* available on all CPU maps - only
361 // a single map contains the name, so we must find it. we only need
362 // to copy it though if the IRQ is new for us.
249 - bool name_saved = false;
363 uint64_t total_latency = 0;
364 int i;
252 - int end = (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs;
253 - for (i = 0; i < end; i++) {
365 + for (i = 0; i < ebpf_nprocs; i++) {
366 total_latency += hardirq_ebpf_vals[i].latency/1000;
255 -
256 - // copy name for new IRQs.
257 - if (v_is_new && !name_saved && hardirq_ebpf_vals[i].name[0] != '\0') {
258 - strncpyz(
259 - v->name,
260 - hardirq_ebpf_vals[i].name,
261 - NETDATA_HARDIRQ_NAME_LEN
262 - );
263 - name_saved = true;
264 - }
367 }
368
369 // can now safely publish latency for existing IRQs.
@@ -269,6 +371,11 @@ static void hardirq_read_latency_map(int mapfd)
371
372 // can now safely publish new IRQ.
373 if (v_is_new) {
374 + if (hardirq_parse_interrupts(v->name, v->irq)) {
375 + ebpf_hardirq_release(v);
376 + return -1;
377 + }
378 +
379 avl_t *check = avl_insert_lock(&hardirq_pub, (avl_t *)v);
380 if (check != (avl_t *)v) {
381 error("Internal error, cannot insert the AVL tree.");
@@ -277,10 +384,14 @@ static void hardirq_read_latency_map(int mapfd)
384
385 key = next_key;
386 }
387 +
388 + return 0;
389 }
390
391 static void hardirq_read_latency_static_map(int mapfd)
392 {
393 + hardirq_ebpf_static_val_t hardirq_ebpf_static_vals[ebpf_nprocs + 1];
394 +
395 uint32_t i;
396 for (i = 0; i < HARDIRQ_EBPF_STATIC_END; i++) {
397 uint32_t map_i = hardirq_static_vals[i].idx;
@@ -302,11 +413,17 @@ static void hardirq_read_latency_static_map(int mapfd)
413
414 /**
415 * Read eBPF maps for hard IRQ.
416 + *
417 + * @return When it is not possible to parse /proc, it returns -1, on success it returns 0;
418 */
306 -static void hardirq_reader()
419 +static int hardirq_reader()
420 {
308 - hardirq_read_latency_map(hardirq_maps[HARDIRQ_MAP_LATENCY].map_fd);
421 + if (hardirq_read_latency_map(hardirq_maps[HARDIRQ_MAP_LATENCY].map_fd))
422 + return -1;
423 +
424 hardirq_read_latency_static_map(hardirq_maps[HARDIRQ_MAP_LATENCY_STATIC].map_fd);
425 +
426 + return 0;
427 }
428
429 static void hardirq_create_charts(int update_every)
@@ -375,16 +492,8 @@ static inline void hardirq_write_static_dims()
492 */
493 static void hardirq_collector(ebpf_module_t *em)
494 {
378 - hardirq_ebpf_vals = callocz(
379 - (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs,
380 - sizeof(hardirq_ebpf_val_t)
381 - );
382 - hardirq_ebpf_static_vals = callocz(
383 - (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs,
384 - sizeof(hardirq_ebpf_static_val_t)
385 - );
386 -
495 avl_init_lock(&hardirq_pub, hardirq_val_cmp);
496 + ebpf_hardirq_aral_init();
497
498 // create chart and static dims.
499 pthread_mutex_lock(&lock);
@@ -407,7 +516,9 @@ static void hardirq_collector(ebpf_module_t *em)
516 continue;
517
518 counter = 0;
410 - hardirq_reader();
519 + if (hardirq_reader())
520 + break;
521 +
522 pthread_mutex_lock(&lock);
523
524 // write dims now for all hitherto discovered IRQs.
collectors/ebpf.plugin/ebpf_hardirq.h
+6 -6
@@ -3,6 +3,9 @@
3 #ifndef NETDATA_EBPF_HARDIRQ_H
4 #define NETDATA_EBPF_HARDIRQ_H 1
5
6 +#include <stdint.h>
7 +#include "libnetdata/avl/avl.h"
8 +
9 /*****************************************************************
10 * copied from kernel-collectors repo, with modifications needed
11 * for inclusion here.
@@ -15,12 +18,6 @@ typedef struct hardirq_ebpf_key {
18 int irq;
19 } hardirq_ebpf_key_t;
20
18 -typedef struct hardirq_ebpf_val {
19 - uint64_t latency;
20 - uint64_t ts;
21 - char name[NETDATA_HARDIRQ_NAME_LEN];
22 -} hardirq_ebpf_val_t;
23 -
21 enum hardirq_ebpf_static {
22 HARDIRQ_EBPF_STATIC_APIC_THERMAL,
23 HARDIRQ_EBPF_STATIC_APIC_THRESHOLD,
@@ -46,6 +43,9 @@ typedef struct hardirq_ebpf_static_val {
43 * below this is eBPF plugin-specific code.
44 *****************************************************************/
45
46 +// ARAL Name
47 +#define NETDATA_EBPF_HARDIRQ_ARAL_NAME "ebpf_harddirq"
48 +
49 #define NETDATA_EBPF_MODULE_NAME_HARDIRQ "hardirq"
50 #define NETDATA_HARDIRQ_CONFIG_FILE "hardirq.conf"
51
collectors/ebpf.plugin/ebpf_oomkill.c
+1 -1
@@ -299,7 +299,7 @@ static void oomkill_collector(ebpf_module_t *em)
299 int counter = update_every - 1;
300 while (!ebpf_exit_plugin) {
301 (void)heartbeat_next(&hb, USEC_PER_SEC);
302 - if (!ebpf_exit_plugin || ++counter != update_every)
302 + if (ebpf_exit_plugin || ++counter != update_every)
303 continue;
304
305 counter = 0;
libnetdata/ebpf/ebpf.c
+7 -5
@@ -366,20 +366,22 @@ static uint32_t ebpf_select_index(uint32_t kernels, int is_rhf, uint32_t kver)
366 * V - The kernel version in string format.
367 *
368 * @param out the vector where the name will be stored
369 - * @param path
369 * @param len the size of the out vector.
370 + * @param path where the binaries are stored
371 * @param kver the kernel version
372 * @param name the eBPF program name.
373 * @param is_return is return or entry ?
374 */
375 -static void ebpf_mount_name(char *out, size_t len, char *path, uint32_t kver, const char *name, int is_return)
375 +static void ebpf_mount_name(char *out, size_t len, char *path, uint32_t kver, const char *name,
376 + int is_return, int is_rhf)
377 {
378 char *version = ebpf_select_kernel_name(kver);
378 - snprintfz(out, len, "%s/ebpf.d/%cnetdata_ebpf_%s.%s.o",
379 + snprintfz(out, len, "%s/ebpf.d/%cnetdata_ebpf_%s.%s%s.o",
380 path,
381 (is_return) ? 'r' : 'p',
382 name,
382 - version);
383 + version,
384 + (is_rhf != -1) ? ".rhf" : "");
385 }
386
387 //----------------------------------------------------------------------------------------------------------------------
@@ -781,7 +783,7 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kv
783
784 uint32_t idx = ebpf_select_index(em->kernels, is_rhf, kver);
785
784 - ebpf_mount_name(lpath, 4095, plugins_dir, idx, em->thread_name, em->mode);
786 + ebpf_mount_name(lpath, 4095, plugins_dir, idx, em->thread_name, em->mode, is_rhf);
787
788 // When this function is called ebpf.plugin is using legacy code, so we should reset the variable
789 em->load &= ~ NETDATA_EBPF_LOAD_METHODS;
netdata-installer.sh
+5
@@ -1564,6 +1564,11 @@ remove_old_ebpf() {
1564 rm -f "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/pnetdata_ebpf"*.?.*.o
1565 fi
1566
1567 + # Remove old eBPF programs that did not have "rhf" suffix
1568 + if [ ! -f "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/ebpf.d/pnetdata_ebpf_process.3.10.rhf.o" ]; then
1569 + rm -f "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/ebpf.d/"*.o
1570 + fi
1571 +
1572 # Remove old reject list from previous directory
1573 if [ -f "${NETDATA_PREFIX}/usr/lib/netdata/conf.d/ebpf_kernel_reject_list.txt" ]; then
1574 echo >&2 "Removing old ebpf_kernel_reject_list.txt."
packaging/ebpf-co-re.checksums
+1 -1
@@ -1 +1 @@
1 -d1864cd736d236aa3738152d86096529830822a26405a62fe164779949bb3658 netdata-ebpf-co-re-glibc-v1.1.0.tar.xz
1 +a50e649635cc2fe86c21a08334ee73451f08591ebbda8b5d0012c3b8fad2cc1e netdata-ebpf-co-re-glibc-v1.1.2.tar.xz
packaging/ebpf-co-re.version
+1 -1
@@ -1 +1 @@
1 -v1.1.0
1 +v1.1.2
packaging/ebpf.checksums
+3 -3
@@ -1,3 +1,3 @@
1 -7f28bb61b1e9fdac59e5f8f041502c54f319048c1cf4adaa96ace3360f55a80e ./netdata-kernel-collector-glibc-v1.1.0.tar.xz
2 -5d927deadac9a4a5bc8a5be386aec2ea4f9b8335e60eadf375b11e7656404270 ./netdata-kernel-collector-musl-v1.1.0.tar.xz
3 -0d8825b77b8ba20e10b6e24f15c1d65a43f1c47dced93798839adc789f1427d3 ./netdata-kernel-collector-static-v1.1.0.tar.xz
1 +597a20895bbedcf87528b08fa9057426bd3c7638aa1ffac94f8987a90634513d ./netdata-kernel-collector-glibc-v1.1.2.tar.xz
2 +25db2232b75bdb7fc6e10db870c3a3290f52ecfcdcf546d0e51947f2a4c17ccf ./netdata-kernel-collector-musl-v1.1.2.tar.xz
3 +1d60425f5e8c6e30b3be86028dfc62c16022d8fe561e4c21c84cf6e8b998cd7d ./netdata-kernel-collector-static-v1.1.2.tar.xz
packaging/ebpf.version
+1 -1
@@ -1 +1 @@
1 -v1.1.0
1 +v1.1.2