master
c 552 lines 15.3 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf.h"
4 #include "ebpf_mount.h"
5 #include "libbpf_api/ebpf_library.h"
6
7 static ebpf_local_maps_t mount_maps[] = {
8 {.name = "tbl_mount",
9 .internal_input = NETDATA_MOUNT_END,
10 .user_input = 0,
11 .type = NETDATA_EBPF_MAP_STATIC,
12 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
13 #ifdef LIBBPF_MAJOR_VERSION
14 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
15 #endif
16 },
17 {.name = NULL,
18 .internal_input = 0,
19 .user_input = 0,
20 .type = NETDATA_EBPF_MAP_CONTROLLER,
21 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
22 #ifdef LIBBPF_MAJOR_VERSION
23 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
24 #endif
25 }};
26
27 static char *mount_dimension_name[NETDATA_EBPF_MOUNT_SYSCALL] = {"mount", "umount"};
28 static netdata_syscall_stat_t mount_aggregated_data[NETDATA_EBPF_MOUNT_SYSCALL];
29 static netdata_publish_syscall_t mount_publish_aggregated[NETDATA_EBPF_MOUNT_SYSCALL];
30
31 struct config mount_config = APPCONFIG_INITIALIZER;
32
33 static netdata_idx_t mount_hash_values[NETDATA_MOUNT_END];
34
35 netdata_ebpf_targets_t mount_targets[] = {
36 {.name = "mount", .mode = EBPF_LOAD_TRAMPOLINE},
37 {.name = "umount", .mode = EBPF_LOAD_TRAMPOLINE},
38 {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
39
40 #ifdef LIBBPF_MAJOR_VERSION
41 /*****************************************************************
42 *
43 * BTF FUNCTIONS
44 *
45 *****************************************************************/
46
47 /*
48 * Disable probe
49 *
50 * Disable all probes to use exclusively another method.
51 *
52 * @param obj is the main structure for bpf objects.
53 */
54 static inline void ebpf_mount_disable_probe(struct mount_bpf *obj)
55 {
56 bpf_program__set_autoload(obj->progs.netdata_mount_probe, false);
57 bpf_program__set_autoload(obj->progs.netdata_umount_probe, false);
58
59 bpf_program__set_autoload(obj->progs.netdata_mount_retprobe, false);
60 bpf_program__set_autoload(obj->progs.netdata_umount_retprobe, false);
61 }
62
63 /*
64 * Disable tracepoint
65 *
66 * Disable all tracepoints to use exclusively another method.
67 *
68 * @param obj is the main structure for bpf objects.
69 */
70 static inline void ebpf_mount_disable_tracepoint(struct mount_bpf *obj)
71 {
72 bpf_program__set_autoload(obj->progs.netdata_mount_exit, false);
73 bpf_program__set_autoload(obj->progs.netdata_umount_exit, false);
74 }
75
76 /*
77 * Disable trampoline
78 *
79 * Disable all trampoline to use exclusively another method.
80 *
81 * @param obj is the main structure for bpf objects.
82 */
83 static inline void ebpf_mount_disable_trampoline(struct mount_bpf *obj)
84 {
85 bpf_program__set_autoload(obj->progs.netdata_mount_fentry, false);
86 bpf_program__set_autoload(obj->progs.netdata_umount_fentry, false);
87 bpf_program__set_autoload(obj->progs.netdata_mount_fexit, false);
88 bpf_program__set_autoload(obj->progs.netdata_umount_fexit, false);
89 }
90
91 /**
92 * Set trampoline target
93 *
94 * Set the targets we will monitor.
95 *
96 * @param obj is the main structure for bpf objects.
97 */
98 static inline void netdata_set_trampoline_target(struct mount_bpf *obj)
99 {
100 char syscall[NETDATA_EBPF_MAX_SYSCALL_LENGTH + 1];
101 ebpf_select_host_prefix(
102 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, mount_targets[NETDATA_MOUNT_SYSCALL].name, running_on_kernel);
103
104 bpf_program__set_attach_target(obj->progs.netdata_mount_fentry, 0, syscall);
105
106 bpf_program__set_attach_target(obj->progs.netdata_mount_fexit, 0, syscall);
107
108 ebpf_select_host_prefix(
109 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, mount_targets[NETDATA_UMOUNT_SYSCALL].name, running_on_kernel);
110
111 bpf_program__set_attach_target(obj->progs.netdata_umount_fentry, 0, syscall);
112
113 bpf_program__set_attach_target(obj->progs.netdata_umount_fexit, 0, syscall);
114 }
115
116 /**
117 * Mount Attach Probe
118 *
119 * Attach probes to target
120 *
121 * @param obj is the main structure for bpf objects.
122 *
123 * @return It returns 0 on success and -1 otherwise.
124 */
125 static int ebpf_mount_attach_probe(struct mount_bpf *obj)
126 {
127 char syscall[NETDATA_EBPF_MAX_SYSCALL_LENGTH + 1];
128
129 ebpf_select_host_prefix(
130 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, mount_targets[NETDATA_MOUNT_SYSCALL].name, running_on_kernel);
131
132 obj->links.netdata_mount_probe = bpf_program__attach_kprobe(obj->progs.netdata_mount_probe, false, syscall);
133 int ret = (int)libbpf_get_error(obj->links.netdata_mount_probe);
134 if (ret)
135 return -1;
136
137 obj->links.netdata_mount_retprobe = bpf_program__attach_kprobe(obj->progs.netdata_mount_retprobe, true, syscall);
138 ret = (int)libbpf_get_error(obj->links.netdata_mount_retprobe);
139 if (ret)
140 return -1;
141
142 ebpf_select_host_prefix(
143 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, mount_targets[NETDATA_UMOUNT_SYSCALL].name, running_on_kernel);
144
145 obj->links.netdata_umount_probe = bpf_program__attach_kprobe(obj->progs.netdata_umount_probe, false, syscall);
146 ret = (int)libbpf_get_error(obj->links.netdata_umount_probe);
147 if (ret)
148 return -1;
149
150 obj->links.netdata_umount_retprobe = bpf_program__attach_kprobe(obj->progs.netdata_umount_retprobe, true, syscall);
151 ret = (int)libbpf_get_error(obj->links.netdata_umount_retprobe);
152 if (ret)
153 return -1;
154
155 return 0;
156 }
157
158 /**
159 * Set hash tables
160 *
161 * Set the values for maps according the value given by kernel.
162 *
163 * @param obj is the main structure for bpf objects.
164 */
165 static void ebpf_mount_set_hash_tables(struct mount_bpf *obj)
166 {
167 mount_maps[NETDATA_KEY_MOUNT_TABLE].map_fd = bpf_map__fd(obj->maps.tbl_mount);
168 }
169
170 /**
171 * Load and attach
172 *
173 * Load and attach the eBPF code in kernel.
174 *
175 * @param obj is the main structure for bpf objects.
176 * @param em structure with configuration
177 *
178 * @return it returns 0 on success and -1 otherwise
179 */
180 static inline int ebpf_mount_load_and_attach(struct mount_bpf *obj, ebpf_module_t *em)
181 {
182 netdata_ebpf_program_loaded_t mode = em->targets[NETDATA_MOUNT_SYSCALL].mode;
183
184 // We are testing only one, because all will have the same behavior
185 if (mode == EBPF_LOAD_TRAMPOLINE) {
186 ebpf_mount_disable_probe(obj);
187 ebpf_mount_disable_tracepoint(obj);
188
189 netdata_set_trampoline_target(obj);
190 } else if (mode == EBPF_LOAD_PROBE || mode == EBPF_LOAD_RETPROBE) {
191 ebpf_mount_disable_tracepoint(obj);
192 ebpf_mount_disable_trampoline(obj);
193 } else {
194 ebpf_mount_disable_probe(obj);
195 ebpf_mount_disable_trampoline(obj);
196 }
197
198 ebpf_update_map_type(obj->maps.tbl_mount, &mount_maps[NETDATA_KEY_MOUNT_TABLE]);
199
200 int ret = mount_bpf__load(obj);
201 if (!ret) {
202 if (mode != EBPF_LOAD_PROBE && mode != EBPF_LOAD_RETPROBE)
203 ret = mount_bpf__attach(obj);
204 else
205 ret = ebpf_mount_attach_probe(obj);
206
207 if (!ret)
208 ebpf_mount_set_hash_tables(obj);
209 }
210
211 return ret;
212 }
213 #endif
214 /*****************************************************************
215 *
216 * FUNCTIONS TO CLOSE THE THREAD
217 *
218 *****************************************************************/
219
220 /**
221 * Obsolete global
222 *
223 * Obsolete global charts created by thread.
224 *
225 * @param em a pointer to `struct ebpf_module`
226 */
227 static void ebpf_obsolete_mount_global(ebpf_module_t *em)
228 {
229 ebpf_write_chart_obsolete(
230 NETDATA_EBPF_MOUNT_GLOBAL_FAMILY,
231 NETDATA_EBPF_MOUNT_CALLS,
232 "",
233 "Calls to mount and umount syscalls",
234 EBPF_COMMON_UNITS_CALLS_PER_SEC,
235 NETDATA_EBPF_MOUNT_FAMILY,
236 NETDATA_EBPF_CHART_TYPE_LINE,
237 "mount_points.call",
238 NETDATA_CHART_PRIO_EBPF_MOUNT_CHARTS,
239 em->update_every);
240
241 ebpf_write_chart_obsolete(
242 NETDATA_EBPF_MOUNT_GLOBAL_FAMILY,
243 NETDATA_EBPF_MOUNT_ERRORS,
244 "",
245 "Errors to mount and umount file systems",
246 EBPF_COMMON_UNITS_CALLS_PER_SEC,
247 NETDATA_EBPF_MOUNT_FAMILY,
248 NETDATA_EBPF_CHART_TYPE_LINE,
249 "mount_points.error",
250 NETDATA_CHART_PRIO_EBPF_MOUNT_CHARTS + 1,
251 em->update_every);
252 }
253
254 /**
255 * Mount Exit
256 *
257 * Cancel child thread.
258 *
259 * @param ptr thread data.
260 */
261 void ebpf_mount_unload_bpf(ebpf_module_t *em)
262 {
263 #ifdef LIBBPF_MAJOR_VERSION
264 if (mount_bpf_obj) {
265 mount_bpf__destroy(mount_bpf_obj);
266 mount_bpf_obj = NULL;
267 }
268 #endif
269 if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
270 ebpf_unload_legacy_code(em->objects, em->probe_links);
271 em->objects = NULL;
272 em->probe_links = NULL;
273 }
274 }
275
276 static void ebpf_mount_exit(void *pptr)
277 {
278 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
279 if (!em)
280 return;
281
282 if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
283 netdata_mutex_lock(&lock);
284
285 ebpf_obsolete_mount_global(em);
286
287 fflush(stdout);
288 netdata_mutex_unlock(&lock);
289 }
290
291 if (!ebpf_plugin_stop() && em->functions.bpf_unload)
292 em->functions.bpf_unload(em);
293
294 netdata_mutex_lock(&ebpf_exit_cleanup);
295 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
296 netdata_mutex_unlock(&ebpf_exit_cleanup);
297 }
298
299 /*****************************************************************
300 *
301 * MAIN LOOP
302 *
303 *****************************************************************/
304
305 /**
306 * Read global table
307 *
308 * Read the table with number of calls for all functions
309 *
310 * @param maps_per_core do I need to read all cores?
311 */
312 static void ebpf_mount_read_global_table(int maps_per_core)
313 {
314 static netdata_idx_t *mount_values = NULL;
315 if (!mount_values)
316 mount_values = callocz((size_t)ebpf_nprocs + 1, sizeof(netdata_idx_t));
317
318 uint32_t idx;
319 netdata_idx_t *val = mount_hash_values;
320 netdata_idx_t *stored = mount_values;
321 size_t length = sizeof(netdata_idx_t);
322 if (maps_per_core)
323 length *= ebpf_nprocs;
324
325 int fd = mount_maps[NETDATA_KEY_MOUNT_TABLE].map_fd;
326
327 for (idx = NETDATA_KEY_MOUNT_CALL; idx < NETDATA_MOUNT_END; idx++) {
328 if (bpf_map_lookup_elem(fd, &idx, stored) == 0) {
329 int i;
330 int end = (maps_per_core) ? ebpf_nprocs : 1;
331 netdata_idx_t total = 0;
332 for (i = 0; i < end; i++)
333 total += stored[i];
334
335 val[idx] = total;
336 memset(stored, 0, length);
337 }
338 }
339 }
340
341 /**
342 * Send data to Netdata calling auxiliary functions.
343 */
344 static void ebpf_mount_send_data()
345 {
346 int i;
347 int end = NETDATA_EBPF_MOUNT_SYSCALL;
348 for (i = 0; i < end; i++) {
349 mount_publish_aggregated[i].ncall = mount_hash_values[i];
350 mount_publish_aggregated[i].nerr = mount_hash_values[i + NETDATA_EBPF_MOUNT_SYSCALL];
351 }
352
353 write_count_chart(
354 NETDATA_EBPF_MOUNT_CALLS,
355 NETDATA_EBPF_MOUNT_GLOBAL_FAMILY,
356 mount_publish_aggregated,
357 NETDATA_EBPF_MOUNT_SYSCALL);
358
359 write_err_chart(
360 NETDATA_EBPF_MOUNT_ERRORS,
361 NETDATA_EBPF_MOUNT_GLOBAL_FAMILY,
362 mount_publish_aggregated,
363 NETDATA_EBPF_MOUNT_SYSCALL);
364 }
365
366 /**
367 * Main loop for this collector.
368 */
369 static void mount_collector(ebpf_module_t *em)
370 {
371 memset(mount_hash_values, 0, sizeof(mount_hash_values));
372
373 int update_every = em->update_every;
374 int counter = update_every - 1;
375 int maps_per_core = em->maps_per_core;
376 uint32_t running_time = 0;
377 uint32_t lifetime = em->lifetime;
378 heartbeat_t hb;
379 heartbeat_init(&hb, USEC_PER_SEC);
380 while (!ebpf_plugin_stop() && running_time < lifetime) {
381 if (ebpf_plugin_stop())
382 break;
383
384 heartbeat_next(&hb);
385 if (ebpf_plugin_stop())
386 break;
387
388 if (++counter != update_every)
389 continue;
390
391 counter = 0;
392 ebpf_mount_read_global_table(maps_per_core);
393 netdata_mutex_lock(&lock);
394
395 ebpf_mount_send_data();
396
397 netdata_mutex_unlock(&lock);
398
399 if (ebpf_plugin_stop())
400 break;
401
402 netdata_mutex_lock(&ebpf_exit_cleanup);
403 running_time += update_every;
404 em->running_time = running_time;
405 netdata_mutex_unlock(&ebpf_exit_cleanup);
406 }
407 }
408
409 /*****************************************************************
410 *
411 * INITIALIZE THREAD
412 *
413 *****************************************************************/
414
415 /**
416 * Create mount charts
417 *
418 * Call ebpf_create_chart to create the charts for the collector.
419 *
420 * @param update_every value to overwrite the update frequency set by the server.
421 */
422 static void ebpf_create_mount_charts(int update_every)
423 {
424 ebpf_create_chart(
425 NETDATA_EBPF_MOUNT_GLOBAL_FAMILY,
426 NETDATA_EBPF_MOUNT_CALLS,
427 "Calls to mount and umount syscalls",
428 EBPF_COMMON_UNITS_CALLS_PER_SEC,
429 NETDATA_EBPF_MOUNT_FAMILY,
430 "mount_points.call",
431 NETDATA_EBPF_CHART_TYPE_LINE,
432 NETDATA_CHART_PRIO_EBPF_MOUNT_CHARTS,
433 ebpf_create_global_dimension,
434 mount_publish_aggregated,
435 NETDATA_EBPF_MOUNT_SYSCALL,
436 update_every,
437 NETDATA_EBPF_MODULE_NAME_MOUNT);
438
439 ebpf_create_chart(
440 NETDATA_EBPF_MOUNT_GLOBAL_FAMILY,
441 NETDATA_EBPF_MOUNT_ERRORS,
442 "Errors to mount and umount file systems",
443 EBPF_COMMON_UNITS_CALLS_PER_SEC,
444 NETDATA_EBPF_MOUNT_FAMILY,
445 "mount_points.error",
446 NETDATA_EBPF_CHART_TYPE_LINE,
447 NETDATA_CHART_PRIO_EBPF_MOUNT_CHARTS + 1,
448 ebpf_create_global_dimension,
449 mount_publish_aggregated,
450 NETDATA_EBPF_MOUNT_SYSCALL,
451 update_every,
452 NETDATA_EBPF_MODULE_NAME_MOUNT);
453
454 fflush(stdout);
455 }
456
457 /*****************************************************************
458 *
459 * MAIN THREAD
460 *
461 *****************************************************************/
462
463 /*
464 * Load BPF
465 *
466 * Load BPF files.
467 *
468 * @param em the structure with configuration
469 */
470 static int ebpf_mount_load_bpf(ebpf_module_t *em)
471 {
472 #ifdef LIBBPF_MAJOR_VERSION
473 ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
474 #endif
475
476 int ret = 0;
477 if (em->load & EBPF_LOAD_LEGACY) {
478 em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
479 if (!em->probe_links) {
480 ret = -1;
481 }
482 }
483 #ifdef LIBBPF_MAJOR_VERSION
484 else {
485 mount_bpf_obj = mount_bpf__open();
486 if (!mount_bpf_obj)
487 ret = -1;
488 else {
489 ret = ebpf_mount_load_and_attach(mount_bpf_obj, em);
490 if (ret) {
491 mount_bpf__destroy(mount_bpf_obj);
492 mount_bpf_obj = NULL;
493 }
494 }
495 }
496 #endif
497
498 if (ret)
499 netdata_log_error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->info.thread_name);
500
501 return ret;
502 }
503
504 /**
505 * Mount thread
506 *
507 * Thread used to make mount thread
508 *
509 * @param ptr a pointer to `struct ebpf_module`
510 *
511 * @return It always returns NULL
512 */
513 void ebpf_mount_thread(void *ptr)
514 {
515 ebpf_module_t *em = ptr;
516 CLEANUP_FUNCTION_REGISTER(ebpf_mount_exit) cleanup_ptr = em;
517
518 if (!ebpf_module_thread_has_valid_state(em)) {
519 goto endmount;
520 }
521
522 em->maps = mount_maps;
523
524 #ifdef LIBBPF_MAJOR_VERSION
525 ebpf_adjust_thread_load(em, default_btf);
526 #endif
527 if (ebpf_mount_load_bpf(em)) {
528 goto endmount;
529 }
530 ebpf_mark_program_loaded();
531
532 int algorithms[NETDATA_EBPF_MOUNT_SYSCALL] = {NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX};
533
534 ebpf_global_labels(
535 mount_aggregated_data,
536 mount_publish_aggregated,
537 mount_dimension_name,
538 mount_dimension_name,
539 algorithms,
540 NETDATA_EBPF_MOUNT_SYSCALL);
541
542 netdata_mutex_lock(&lock);
543 ebpf_create_mount_charts(em->update_every);
544 ebpf_update_stats(&plugin_statistics, em);
545 ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_ADD);
546 netdata_mutex_unlock(&lock);
547
548 mount_collector(em);
549
550 endmount:
551 ebpf_update_disabled_plugin_stats(em);
552 }