master
c 1,622 lines 49.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf.h"
4 #include "ebpf_dcstat.h"
5 #include "libbpf_api/ebpf_library.h"
6
7 static char *dcstat_counter_dimension_name[NETDATA_DCSTAT_IDX_END] = {"ratio", "reference", "slow", "miss"};
8 static netdata_syscall_stat_t dcstat_counter_aggregated_data[NETDATA_DCSTAT_IDX_END];
9 static netdata_publish_syscall_t dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_END];
10
11 netdata_dcstat_pid_t *dcstat_vector = NULL;
12
13 static netdata_idx_t dcstat_hash_values[NETDATA_DCSTAT_IDX_END];
14 static netdata_idx_t *dcstat_values = NULL;
15
16 struct config dcstat_config = APPCONFIG_INITIALIZER;
17
18 ebpf_local_maps_t dcstat_maps[] = {
19 {.name = "dcstat_global",
20 .internal_input = NETDATA_DIRECTORY_CACHE_END,
21 .user_input = 0,
22 .type = NETDATA_EBPF_MAP_STATIC,
23 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
24 #ifdef LIBBPF_MAJOR_VERSION
25 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
26 #endif
27 },
28 {.name = "dcstat_pid",
29 .internal_input = ND_EBPF_DEFAULT_PID_SIZE,
30 .user_input = 0,
31 .type = NETDATA_EBPF_MAP_RESIZABLE | NETDATA_EBPF_MAP_PID,
32 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
33 #ifdef LIBBPF_MAJOR_VERSION
34 .map_type = BPF_MAP_TYPE_PERCPU_HASH
35 #endif
36 },
37 {.name = "dcstat_ctrl",
38 .internal_input = NETDATA_CONTROLLER_END,
39 .user_input = 0,
40 .type = NETDATA_EBPF_MAP_CONTROLLER,
41 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
42 #ifdef LIBBPF_MAJOR_VERSION
43 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
44 #endif
45 },
46 {.name = NULL,
47 .internal_input = 0,
48 .user_input = 0,
49 .type = NETDATA_EBPF_MAP_CONTROLLER,
50 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
51 #ifdef LIBBPF_MAJOR_VERSION
52 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
53 #endif
54 }};
55
56 static ebpf_specify_name_t dc_optional_name[] = {
57 {.program_name = "netdata_lookup_fast",
58 .function_to_attach = "lookup_fast",
59 .optional = NULL,
60 .retprobe = CONFIG_BOOLEAN_NO},
61 {.program_name = NULL}};
62
63 netdata_ebpf_targets_t dc_targets[] = {
64 {.name = "lookup_fast", .mode = EBPF_LOAD_TRAMPOLINE},
65 {.name = "d_lookup", .mode = EBPF_LOAD_TRAMPOLINE},
66 {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
67
68 struct netdata_static_thread ebpf_read_dcstat = {
69 .name = "EBPF_READ_DCSTAT",
70 .config_section = NULL,
71 .config_name = NULL,
72 .env_name = NULL,
73 .enabled = 1,
74 .thread = NULL,
75 .init_routine = NULL,
76 .start_routine = NULL};
77
78 #ifdef LIBBPF_MAJOR_VERSION
79 /**
80 * Disable probe
81 *
82 * Disable all probes to use exclusively another method.
83 *
84 * @param obj is the main structure for bpf objects
85 */
86 static inline void ebpf_dc_disable_probes(struct dc_bpf *obj)
87 {
88 bpf_program__set_autoload(obj->progs.netdata_lookup_fast_kprobe, false);
89 bpf_program__set_autoload(obj->progs.netdata_d_lookup_kretprobe, false);
90 }
91
92 /*
93 * Disable trampoline
94 *
95 * Disable all trampoline to use exclusively another method.
96 *
97 * @param obj is the main structure for bpf objects.
98 */
99 static inline void ebpf_dc_disable_trampoline(struct dc_bpf *obj)
100 {
101 bpf_program__set_autoload(obj->progs.netdata_lookup_fast_fentry, false);
102 bpf_program__set_autoload(obj->progs.netdata_d_lookup_fexit, false);
103 }
104
105 /**
106 * Set trampoline target
107 *
108 * Set the targets we will monitor.
109 *
110 * @param obj is the main structure for bpf objects.
111 */
112 static void ebpf_dc_set_trampoline_target(struct dc_bpf *obj)
113 {
114 bpf_program__set_attach_target(
115 obj->progs.netdata_lookup_fast_fentry, 0, dc_targets[NETDATA_DC_TARGET_LOOKUP_FAST].name);
116
117 bpf_program__set_attach_target(obj->progs.netdata_d_lookup_fexit, 0, dc_targets[NETDATA_DC_TARGET_D_LOOKUP].name);
118 }
119
120 /**
121 * Mount Attach Probe
122 *
123 * Attach probes to target
124 *
125 * @param obj is the main structure for bpf objects.
126 *
127 * @return It returns 0 on success and -1 otherwise.
128 */
129 static int ebpf_dc_attach_probes(struct dc_bpf *obj)
130 {
131 obj->links.netdata_d_lookup_kretprobe = bpf_program__attach_kprobe(
132 obj->progs.netdata_d_lookup_kretprobe, true, dc_targets[NETDATA_DC_TARGET_D_LOOKUP].name);
133 long ret = libbpf_get_error(obj->links.netdata_d_lookup_kretprobe);
134 if (ret)
135 return -1;
136
137 char *lookup_name = (dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].optional) ?
138 dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].optional :
139 dc_targets[NETDATA_DC_TARGET_LOOKUP_FAST].name;
140
141 obj->links.netdata_lookup_fast_kprobe =
142 bpf_program__attach_kprobe(obj->progs.netdata_lookup_fast_kprobe, false, lookup_name);
143 ret = libbpf_get_error(obj->links.netdata_lookup_fast_kprobe);
144 if (ret)
145 return -1;
146
147 return 0;
148 }
149
150 /**
151 * Adjust Map Size
152 *
153 * Resize maps according input from users.
154 *
155 * @param obj is the main structure for bpf objects.
156 * @param em structure with configuration
157 */
158 static void ebpf_dc_adjust_map(struct dc_bpf *obj, ebpf_module_t *em)
159 {
160 ebpf_update_map_size(
161 obj->maps.dcstat_pid, &dcstat_maps[NETDATA_DCSTAT_PID_STATS], em, bpf_map__name(obj->maps.dcstat_pid));
162
163 ebpf_update_map_type(obj->maps.dcstat_global, &dcstat_maps[NETDATA_DCSTAT_GLOBAL_STATS]);
164 ebpf_update_map_type(obj->maps.dcstat_pid, &dcstat_maps[NETDATA_DCSTAT_PID_STATS]);
165 ebpf_update_map_type(obj->maps.dcstat_ctrl, &dcstat_maps[NETDATA_DCSTAT_CTRL]);
166 }
167
168 /**
169 * Set hash tables
170 *
171 * Set the values for maps according the value given by kernel.
172 *
173 * @param obj is the main structure for bpf objects.
174 */
175 static void ebpf_dc_set_hash_tables(struct dc_bpf *obj)
176 {
177 dcstat_maps[NETDATA_DCSTAT_GLOBAL_STATS].map_fd = bpf_map__fd(obj->maps.dcstat_global);
178 dcstat_maps[NETDATA_DCSTAT_PID_STATS].map_fd = bpf_map__fd(obj->maps.dcstat_pid);
179 dcstat_maps[NETDATA_DCSTAT_CTRL].map_fd = bpf_map__fd(obj->maps.dcstat_ctrl);
180 }
181
182 /**
183 * Update Load
184 *
185 * For directory cache, some distributions change the function name, and we do not have condition to use
186 * TRAMPOLINE like other functions.
187 *
188 * @param em structure with configuration
189 *
190 * @return When then symbols were not modified, it returns TRAMPOLINE, else it returns RETPROBE.
191 */
192 netdata_ebpf_program_loaded_t ebpf_dc_update_load(ebpf_module_t *em)
193 {
194 if (dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].optional &&
195 !strcmp(
196 dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].optional,
197 dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].function_to_attach))
198 return EBPF_LOAD_TRAMPOLINE;
199
200 if (em->targets[NETDATA_DC_TARGET_LOOKUP_FAST].mode != EBPF_LOAD_RETPROBE)
201 netdata_log_info(
202 "When your kernel was compiled the symbol %s was modified, instead to use `trampoline`, the plugin will use `probes`.",
203 dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].function_to_attach);
204
205 return EBPF_LOAD_RETPROBE;
206 }
207
208 /**
209 * Load and attach
210 *
211 * Load and attach the eBPF code in kernel.
212 *
213 * @param obj is the main structure for bpf objects.
214 * @param em structure with configuration
215 *
216 * @return it returns 0 on success and -1 otherwise
217 */
218 static inline int ebpf_dc_load_and_attach(struct dc_bpf *obj, ebpf_module_t *em)
219 {
220 netdata_ebpf_program_loaded_t test = ebpf_dc_update_load(em);
221 if (test == EBPF_LOAD_TRAMPOLINE) {
222 ebpf_dc_disable_probes(obj);
223
224 ebpf_dc_set_trampoline_target(obj);
225 } else {
226 ebpf_dc_disable_trampoline(obj);
227 }
228
229 ebpf_dc_adjust_map(obj, em);
230
231 int ret = dc_bpf__load(obj);
232 if (ret) {
233 return ret;
234 }
235
236 ret = (test == EBPF_LOAD_TRAMPOLINE) ? dc_bpf__attach(obj) : ebpf_dc_attach_probes(obj);
237 if (!ret) {
238 ebpf_dc_set_hash_tables(obj);
239
240 ebpf_update_controller(dcstat_maps[NETDATA_DCSTAT_CTRL].map_fd, em);
241 }
242
243 return ret;
244 }
245 #endif
246
247 /*****************************************************************
248 *
249 * COMMON FUNCTIONS
250 *
251 *****************************************************************/
252
253 /**
254 * Update publish
255 *
256 * Update publish values before to write dimension.
257 *
258 * @param out structure that will receive data.
259 * @param cache_access number of access to directory cache.
260 * @param not_found number of files not found on the file system
261 */
262 void dcstat_update_publish(netdata_publish_dcstat_t *out, uint64_t cache_access, uint64_t not_found)
263 {
264 NETDATA_DOUBLE successful_access = (NETDATA_DOUBLE)(((long long)cache_access) - ((long long)not_found));
265 if (successful_access < 0)
266 successful_access = 0;
267 NETDATA_DOUBLE ratio = (cache_access) ? successful_access / (NETDATA_DOUBLE)cache_access : 0;
268
269 out->ratio = (long long)(ratio * 100);
270 }
271
272 /*****************************************************************
273 *
274 * FUNCTIONS TO CLOSE THE THREAD
275 *
276 *****************************************************************/
277
278 static void ebpf_obsolete_specific_dc_charts(char *type, int update_every);
279
280 /**
281 * Obsolete services
282 *
283 * Obsolete all service charts created
284 *
285 * @param em a pointer to `struct ebpf_module`
286 */
287 static void ebpf_obsolete_dc_services(ebpf_module_t *em, char *id)
288 {
289 ebpf_write_chart_obsolete(
290 id,
291 NETDATA_DC_HIT_CHART,
292 "",
293 "Percentage of files inside directory cache",
294 EBPF_COMMON_UNITS_PERCENTAGE,
295 NETDATA_DIRECTORY_CACHE_SUBMENU,
296 NETDATA_EBPF_CHART_TYPE_LINE,
297 NETDATA_SYSTEMD_DC_HIT_RATIO_CONTEXT,
298 21200,
299 em->update_every);
300
301 ebpf_write_chart_obsolete(
302 id,
303 NETDATA_DC_REFERENCE_CHART,
304 "",
305 "Count file access",
306 EBPF_COMMON_UNITS_FILES,
307 NETDATA_DIRECTORY_CACHE_SUBMENU,
308 NETDATA_EBPF_CHART_TYPE_LINE,
309 NETDATA_SYSTEMD_DC_REFERENCE_CONTEXT,
310 21201,
311 em->update_every);
312
313 ebpf_write_chart_obsolete(
314 id,
315 NETDATA_DC_REQUEST_NOT_CACHE_CHART,
316 "",
317 "Files not present inside directory cache",
318 EBPF_COMMON_UNITS_FILES,
319 NETDATA_DIRECTORY_CACHE_SUBMENU,
320 NETDATA_EBPF_CHART_TYPE_LINE,
321 NETDATA_SYSTEMD_DC_NOT_CACHE_CONTEXT,
322 21202,
323 em->update_every);
324
325 ebpf_write_chart_obsolete(
326 id,
327 NETDATA_DC_REQUEST_NOT_FOUND_CHART,
328 "",
329 "Files not found",
330 EBPF_COMMON_UNITS_FILES,
331 NETDATA_DIRECTORY_CACHE_SUBMENU,
332 NETDATA_EBPF_CHART_TYPE_LINE,
333 NETDATA_SYSTEMD_DC_NOT_FOUND_CONTEXT,
334 21203,
335 em->update_every);
336 }
337
338 /**
339 * Obsolete cgroup chart
340 *
341 * Send obsolete for all charts created before to close.
342 *
343 * @param em a pointer to `struct ebpf_module`
344 */
345 static inline void ebpf_obsolete_dc_cgroup_charts(ebpf_module_t *em)
346 {
347 netdata_mutex_lock(&mutex_cgroup_shm);
348
349 ebpf_cgroup_target_t *ect;
350 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
351 if (ect->systemd) {
352 ebpf_obsolete_dc_services(em, ect->name);
353
354 continue;
355 }
356
357 ebpf_obsolete_specific_dc_charts(ect->name, em->update_every);
358 }
359 netdata_mutex_unlock(&mutex_cgroup_shm);
360 }
361
362 /**
363 * Obsolette apps charts
364 *
365 * Obsolete apps charts.
366 *
367 * @param em a pointer to the structure with the default values.
368 */
369 void ebpf_obsolete_dc_apps_charts(struct ebpf_module *em)
370 {
371 struct ebpf_target *w;
372 int update_every = em->update_every;
373 netdata_mutex_lock(&collect_data_mutex);
374 for (w = apps_groups_root_target; w; w = w->next) {
375 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_DCSTAT_IDX))))
376 continue;
377
378 ebpf_write_chart_obsolete(
379 NETDATA_APP_FAMILY,
380 w->clean_name,
381 "_ebpf_dc_hit",
382 "Percentage of files inside directory cache.",
383 EBPF_COMMON_UNITS_PERCENTAGE,
384 NETDATA_DIRECTORY_CACHE_SUBMENU,
385 NETDATA_EBPF_CHART_TYPE_LINE,
386 "app.ebpf_dc_hit",
387 20265,
388 update_every);
389
390 ebpf_write_chart_obsolete(
391 NETDATA_APP_FAMILY,
392 w->clean_name,
393 "_ebpf_dc_reference",
394 "Count file access.",
395 EBPF_COMMON_UNITS_FILES,
396 NETDATA_DIRECTORY_CACHE_SUBMENU,
397 NETDATA_EBPF_CHART_TYPE_STACKED,
398 "app.ebpf_dc_reference",
399 20266,
400 update_every);
401
402 ebpf_write_chart_obsolete(
403 NETDATA_APP_FAMILY,
404 w->clean_name,
405 "_ebpf_not_cache",
406 "Files not present inside directory cache.",
407 EBPF_COMMON_UNITS_FILES,
408 NETDATA_DIRECTORY_CACHE_SUBMENU,
409 NETDATA_EBPF_CHART_TYPE_STACKED,
410 "app.ebpf_dc_not_cache",
411 20267,
412 update_every);
413
414 ebpf_write_chart_obsolete(
415 NETDATA_APP_FAMILY,
416 w->clean_name,
417 "_ebpf_not_found",
418 "Files not found.",
419 EBPF_COMMON_UNITS_FILES,
420 NETDATA_DIRECTORY_CACHE_SUBMENU,
421 NETDATA_EBPF_CHART_TYPE_STACKED,
422 "app.ebpf_dc_not_found",
423 20268,
424 update_every);
425
426 w->charts_created &= ~(1 << EBPF_MODULE_DCSTAT_IDX);
427 }
428 netdata_mutex_unlock(&collect_data_mutex);
429 }
430
431 /**
432 * Obsolete global
433 *
434 * Obsolete global charts created by thread.
435 *
436 * @param em a pointer to `struct ebpf_module`
437 */
438 static void ebpf_obsolete_dc_global(ebpf_module_t *em)
439 {
440 ebpf_write_chart_obsolete(
441 NETDATA_FILESYSTEM_FAMILY,
442 NETDATA_DC_HIT_CHART,
443 "",
444 "Percentage of files inside directory cache",
445 EBPF_COMMON_UNITS_PERCENTAGE,
446 NETDATA_DIRECTORY_CACHE_SUBMENU,
447 NETDATA_EBPF_CHART_TYPE_LINE,
448 NETDATA_FS_DC_HIT_RATIO_CONTEXT,
449 21200,
450 em->update_every);
451
452 ebpf_write_chart_obsolete(
453 NETDATA_FILESYSTEM_FAMILY,
454 NETDATA_DC_REFERENCE_CHART,
455 "",
456 "Variables used to calculate hit ratio.",
457 EBPF_COMMON_UNITS_FILES,
458 NETDATA_DIRECTORY_CACHE_SUBMENU,
459 NETDATA_EBPF_CHART_TYPE_LINE,
460 NETDATA_FS_DC_REFERENCE_CONTEXT,
461 21201,
462 em->update_every);
463 }
464
465 /**
466 * DCstat exit
467 *
468 * Cancel child and exit.
469 *
470 * @param ptr thread data.
471 */
472 void ebpf_dcstat_unload_bpf(ebpf_module_t *em)
473 {
474 #ifdef LIBBPF_MAJOR_VERSION
475 if (dc_bpf_obj) {
476 dc_bpf__destroy(dc_bpf_obj);
477 dc_bpf_obj = NULL;
478 }
479 #endif
480 if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
481 ebpf_unload_legacy_code(em->objects, em->probe_links);
482 em->objects = NULL;
483 em->probe_links = NULL;
484 }
485 }
486
487 static void ebpf_dcstat_exit(void *pptr)
488 {
489 ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_DCSTAT_IDX, -1);
490 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
491 if (!em)
492 return;
493
494 netdata_mutex_lock(&lock);
495 collect_pids &= ~(1 << EBPF_MODULE_DCSTAT_IDX);
496 netdata_mutex_unlock(&lock);
497
498 if (ebpf_read_dcstat.thread)
499 nd_thread_signal_cancel(ebpf_read_dcstat.thread);
500
501 // Drop this module's bits from the shared PID pool so its slots don't
502 // stay pinned if the plugin keeps running after the module stops.
503 if (integration_shm && ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
504 netdata_ebpf_sweep_shm_for_module_unsafe(NETDATA_EBPF_PIDS_DCSTAT_IDX);
505 sem_post(shm_mutex_ebpf_integration);
506 }
507
508 if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
509 netdata_mutex_lock(&lock);
510 if (em->cgroup_charts) {
511 ebpf_obsolete_dc_cgroup_charts(em);
512 fflush(stdout);
513 }
514
515 if (em->apps_charts & NETDATA_EBPF_APPS_FLAG_CHART_CREATED) {
516 ebpf_obsolete_dc_apps_charts(em);
517 }
518
519 ebpf_obsolete_dc_global(em);
520
521 fflush(stdout);
522 netdata_mutex_unlock(&lock);
523 }
524
525 if (!ebpf_plugin_stop() && em->functions.bpf_unload)
526 em->functions.bpf_unload(em);
527
528 netdata_mutex_lock(&ebpf_exit_cleanup);
529 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
530 netdata_mutex_unlock(&ebpf_exit_cleanup);
531 }
532
533 /*****************************************************************
534 *
535 * APPS
536 *
537 *****************************************************************/
538
539 /**
540 * Apps Accumulator
541 *
542 * Sum all values read from kernel and store in the first address.
543 *
544 * @param out the vector with read values.
545 * @param maps_per_core do I need to read all cores?
546 */
547 static void ebpf_dcstat_apps_accumulator(netdata_dcstat_pid_t *out, int maps_per_core)
548 {
549 int i, end = (maps_per_core) ? ebpf_nprocs : 1;
550 netdata_dcstat_pid_t *total = &out[0];
551 uint64_t ct = total->ct;
552 for (i = 1; i < end; i++) {
553 if (ebpf_plugin_stop())
554 break;
555
556 netdata_dcstat_pid_t *w = &out[i];
557 total->cache_access += w->cache_access;
558 total->file_system += w->file_system;
559 total->not_found += w->not_found;
560
561 if (w->ct > ct)
562 ct = w->ct;
563
564 if (!total->name[0] && w->name[0])
565 strncpyz(total->name, w->name, sizeof(total->name));
566 }
567 total->ct = ct;
568 }
569
570 /**
571 * Read Directory Cache APPS table
572 *
573 * Read the apps table and store data inside the structure.
574 *
575 * @param maps_per_core do I need to read all cores?
576 */
577 static void ebpf_read_dc_apps_table(int maps_per_core)
578 {
579 netdata_dcstat_pid_t *cv = dcstat_vector;
580 int fd = dcstat_maps[NETDATA_DCSTAT_PID_STATS].map_fd;
581 size_t length = sizeof(netdata_dcstat_pid_t);
582 if (maps_per_core)
583 length *= ebpf_nprocs;
584
585 uint32_t key = 0, next_key = 0;
586 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
587 if (ebpf_plugin_stop())
588 break;
589
590 if (bpf_map_lookup_elem(fd, &key, cv)) {
591 goto end_dc_loop;
592 }
593
594 ebpf_dcstat_apps_accumulator(cv, maps_per_core);
595
596 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_get_shm_pointer_unsafe(key, NETDATA_EBPF_PIDS_DCSTAT_IDX);
597 if (!local_pid)
598 goto end_dc_loop;
599 netdata_publish_dcstat_t *publish = &local_pid->directory_cache;
600 if (!publish->ct || publish->ct != cv->ct) {
601 publish->ct = cv->ct;
602 publish->curr.not_found = cv[0].not_found;
603 publish->curr.file_system = cv[0].file_system;
604 publish->curr.cache_access = cv[0].cache_access;
605 } else {
606 if (kill((pid_t)key, 0) == -1 && errno == ESRCH) {
607 if (netdata_ebpf_reset_shm_pointer_unsafe(fd, key, NETDATA_EBPF_PIDS_DCSTAT_IDX))
608 memset(publish, 0, sizeof(*publish));
609 }
610 }
611
612 end_dc_loop:
613 // We are cleaning to avoid passing data read from one process to other.
614 memset(cv, 0, length);
615 key = next_key;
616 }
617 }
618
619 /**
620 * Cachestat sum PIDs
621 *
622 * Sum values for all PIDs associated to a group
623 *
624 * @param publish output structure.
625 * @param root structure with listed IPs
626 */
627 void ebpf_dcstat_sum_pids(netdata_publish_dcstat_t *publish, struct ebpf_pid_on_target *root)
628 {
629 memset(&publish->curr, 0, sizeof(netdata_publish_dcstat_pid_t));
630 for (; root; root = root->next) {
631 if (ebpf_plugin_stop())
632 break;
633
634 uint32_t pid = root->pid;
635 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_lookup_shm_pointer_unsafe(pid);
636 if (!local_pid || !(local_pid->threads & (1U << (NETDATA_EBPF_PIDS_DCSTAT_IDX << 1))))
637 continue;
638 netdata_publish_dcstat_t *w = &local_pid->directory_cache;
639
640 publish->curr.cache_access += w->curr.cache_access;
641 publish->curr.file_system += w->curr.file_system;
642 publish->curr.not_found += w->curr.not_found;
643 }
644 }
645
646 /**
647 * Resume apps data
648 */
649 void ebpf_dc_resume_apps_data(void)
650 {
651 struct ebpf_target *w;
652
653 netdata_mutex_lock(&collect_data_mutex);
654 for (w = apps_groups_root_target; w; w = w->next) {
655 if (ebpf_plugin_stop())
656 break;
657
658 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_DCSTAT_IDX))))
659 continue;
660
661 ebpf_dcstat_sum_pids(&w->dcstat, w->root_pid);
662
663 uint64_t cache = w->dcstat.curr.cache_access;
664 uint64_t not_found = w->dcstat.curr.not_found;
665
666 dcstat_update_publish(&w->dcstat, cache, not_found);
667 }
668 netdata_mutex_unlock(&collect_data_mutex);
669 }
670
671 /**
672 * Update cgroup
673 *
674 * Update cgroup data based in collected PID.
675 *
676 * @param maps_per_core do I need to read all cores?
677 */
678 static void ebpf_update_dc_cgroup()
679 {
680 ebpf_cgroup_target_t *ect;
681 netdata_mutex_lock(&mutex_cgroup_shm);
682 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
683 if (ebpf_plugin_stop())
684 break;
685
686 struct pid_on_target2 *pids;
687 for (pids = ect->pids; pids; pids = pids->next) {
688 uint32_t pid = pids->pid;
689 netdata_dcstat_pid_t *out = &pids->dc;
690 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_lookup_shm_pointer_unsafe(pid);
691 if (!local_pid || !(local_pid->threads & (1U << (NETDATA_EBPF_PIDS_DCSTAT_IDX << 1))))
692 continue;
693 netdata_publish_dcstat_t *in = &local_pid->directory_cache;
694
695 memcpy(out, &in->curr, sizeof(netdata_publish_dcstat_pid_t));
696 }
697 }
698 netdata_mutex_unlock(&mutex_cgroup_shm);
699 }
700
701 /**
702 * DCstat thread
703 *
704 * Thread used to generate dcstat charts.
705 *
706 * @param ptr a pointer to `struct ebpf_module`
707 *
708 * @return It always return NULL
709 */
710 void ebpf_read_dcstat_thread(void *ptr)
711 {
712 ebpf_module_t *em = (ebpf_module_t *)ptr;
713
714 int maps_per_core = em->maps_per_core;
715 int update_every = em->update_every;
716 int collect_pid = (em->apps_charts || em->cgroup_charts);
717 int cgroups = em->cgroup_charts;
718 if (!collect_pid)
719 return;
720
721 int counter = update_every - 1;
722
723 uint32_t lifetime = em->lifetime;
724 uint32_t running_time = 0;
725 ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_DCSTAT_IDX, dcstat_maps[NETDATA_DCSTAT_PID_STATS].map_fd);
726 heartbeat_t hb;
727 heartbeat_init(&hb, USEC_PER_SEC);
728 while (!ebpf_plugin_stop() && running_time < lifetime) {
729 if (ebpf_plugin_stop())
730 break;
731
732 (void)heartbeat_next(&hb);
733 if (ebpf_plugin_stop())
734 break;
735
736 if (++counter != update_every)
737 continue;
738
739 if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
740 if (errno != ECANCELED)
741 netdata_log_error("DCSTAT: Failed to wait on semaphore.");
742 break;
743 }
744
745 ebpf_read_dc_apps_table(maps_per_core);
746 ebpf_dc_resume_apps_data();
747 if (ebpf_plugin_stop()) {
748 if (sem_post(shm_mutex_ebpf_integration))
749 netdata_log_error("DCSTAT: Failed to post semaphore.");
750 break;
751 }
752
753 if (cgroups && ebpf_cgroup_integration_active_get())
754 ebpf_update_dc_cgroup();
755
756 if (sem_post(shm_mutex_ebpf_integration)) {
757 netdata_log_error("DCSTAT: Failed to post semaphore.");
758 break;
759 }
760
761 counter = 0;
762
763 if (ebpf_plugin_stop()) {
764 break;
765 }
766
767 netdata_mutex_lock(&ebpf_exit_cleanup);
768 if (running_time)
769 running_time += update_every;
770 else
771 running_time = update_every;
772
773 em->running_time = running_time;
774 netdata_mutex_unlock(&ebpf_exit_cleanup);
775 }
776 }
777
778 /**
779 * Create apps charts
780 *
781 * Call ebpf_create_chart to create the charts on apps submenu.
782 *
783 * @param em a pointer to the structure with the default values.
784 */
785 void ebpf_dcstat_create_apps_charts(struct ebpf_module *em, void *ptr)
786 {
787 struct ebpf_target *root = ptr;
788 struct ebpf_target *w;
789 int update_every = em->update_every;
790 for (w = root; w; w = w->next) {
791 if (ebpf_plugin_stop())
792 break;
793
794 if (unlikely(!w->exposed))
795 continue;
796
797 ebpf_write_chart_cmd(
798 NETDATA_APP_FAMILY,
799 w->clean_name,
800 "_ebpf_dc_hit",
801 "Percentage of files inside directory cache.",
802 EBPF_COMMON_UNITS_PERCENTAGE,
803 NETDATA_DIRECTORY_CACHE_SUBMENU,
804 NETDATA_EBPF_CHART_TYPE_LINE,
805 "app.ebpf_dc_hit",
806 20265,
807 update_every,
808 NETDATA_EBPF_MODULE_NAME_DCSTAT);
809 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
810 ebpf_commit_label();
811 fprintf(stdout, "DIMENSION ratio '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
812
813 ebpf_write_chart_cmd(
814 NETDATA_APP_FAMILY,
815 w->clean_name,
816 "_ebpf_dc_reference",
817 "Count file access.",
818 EBPF_COMMON_UNITS_FILES,
819 NETDATA_DIRECTORY_CACHE_SUBMENU,
820 NETDATA_EBPF_CHART_TYPE_STACKED,
821 "app.ebpf_dc_reference",
822 20266,
823 update_every,
824 NETDATA_EBPF_MODULE_NAME_DCSTAT);
825 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
826 ebpf_commit_label();
827 fprintf(stdout, "DIMENSION files '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
828
829 ebpf_write_chart_cmd(
830 NETDATA_APP_FAMILY,
831 w->clean_name,
832 "_ebpf_not_cache",
833 "Files not present inside directory cache.",
834 EBPF_COMMON_UNITS_FILES,
835 NETDATA_DIRECTORY_CACHE_SUBMENU,
836 NETDATA_EBPF_CHART_TYPE_STACKED,
837 "app.ebpf_dc_not_cache",
838 20267,
839 update_every,
840 NETDATA_EBPF_MODULE_NAME_DCSTAT);
841 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
842 ebpf_commit_label();
843 fprintf(stdout, "DIMENSION files '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
844
845 ebpf_write_chart_cmd(
846 NETDATA_APP_FAMILY,
847 w->clean_name,
848 "_ebpf_not_found",
849 "Files not found.",
850 EBPF_COMMON_UNITS_FILES,
851 NETDATA_DIRECTORY_CACHE_SUBMENU,
852 NETDATA_EBPF_CHART_TYPE_STACKED,
853 "app.ebpf_dc_not_found",
854 20268,
855 update_every,
856 NETDATA_EBPF_MODULE_NAME_DCSTAT);
857 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
858 ebpf_commit_label();
859 fprintf(stdout, "DIMENSION files '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
860
861 w->charts_created |= 1 << EBPF_MODULE_DCSTAT_IDX;
862 }
863
864 em->apps_charts |= NETDATA_EBPF_APPS_FLAG_CHART_CREATED;
865 }
866
867 /*****************************************************************
868 *
869 * MAIN LOOP
870 *
871 *****************************************************************/
872
873 /**
874 * Read global table
875 *
876 * Read the table with number of calls for all functions
877 *
878 * @param maps_per_core do I need to read all cores?
879 */
880 static void ebpf_dc_read_global_tables(int maps_per_core)
881 {
882 ebpf_read_global_table_stats(
883 dcstat_hash_values,
884 dcstat_values,
885 dcstat_maps[NETDATA_DCSTAT_GLOBAL_STATS].map_fd,
886 maps_per_core,
887 NETDATA_KEY_DC_REFERENCE,
888 NETDATA_DIRECTORY_CACHE_END);
889 }
890
891 /**
892 * Send data to Netdata calling auxiliary functions.
893 *
894 * @param root the target list.
895 */
896 void ebpf_dcache_send_apps_data(struct ebpf_target *root)
897 {
898 struct ebpf_target *w;
899 collected_number value;
900
901 netdata_mutex_lock(&collect_data_mutex);
902 for (w = root; w; w = w->next) {
903 if (ebpf_plugin_stop())
904 break;
905
906 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_DCSTAT_IDX))))
907 continue;
908
909 value = (collected_number)w->dcstat.ratio;
910 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_dc_hit");
911 write_chart_dimension("ratio", value);
912 ebpf_write_end_chart();
913
914 if (w->dcstat.curr.cache_access < w->dcstat.prev.cache_access) {
915 w->dcstat.prev.cache_access = 0;
916 }
917 w->dcstat.cache_access = (long long)w->dcstat.curr.cache_access - (long long)w->dcstat.prev.cache_access;
918
919 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_dc_reference");
920 value = (collected_number)w->dcstat.cache_access;
921 write_chart_dimension("files", value);
922 ebpf_write_end_chart();
923 w->dcstat.prev.cache_access = w->dcstat.curr.cache_access;
924
925 if (w->dcstat.curr.file_system < w->dcstat.prev.file_system) {
926 w->dcstat.prev.file_system = 0;
927 }
928 value = (collected_number)(!w->dcstat.cache_access) ?
929 0 :
930 (long long)w->dcstat.curr.file_system - (long long)w->dcstat.prev.file_system;
931 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_not_cache");
932 write_chart_dimension("files", value);
933 ebpf_write_end_chart();
934 w->dcstat.prev.file_system = w->dcstat.curr.file_system;
935
936 if (w->dcstat.curr.not_found < w->dcstat.prev.not_found) {
937 w->dcstat.prev.not_found = 0;
938 }
939 value = (collected_number)(!w->dcstat.cache_access) ?
940 0 :
941 (long long)w->dcstat.curr.not_found - (long long)w->dcstat.prev.not_found;
942 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_not_found");
943 write_chart_dimension("files", value);
944 ebpf_write_end_chart();
945 w->dcstat.prev.not_found = w->dcstat.curr.not_found;
946 }
947 netdata_mutex_unlock(&collect_data_mutex);
948 }
949
950 /**
951 * Send global
952 *
953 * Send global charts to Netdata
954 */
955 static void dcstat_send_global(netdata_publish_dcstat_t *publish)
956 {
957 dcstat_update_publish(
958 publish, dcstat_hash_values[NETDATA_KEY_DC_REFERENCE], dcstat_hash_values[NETDATA_KEY_DC_MISS]);
959
960 netdata_publish_syscall_t *ptr = dcstat_counter_publish_aggregated;
961
962 netdata_idx_t ref_value = dcstat_hash_values[NETDATA_KEY_DC_REFERENCE];
963 ptr[NETDATA_DCSTAT_IDX_REFERENCE].ncall = ref_value - ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall;
964 ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall = ref_value;
965
966 netdata_idx_t slow_value = dcstat_hash_values[NETDATA_KEY_DC_SLOW];
967 ptr[NETDATA_DCSTAT_IDX_SLOW].ncall = slow_value - ptr[NETDATA_DCSTAT_IDX_SLOW].pcall;
968 ptr[NETDATA_DCSTAT_IDX_SLOW].pcall = slow_value;
969
970 netdata_idx_t miss_value = dcstat_hash_values[NETDATA_KEY_DC_MISS];
971 ptr[NETDATA_DCSTAT_IDX_MISS].ncall = miss_value - ptr[NETDATA_DCSTAT_IDX_MISS].pcall;
972 ptr[NETDATA_DCSTAT_IDX_MISS].pcall = miss_value;
973
974 ebpf_one_dimension_write_charts(
975 NETDATA_FILESYSTEM_FAMILY, NETDATA_DC_HIT_CHART, ptr[NETDATA_DCSTAT_IDX_RATIO].dimension, publish->ratio);
976
977 write_count_chart(
978 NETDATA_DC_REFERENCE_CHART,
979 NETDATA_FILESYSTEM_FAMILY,
980 &dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_REFERENCE],
981 3);
982 }
983
984 /**
985 * Create specific directory cache charts
986 *
987 * Create charts for cgroup/application.
988 *
989 * @param type the chart type.
990 * @param update_every value to overwrite the update frequency set by the server.
991 */
992 static void ebpf_create_specific_dc_charts(char *type, int update_every)
993 {
994 char *label = (!strncmp(type, "cgroup_", 7)) ? &type[7] : type;
995 ebpf_create_chart(
996 type,
997 NETDATA_DC_HIT_CHART,
998 "Percentage of files inside directory cache",
999 EBPF_COMMON_UNITS_PERCENTAGE,
1000 NETDATA_DIRECTORY_CACHE_SUBMENU,
1001 NETDATA_CGROUP_DC_HIT_RATIO_CONTEXT,
1002 NETDATA_EBPF_CHART_TYPE_LINE,
1003 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5700,
1004 ebpf_create_global_dimension,
1005 dcstat_counter_publish_aggregated,
1006 1,
1007 update_every,
1008 NETDATA_EBPF_MODULE_NAME_DCSTAT);
1009 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
1010 ebpf_commit_label();
1011
1012 ebpf_create_chart(
1013 type,
1014 NETDATA_DC_REFERENCE_CHART,
1015 "Count file access",
1016 EBPF_COMMON_UNITS_FILES,
1017 NETDATA_DIRECTORY_CACHE_SUBMENU,
1018 NETDATA_CGROUP_DC_REFERENCE_CONTEXT,
1019 NETDATA_EBPF_CHART_TYPE_LINE,
1020 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5701,
1021 ebpf_create_global_dimension,
1022 &dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_REFERENCE],
1023 1,
1024 update_every,
1025 NETDATA_EBPF_MODULE_NAME_DCSTAT);
1026 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
1027 ebpf_commit_label();
1028
1029 ebpf_create_chart(
1030 type,
1031 NETDATA_DC_REQUEST_NOT_CACHE_CHART,
1032 "Files not present inside directory cache",
1033 EBPF_COMMON_UNITS_FILES,
1034 NETDATA_DIRECTORY_CACHE_SUBMENU,
1035 NETDATA_CGROUP_DC_NOT_CACHE_CONTEXT,
1036 NETDATA_EBPF_CHART_TYPE_LINE,
1037 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5702,
1038 ebpf_create_global_dimension,
1039 &dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_SLOW],
1040 1,
1041 update_every,
1042 NETDATA_EBPF_MODULE_NAME_DCSTAT);
1043 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
1044 ebpf_commit_label();
1045
1046 ebpf_create_chart(
1047 type,
1048 NETDATA_DC_REQUEST_NOT_FOUND_CHART,
1049 "Files not found",
1050 EBPF_COMMON_UNITS_FILES,
1051 NETDATA_DIRECTORY_CACHE_SUBMENU,
1052 NETDATA_CGROUP_DC_NOT_FOUND_CONTEXT,
1053 NETDATA_EBPF_CHART_TYPE_LINE,
1054 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5703,
1055 ebpf_create_global_dimension,
1056 &dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_MISS],
1057 1,
1058 update_every,
1059 NETDATA_EBPF_MODULE_NAME_DCSTAT);
1060 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
1061 ebpf_commit_label();
1062 }
1063
1064 /**
1065 * Obsolete specific directory cache charts
1066 *
1067 * Obsolete charts for cgroup/application.
1068 *
1069 * @param type the chart type.
1070 * @param update_every value to overwrite the update frequency set by the server.
1071 */
1072 static void ebpf_obsolete_specific_dc_charts(char *type, int update_every)
1073 {
1074 ebpf_write_chart_obsolete(
1075 type,
1076 NETDATA_DC_HIT_CHART,
1077 "",
1078 "Percentage of files inside directory cache",
1079 EBPF_COMMON_UNITS_PERCENTAGE,
1080 NETDATA_DIRECTORY_CACHE_SUBMENU,
1081 NETDATA_EBPF_CHART_TYPE_LINE,
1082 NETDATA_CGROUP_DC_HIT_RATIO_CONTEXT,
1083 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5700,
1084 update_every);
1085
1086 ebpf_write_chart_obsolete(
1087 type,
1088 NETDATA_DC_REFERENCE_CHART,
1089 "",
1090 "Count file access",
1091 EBPF_COMMON_UNITS_FILES,
1092 NETDATA_DIRECTORY_CACHE_SUBMENU,
1093 NETDATA_EBPF_CHART_TYPE_LINE,
1094 NETDATA_CGROUP_DC_REFERENCE_CONTEXT,
1095 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5701,
1096 update_every);
1097
1098 ebpf_write_chart_obsolete(
1099 type,
1100 NETDATA_DC_REQUEST_NOT_CACHE_CHART,
1101 "",
1102 "Files not present inside directory cache",
1103 EBPF_COMMON_UNITS_FILES,
1104 NETDATA_DIRECTORY_CACHE_SUBMENU,
1105 NETDATA_EBPF_CHART_TYPE_LINE,
1106 NETDATA_CGROUP_DC_NOT_CACHE_CONTEXT,
1107 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5702,
1108 update_every);
1109
1110 ebpf_write_chart_obsolete(
1111 type,
1112 NETDATA_DC_REQUEST_NOT_FOUND_CHART,
1113 "",
1114 "Files not found",
1115 EBPF_COMMON_UNITS_FILES,
1116 NETDATA_DIRECTORY_CACHE_SUBMENU,
1117 NETDATA_EBPF_CHART_TYPE_LINE,
1118 NETDATA_CGROUP_DC_NOT_FOUND_CONTEXT,
1119 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5703,
1120 update_every);
1121 }
1122
1123 /**
1124 * Cachestat sum PIDs
1125 *
1126 * Sum values for all PIDs associated to a group
1127 *
1128 * @param publish output structure.
1129 * @param root structure with listed IPs
1130 */
1131 void ebpf_dc_sum_cgroup_pids(netdata_publish_dcstat_t *publish, struct pid_on_target2 *root)
1132 {
1133 memset(&publish->curr, 0, sizeof(netdata_publish_dcstat_pid_t));
1134 while (root) {
1135 if (ebpf_plugin_stop())
1136 break;
1137
1138 netdata_dcstat_pid_t *src = &root->dc;
1139
1140 publish->curr.cache_access += src->cache_access;
1141 publish->curr.file_system += src->file_system;
1142 publish->curr.not_found += src->not_found;
1143
1144 root = root->next;
1145 }
1146 }
1147
1148 /**
1149 * Calc chart values
1150 *
1151 * Do necessary math to plot charts.
1152 */
1153 void ebpf_dc_calc_chart_values(void)
1154 {
1155 ebpf_cgroup_target_t *ect;
1156 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1157 if (ebpf_plugin_stop())
1158 break;
1159
1160 ebpf_dc_sum_cgroup_pids(&ect->publish_dc, ect->pids);
1161 uint64_t cache = ect->publish_dc.curr.cache_access;
1162 uint64_t not_found = ect->publish_dc.curr.not_found;
1163
1164 dcstat_update_publish(&ect->publish_dc, cache, not_found);
1165
1166 ect->publish_dc.cache_access =
1167 (long long)ect->publish_dc.curr.cache_access - (long long)ect->publish_dc.prev.cache_access;
1168 ect->publish_dc.prev.cache_access = ect->publish_dc.curr.cache_access;
1169
1170 if (ect->publish_dc.curr.not_found < ect->publish_dc.prev.not_found) {
1171 ect->publish_dc.prev.not_found = 0;
1172 }
1173 }
1174 }
1175
1176 /**
1177 * Create Systemd directory cache Charts
1178 *
1179 * Create charts when systemd is enabled
1180 *
1181 * @param update_every value to overwrite the update frequency set by the server.
1182 **/
1183 static void ebpf_create_systemd_dc_charts(int update_every)
1184 {
1185 static ebpf_systemd_args_t data_dc_hit_ratio = {
1186 .title = "Percentage of files inside directory cache",
1187 .units = EBPF_COMMON_UNITS_PERCENTAGE,
1188 .family = NETDATA_DIRECTORY_CACHE_SUBMENU,
1189 .charttype = NETDATA_EBPF_CHART_TYPE_LINE,
1190 .order = 21200,
1191 .algorithm = EBPF_CHART_ALGORITHM_ABSOLUTE,
1192 .context = NETDATA_SYSTEMD_DC_HIT_RATIO_CONTEXT,
1193 .module = NETDATA_EBPF_MODULE_NAME_DCSTAT,
1194 .update_every = 0,
1195 .suffix = NETDATA_DC_HIT_CHART,
1196 .dimension = "percentage"};
1197
1198 static ebpf_systemd_args_t data_dc_references = {
1199 .title = "Count file access",
1200 .units = EBPF_COMMON_UNITS_FILES,
1201 .family = NETDATA_DIRECTORY_CACHE_SUBMENU,
1202 .charttype = NETDATA_EBPF_CHART_TYPE_LINE,
1203 .order = 21201,
1204 .algorithm = EBPF_CHART_ALGORITHM_ABSOLUTE,
1205 .context = NETDATA_SYSTEMD_DC_REFERENCE_CONTEXT,
1206 .module = NETDATA_EBPF_MODULE_NAME_DCSTAT,
1207 .update_every = 0,
1208 .suffix = NETDATA_DC_REFERENCE_CHART,
1209 .dimension = "files"};
1210
1211 static ebpf_systemd_args_t data_dc_not_cache = {
1212 .title = "Files not present inside directory cache",
1213 .units = EBPF_COMMON_UNITS_FILES,
1214 .family = NETDATA_DIRECTORY_CACHE_SUBMENU,
1215 .charttype = NETDATA_EBPF_CHART_TYPE_LINE,
1216 .order = 21202,
1217 .algorithm = EBPF_CHART_ALGORITHM_ABSOLUTE,
1218 .context = NETDATA_SYSTEMD_DC_NOT_CACHE_CONTEXT,
1219 .module = NETDATA_EBPF_MODULE_NAME_DCSTAT,
1220 .update_every = 0,
1221 .suffix = NETDATA_DC_REQUEST_NOT_CACHE_CHART,
1222 .dimension = "files"};
1223
1224 static ebpf_systemd_args_t data_dc_not_found = {
1225 .title = "Files not found",
1226 .units = EBPF_COMMON_UNITS_FILES,
1227 .family = NETDATA_DIRECTORY_CACHE_SUBMENU,
1228 .charttype = NETDATA_EBPF_CHART_TYPE_LINE,
1229 .order = 21203,
1230 .algorithm = EBPF_CHART_ALGORITHM_ABSOLUTE,
1231 .context = NETDATA_SYSTEMD_DC_NOT_CACHE_CONTEXT,
1232 .module = NETDATA_EBPF_MODULE_NAME_DCSTAT,
1233 .update_every = 0,
1234 .suffix = NETDATA_DC_REQUEST_NOT_FOUND_CHART,
1235 .dimension = "files"};
1236
1237 if (!data_dc_not_cache.update_every) {
1238 data_dc_hit_ratio.update_every = update_every;
1239 data_dc_not_cache.update_every = update_every;
1240 data_dc_not_found.update_every = update_every;
1241 data_dc_references.update_every = update_every;
1242 }
1243
1244 ebpf_cgroup_target_t *w;
1245 for (w = ebpf_cgroup_pids; w; w = w->next) {
1246 if (ebpf_plugin_stop())
1247 break;
1248
1249 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_DC_CHART))
1250 continue;
1251
1252 data_dc_hit_ratio.id = data_dc_not_cache.id = data_dc_not_found.id = data_dc_references.id = w->name;
1253 ebpf_create_charts_on_systemd(&data_dc_hit_ratio);
1254
1255 ebpf_create_charts_on_systemd(&data_dc_not_found);
1256
1257 ebpf_create_charts_on_systemd(&data_dc_not_cache);
1258
1259 ebpf_create_charts_on_systemd(&data_dc_references);
1260
1261 w->flags |= NETDATA_EBPF_SERVICES_HAS_DC_CHART;
1262 }
1263 }
1264
1265 /**
1266 * Send Directory Cache charts
1267 *
1268 * Send collected data to Netdata.
1269 */
1270 static void ebpf_send_systemd_dc_charts()
1271 {
1272 ebpf_cgroup_target_t *ect;
1273 collected_number value;
1274 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1275 if (ebpf_plugin_stop())
1276 break;
1277
1278 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_DC_CHART))) {
1279 continue;
1280 }
1281
1282 ebpf_write_begin_chart(ect->name, NETDATA_DC_HIT_CHART, "");
1283 write_chart_dimension("percentage", (long long)ect->publish_dc.ratio);
1284 ebpf_write_end_chart();
1285
1286 ebpf_write_begin_chart(ect->name, NETDATA_DC_REFERENCE_CHART, "");
1287 write_chart_dimension("files", (long long)ect->publish_dc.cache_access);
1288 ebpf_write_end_chart();
1289
1290 ebpf_write_begin_chart(ect->name, NETDATA_DC_REQUEST_NOT_CACHE_CHART, "");
1291 value = (collected_number)(!ect->publish_dc.cache_access) ?
1292 0 :
1293 (long long)ect->publish_dc.curr.file_system - (long long)ect->publish_dc.prev.file_system;
1294 ect->publish_dc.prev.file_system = ect->publish_dc.curr.file_system;
1295 write_chart_dimension("files", (long long)value);
1296 ebpf_write_end_chart();
1297
1298 ebpf_write_begin_chart(ect->name, NETDATA_DC_REQUEST_NOT_FOUND_CHART, "");
1299 value = (collected_number)(!ect->publish_dc.cache_access) ?
1300 0 :
1301 (long long)ect->publish_dc.curr.not_found - (long long)ect->publish_dc.prev.not_found;
1302
1303 ect->publish_dc.prev.not_found = ect->publish_dc.curr.not_found;
1304
1305 write_chart_dimension("files", (long long)value);
1306 ebpf_write_end_chart();
1307 }
1308 }
1309
1310 /**
1311 * Send Directory Cache charts
1312 *
1313 * Send collected data to Netdata.
1314 *
1315 */
1316 static void ebpf_send_specific_dc_data(char *type, netdata_publish_dcstat_t *pdc)
1317 {
1318 collected_number value;
1319 ebpf_write_begin_chart(type, NETDATA_DC_HIT_CHART, "");
1320 write_chart_dimension(dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_RATIO].name, (long long)pdc->ratio);
1321 ebpf_write_end_chart();
1322
1323 ebpf_write_begin_chart(type, NETDATA_DC_REFERENCE_CHART, "");
1324 write_chart_dimension(
1325 dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_REFERENCE].name, (long long)pdc->cache_access);
1326 ebpf_write_end_chart();
1327
1328 value = (collected_number)(!pdc->cache_access) ?
1329 0 :
1330 (long long)pdc->curr.file_system - (long long)pdc->prev.file_system;
1331 pdc->prev.file_system = pdc->curr.file_system;
1332
1333 ebpf_write_begin_chart(type, NETDATA_DC_REQUEST_NOT_CACHE_CHART, "");
1334 write_chart_dimension(dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_SLOW].name, (long long)value);
1335 ebpf_write_end_chart();
1336
1337 value =
1338 (collected_number)(!pdc->cache_access) ? 0 : (long long)pdc->curr.not_found - (long long)pdc->prev.not_found;
1339 pdc->prev.not_found = pdc->curr.not_found;
1340
1341 ebpf_write_begin_chart(type, NETDATA_DC_REQUEST_NOT_FOUND_CHART, "");
1342 write_chart_dimension(dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_MISS].name, (long long)value);
1343 ebpf_write_end_chart();
1344 }
1345
1346 /**
1347 * Send data to Netdata calling auxiliary functions.
1348 *
1349 * @param update_every value to overwrite the update frequency set by the server.
1350 */
1351 void ebpf_dc_send_cgroup_data(int update_every)
1352 {
1353 netdata_mutex_lock(&mutex_cgroup_shm);
1354 ebpf_cgroup_target_t *ect;
1355 ebpf_dc_calc_chart_values();
1356
1357 if (ebpf_cgroup_systemd_enabled_get()) {
1358 if (ebpf_send_cgroup_chart_get()) {
1359 ebpf_create_systemd_dc_charts(update_every);
1360 }
1361
1362 ebpf_send_systemd_dc_charts();
1363 }
1364
1365 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1366 if (ebpf_plugin_stop())
1367 break;
1368
1369 if (ect->systemd)
1370 continue;
1371
1372 if (!(ect->flags & NETDATA_EBPF_CGROUP_HAS_DC_CHART) && ect->updated) {
1373 ebpf_create_specific_dc_charts(ect->name, update_every);
1374 ect->flags |= NETDATA_EBPF_CGROUP_HAS_DC_CHART;
1375 }
1376
1377 if (ect->flags & NETDATA_EBPF_CGROUP_HAS_DC_CHART) {
1378 if (ect->updated) {
1379 ebpf_send_specific_dc_data(ect->name, &ect->publish_dc);
1380 } else {
1381 ebpf_obsolete_specific_dc_charts(ect->name, update_every);
1382 ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_DC_CHART;
1383 }
1384 }
1385 }
1386
1387 netdata_mutex_unlock(&mutex_cgroup_shm);
1388 }
1389
1390 /**
1391 * Main loop for this collector.
1392 */
1393 static void dcstat_collector(ebpf_module_t *em)
1394 {
1395 netdata_publish_dcstat_t publish;
1396 memset(&publish, 0, sizeof(publish));
1397 int cgroups = em->cgroup_charts;
1398 int update_every = em->update_every;
1399 heartbeat_t hb;
1400 heartbeat_init(&hb, USEC_PER_SEC);
1401 int counter = update_every - 1;
1402 int maps_per_core = em->maps_per_core;
1403 uint32_t running_time = 0;
1404 uint32_t lifetime = em->lifetime;
1405 while (!ebpf_plugin_stop() && running_time < lifetime) {
1406 if (ebpf_plugin_stop())
1407 break;
1408
1409 heartbeat_next(&hb);
1410
1411 if (ebpf_plugin_stop())
1412 break;
1413
1414 if (++counter != update_every)
1415 continue;
1416
1417 counter = 0;
1418 netdata_apps_integration_flags_t apps = em->apps_charts;
1419 ebpf_dc_read_global_tables(maps_per_core);
1420
1421 netdata_mutex_lock(&lock);
1422
1423 dcstat_send_global(&publish);
1424
1425 if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
1426 ebpf_dcache_send_apps_data(apps_groups_root_target);
1427
1428 if (ebpf_plugin_stop()) {
1429 netdata_mutex_unlock(&lock);
1430 break;
1431 }
1432
1433 if (cgroups && ebpf_cgroup_integration_active_get())
1434 ebpf_dc_send_cgroup_data(update_every);
1435
1436 netdata_mutex_unlock(&lock);
1437
1438 if (ebpf_plugin_stop())
1439 break;
1440
1441 netdata_mutex_lock(&ebpf_exit_cleanup);
1442 if (running_time)
1443 running_time += update_every;
1444 else
1445 running_time = update_every;
1446
1447 em->running_time = running_time;
1448 netdata_mutex_unlock(&ebpf_exit_cleanup);
1449 }
1450 }
1451
1452 /*****************************************************************
1453 *
1454 * INITIALIZE THREAD
1455 *
1456 *****************************************************************/
1457
1458 /**
1459 * Create filesystem charts
1460 *
1461 * Call ebpf_create_chart to create the charts for the collector.
1462 *
1463 * @param update_every value to overwrite the update frequency set by the server.
1464 */
1465 static void ebpf_create_dc_global_charts(int update_every)
1466 {
1467 ebpf_create_chart(
1468 NETDATA_FILESYSTEM_FAMILY,
1469 NETDATA_DC_HIT_CHART,
1470 "Percentage of files inside directory cache",
1471 EBPF_COMMON_UNITS_PERCENTAGE,
1472 NETDATA_DIRECTORY_CACHE_SUBMENU,
1473 NETDATA_FS_DC_HIT_RATIO_CONTEXT,
1474 NETDATA_EBPF_CHART_TYPE_LINE,
1475 21200,
1476 ebpf_create_global_dimension,
1477 dcstat_counter_publish_aggregated,
1478 1,
1479 update_every,
1480 NETDATA_EBPF_MODULE_NAME_DCSTAT);
1481
1482 ebpf_create_chart(
1483 NETDATA_FILESYSTEM_FAMILY,
1484 NETDATA_DC_REFERENCE_CHART,
1485 "Variables used to calculate hit ratio.",
1486 EBPF_COMMON_UNITS_FILES,
1487 NETDATA_DIRECTORY_CACHE_SUBMENU,
1488 NETDATA_FS_DC_REFERENCE_CONTEXT,
1489 NETDATA_EBPF_CHART_TYPE_LINE,
1490 21201,
1491 ebpf_create_global_dimension,
1492 &dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_REFERENCE],
1493 3,
1494 update_every,
1495 NETDATA_EBPF_MODULE_NAME_DCSTAT);
1496
1497 fflush(stdout);
1498 }
1499
1500 /**
1501 * Allocate vectors used with this thread.
1502 *
1503 * We are not testing the return, because callocz does this and shutdown the software
1504 * case it was not possible to allocate.
1505 */
1506 static void ebpf_dcstat_allocate_global_vectors()
1507 {
1508 dcstat_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_dcstat_pid_t));
1509 dcstat_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
1510
1511 memset(dcstat_counter_aggregated_data, 0, NETDATA_DCSTAT_IDX_END * sizeof(netdata_syscall_stat_t));
1512 memset(dcstat_counter_publish_aggregated, 0, NETDATA_DCSTAT_IDX_END * sizeof(netdata_publish_syscall_t));
1513 }
1514
1515 /*****************************************************************
1516 *
1517 * MAIN THREAD
1518 *
1519 *****************************************************************/
1520
1521 /*
1522 * Load BPF
1523 *
1524 * Load BPF files.
1525 *
1526 * @param em the structure with configuration
1527 */
1528 static int ebpf_dcstat_load_bpf(ebpf_module_t *em)
1529 {
1530 #ifdef LIBBPF_MAJOR_VERSION
1531 ebpf_define_map_type(dcstat_maps, em->maps_per_core, running_on_kernel);
1532 #endif
1533
1534 int ret = 0;
1535 ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_DC_TARGET_LOOKUP_FAST].mode);
1536 if (em->load & EBPF_LOAD_LEGACY) {
1537 em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
1538 if (!em->probe_links) {
1539 ret = -1;
1540 }
1541 }
1542 #ifdef LIBBPF_MAJOR_VERSION
1543 else {
1544 dc_bpf_obj = dc_bpf__open();
1545 if (!dc_bpf_obj)
1546 ret = -1;
1547 else {
1548 ret = ebpf_dc_load_and_attach(dc_bpf_obj, em);
1549 if (ret) {
1550 dc_bpf__destroy(dc_bpf_obj);
1551 dc_bpf_obj = NULL;
1552 }
1553 }
1554 }
1555 #endif
1556
1557 if (ret)
1558 netdata_log_error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->info.thread_name);
1559
1560 return ret;
1561 }
1562
1563 /**
1564 * Directory Cache thread
1565 *
1566 * Thread used to make dcstat thread
1567 *
1568 * @param ptr a pointer to `struct ebpf_module`
1569 *
1570 * @return It always returns NULL
1571 */
1572 void ebpf_dcstat_thread(void *ptr)
1573 {
1574 ebpf_module_t *em = (ebpf_module_t *)ptr;
1575 CLEANUP_FUNCTION_REGISTER(ebpf_dcstat_exit) cleanup_ptr = em;
1576
1577 if (!ebpf_module_thread_has_valid_state(em)) {
1578 goto enddcstat;
1579 }
1580
1581 em->maps = dcstat_maps;
1582
1583 ebpf_update_pid_table(&dcstat_maps[NETDATA_DCSTAT_PID_STATS], em);
1584
1585 ebpf_update_names(dc_optional_name, em);
1586
1587 #ifdef LIBBPF_MAJOR_VERSION
1588 ebpf_adjust_thread_load(em, default_btf);
1589 #endif
1590 if (ebpf_dcstat_load_bpf(em)) {
1591 goto enddcstat;
1592 }
1593 ebpf_mark_program_loaded();
1594
1595 ebpf_dcstat_allocate_global_vectors();
1596
1597 int algorithms[NETDATA_DCSTAT_IDX_END] = {
1598 NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_ABSOLUTE_IDX};
1599
1600 ebpf_global_labels(
1601 dcstat_counter_aggregated_data,
1602 dcstat_counter_publish_aggregated,
1603 dcstat_counter_dimension_name,
1604 dcstat_counter_dimension_name,
1605 algorithms,
1606 NETDATA_DCSTAT_IDX_END);
1607
1608 netdata_mutex_lock(&lock);
1609 ebpf_create_dc_global_charts(em->update_every);
1610 ebpf_update_stats(&plugin_statistics, em);
1611 ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_ADD);
1612
1613 netdata_mutex_unlock(&lock);
1614
1615 ebpf_read_dcstat.thread =
1616 nd_thread_create(ebpf_read_dcstat.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_dcstat_thread, em);
1617
1618 dcstat_collector(em);
1619
1620 enddcstat:
1621 ebpf_update_disabled_plugin_stats(em);
1622 }