master
c 1,244 lines 39.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf_filesystem.h"
4 #include "libbpf_api/ebpf_library.h"
5
6 struct config fs_config = APPCONFIG_INITIALIZER;
7
8 ebpf_local_maps_t ext4_maps[] = {
9 {.name = "tbl_ext4",
10 .internal_input = NETDATA_KEY_CALLS_SYNC,
11 .user_input = 0,
12 .type = NETDATA_EBPF_MAP_STATIC,
13 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
14 #ifdef LIBBPF_MAJOR_VERSION
15 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
16 #endif
17 },
18 {.name = "tmp_ext4",
19 .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
20 .user_input = NETDATA_FS_TEMP_MAP_SIZE,
21 .type = NETDATA_EBPF_MAP_CONTROLLER,
22 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
23 #ifdef LIBBPF_MAJOR_VERSION
24 .map_type = BPF_MAP_TYPE_PERCPU_HASH
25 #endif
26 },
27 {.name = NULL,
28 .internal_input = 0,
29 .user_input = 0,
30 .type = NETDATA_EBPF_MAP_CONTROLLER,
31 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
32 #ifdef LIBBPF_MAJOR_VERSION
33 .map_type = BPF_MAP_TYPE_PERCPU_HASH
34 #endif
35 }};
36
37 ebpf_local_maps_t xfs_maps[] = {
38 {.name = "tbl_xfs",
39 .internal_input = NETDATA_KEY_CALLS_SYNC,
40 .user_input = 0,
41 .type = NETDATA_EBPF_MAP_STATIC,
42 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
43 #ifdef LIBBPF_MAJOR_VERSION
44 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
45 #endif
46 },
47 {.name = "tmp_xfs",
48 .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
49 .user_input = NETDATA_FS_TEMP_MAP_SIZE,
50 .type = NETDATA_EBPF_MAP_CONTROLLER,
51 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
52 #ifdef LIBBPF_MAJOR_VERSION
53 .map_type = BPF_MAP_TYPE_PERCPU_HASH
54 #endif
55 },
56 {.name = NULL,
57 .internal_input = 0,
58 .user_input = 0,
59 .type = NETDATA_EBPF_MAP_CONTROLLER,
60 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
61 #ifdef LIBBPF_MAJOR_VERSION
62 .map_type = BPF_MAP_TYPE_PERCPU_HASH
63 #endif
64 }};
65
66 ebpf_local_maps_t nfs_maps[] = {
67 {.name = "tbl_nfs",
68 .internal_input = NETDATA_KEY_CALLS_SYNC,
69 .user_input = 0,
70 .type = NETDATA_EBPF_MAP_STATIC,
71 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
72 #ifdef LIBBPF_MAJOR_VERSION
73 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
74 #endif
75 },
76 {.name = "tmp_nfs",
77 .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
78 .user_input = NETDATA_FS_TEMP_MAP_SIZE,
79 .type = NETDATA_EBPF_MAP_CONTROLLER,
80 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
81 #ifdef LIBBPF_MAJOR_VERSION
82 .map_type = BPF_MAP_TYPE_PERCPU_HASH
83 #endif
84 },
85 {.name = NULL,
86 .internal_input = 0,
87 .user_input = 0,
88 .type = NETDATA_EBPF_MAP_CONTROLLER,
89 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
90 #ifdef LIBBPF_MAJOR_VERSION
91 .map_type = BPF_MAP_TYPE_PERCPU_HASH
92 #endif
93 }};
94
95 ebpf_local_maps_t zfs_maps[] = {
96 {.name = "tbl_zfs",
97 .internal_input = NETDATA_KEY_CALLS_SYNC,
98 .user_input = 0,
99 .type = NETDATA_EBPF_MAP_STATIC,
100 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
101 #ifdef LIBBPF_MAJOR_VERSION
102 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
103 #endif
104 },
105 {.name = "tmp_zfs",
106 .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
107 .user_input = NETDATA_FS_TEMP_MAP_SIZE,
108 .type = NETDATA_EBPF_MAP_CONTROLLER,
109 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
110 #ifdef LIBBPF_MAJOR_VERSION
111 .map_type = BPF_MAP_TYPE_PERCPU_HASH
112 #endif
113 },
114 {.name = NULL,
115 .internal_input = 0,
116 .user_input = 0,
117 .type = NETDATA_EBPF_MAP_CONTROLLER,
118 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
119 #ifdef LIBBPF_MAJOR_VERSION
120 .map_type = BPF_MAP_TYPE_PERCPU_HASH
121 #endif
122 }};
123
124 ebpf_local_maps_t btrfs_maps[] = {
125 {.name = "tbl_btrfs",
126 .internal_input = NETDATA_KEY_CALLS_SYNC,
127 .user_input = 0,
128 .type = NETDATA_EBPF_MAP_STATIC,
129 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
130 #ifdef LIBBPF_MAJOR_VERSION
131 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
132 #endif
133 },
134 {.name = "tbl_ext_addr",
135 .internal_input = 1,
136 .user_input = 1,
137 .type = NETDATA_EBPF_MAP_CONTROLLER,
138 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
139 #ifdef LIBBPF_MAJOR_VERSION
140 .map_type = BPF_MAP_TYPE_PERCPU_HASH
141 #endif
142 },
143 {.name = "tmp_btrfs",
144 .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
145 .user_input = NETDATA_FS_TEMP_MAP_SIZE,
146 .type = NETDATA_EBPF_MAP_CONTROLLER,
147 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
148 #ifdef LIBBPF_MAJOR_VERSION
149 .map_type = BPF_MAP_TYPE_PERCPU_HASH
150 #endif
151 },
152 {.name = NULL,
153 .internal_input = 0,
154 .user_input = 0,
155 .type = NETDATA_EBPF_MAP_CONTROLLER,
156 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
157 #ifdef LIBBPF_MAJOR_VERSION
158 .map_type = BPF_MAP_TYPE_PERCPU_HASH
159 #endif
160 }};
161
162 static netdata_syscall_stat_t filesystem_aggregated_data[NETDATA_EBPF_HIST_MAX_BINS];
163 static netdata_publish_syscall_t filesystem_publish_aggregated[NETDATA_EBPF_HIST_MAX_BINS];
164
165 char **dimensions = NULL;
166 static netdata_idx_t *filesystem_hash_values = NULL;
167
168 #ifdef LIBBPF_MAJOR_VERSION
169 /**
170 * FS disable kprobe
171 *
172 * Disable kprobes, because system will use trampolines.
173 * We are not calling this function for while, because we are prioritizing kprobes. We opted by this road, because
174 * distribution are still not deliverying necessary btf files per FS.
175 *
176 * @param obj FS object loaded.
177 */
178 static void ebpf_fs_disable_kprobe(struct filesystem_bpf *obj)
179 {
180 // kprobe
181 bpf_program__set_autoload(obj->progs.netdata_fs_file_read_probe, false);
182 bpf_program__set_autoload(obj->progs.netdata_fs_file_write_probe, false);
183 bpf_program__set_autoload(obj->progs.netdata_fs_file_open_probe, false);
184 bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_probe, false);
185 bpf_program__set_autoload(obj->progs.netdata_fs_getattr_probe, false);
186 // kretprobe
187 bpf_program__set_autoload(obj->progs.netdata_fs_file_read_retprobe, false);
188 bpf_program__set_autoload(obj->progs.netdata_fs_file_write_retprobe, false);
189 bpf_program__set_autoload(obj->progs.netdata_fs_file_open_retprobe, false);
190 bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_retprobe, false);
191 bpf_program__set_autoload(obj->progs.netdata_fs_getattr_retprobe, false);
192 }
193
194 /**
195 * Disable trampoline
196 *
197 * Disable trampolines to use kprobes.
198 *
199 * @param obj FS object loaded.
200 */
201 static void ebpf_fs_disable_trampoline(struct filesystem_bpf *obj)
202 {
203 // entry
204 bpf_program__set_autoload(obj->progs.netdata_fs_file_read_entry, false);
205 bpf_program__set_autoload(obj->progs.netdata_fs_file_write_entry, false);
206 bpf_program__set_autoload(obj->progs.netdata_fs_file_open_entry, false);
207 bpf_program__set_autoload(obj->progs.netdata_fs_getattr_entry, false);
208 bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_entry, false);
209
210 // exit
211 bpf_program__set_autoload(obj->progs.netdata_fs_file_read_exit, false);
212 bpf_program__set_autoload(obj->progs.netdata_fs_file_write_exit, false);
213 bpf_program__set_autoload(obj->progs.netdata_fs_file_open_exit, false);
214 bpf_program__set_autoload(obj->progs.netdata_fs_getattr_exit, false);
215 bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_exit, false);
216 }
217
218 /**
219 * Set targets
220 *
221 * Set targets for each objects.
222 *
223 * @param obj FS object loaded.
224 * @param functions array with function names.
225 */
226 static void ebpf_fs_set_target(struct filesystem_bpf *obj, const char **functions)
227 {
228 // entry
229 bpf_program__set_attach_target(obj->progs.netdata_fs_file_read_entry, 0, functions[NETDATA_KEY_BTF_READ]);
230 bpf_program__set_attach_target(obj->progs.netdata_fs_file_write_entry, 0, functions[NETDATA_KEY_BTF_WRITE]);
231 bpf_program__set_attach_target(obj->progs.netdata_fs_file_open_entry, 0, functions[NETDATA_KEY_BTF_OPEN]);
232 bpf_program__set_attach_target(obj->progs.netdata_fs_getattr_entry, 0, functions[NETDATA_KEY_BTF_SYNC_ATTR]);
233
234 // exit
235 bpf_program__set_attach_target(obj->progs.netdata_fs_file_read_exit, 0, functions[NETDATA_KEY_BTF_READ]);
236 bpf_program__set_attach_target(obj->progs.netdata_fs_file_write_exit, 0, functions[NETDATA_KEY_BTF_WRITE]);
237 bpf_program__set_attach_target(obj->progs.netdata_fs_file_open_exit, 0, functions[NETDATA_KEY_BTF_OPEN]);
238 bpf_program__set_attach_target(obj->progs.netdata_fs_getattr_exit, 0, functions[NETDATA_KEY_BTF_SYNC_ATTR]);
239
240 if (functions[NETDATA_KEY_BTF_OPEN2]) {
241 bpf_program__set_attach_target(obj->progs.netdata_fs_2nd_file_open_entry, 0, functions[NETDATA_KEY_BTF_OPEN2]);
242 bpf_program__set_attach_target(obj->progs.netdata_fs_2nd_file_open_exit, 0, functions[NETDATA_KEY_BTF_OPEN2]);
243 } else {
244 bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_entry, false);
245 bpf_program__set_autoload(obj->progs.netdata_fs_2nd_file_open_exit, false);
246 }
247 }
248
249 /**
250 * Attach Kprobe
251 *
252 * Attach kprobe on targets
253 *
254 * @param obj FS object loaded.
255 * @param functions array with function names.
256 */
257 static int ebpf_fs_attach_kprobe(struct filesystem_bpf *obj, const char **functions)
258 {
259 // kprobe
260 obj->links.netdata_fs_file_read_probe =
261 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_read_probe, false, functions[NETDATA_KEY_BTF_READ]);
262 if (libbpf_get_error(obj->links.netdata_fs_file_read_probe))
263 return -1;
264
265 obj->links.netdata_fs_file_write_probe =
266 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_write_probe, false, functions[NETDATA_KEY_BTF_WRITE]);
267 if (libbpf_get_error(obj->links.netdata_fs_file_write_probe))
268 goto cleanup_file_read;
269
270 obj->links.netdata_fs_file_open_probe =
271 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_open_probe, false, functions[NETDATA_KEY_BTF_OPEN]);
272 if (libbpf_get_error(obj->links.netdata_fs_file_open_probe))
273 goto cleanup_file_write;
274
275 obj->links.netdata_fs_getattr_probe =
276 bpf_program__attach_kprobe(obj->progs.netdata_fs_getattr_probe, false, functions[NETDATA_KEY_BTF_SYNC_ATTR]);
277 if (libbpf_get_error(obj->links.netdata_fs_getattr_probe))
278 goto cleanup_file_open;
279
280 // kretprobe
281 obj->links.netdata_fs_file_read_retprobe =
282 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_read_retprobe, false, functions[NETDATA_KEY_BTF_READ]);
283 if (libbpf_get_error(obj->links.netdata_fs_file_read_retprobe))
284 goto cleanup_getattr;
285
286 obj->links.netdata_fs_file_write_retprobe =
287 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_write_retprobe, false, functions[NETDATA_KEY_BTF_WRITE]);
288 if (libbpf_get_error(obj->links.netdata_fs_file_write_retprobe))
289 goto cleanup_file_read_ret;
290
291 obj->links.netdata_fs_file_open_retprobe =
292 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_open_retprobe, false, functions[NETDATA_KEY_BTF_OPEN]);
293 if (libbpf_get_error(obj->links.netdata_fs_file_open_retprobe))
294 goto cleanup_file_write_ret;
295
296 obj->links.netdata_fs_getattr_retprobe =
297 bpf_program__attach_kprobe(obj->progs.netdata_fs_getattr_retprobe, false, functions[NETDATA_KEY_BTF_SYNC_ATTR]);
298 if (libbpf_get_error(obj->links.netdata_fs_getattr_retprobe))
299 goto cleanup_file_open_ret;
300
301 if (functions[NETDATA_KEY_BTF_OPEN2]) {
302 obj->links.netdata_fs_2nd_file_open_probe = bpf_program__attach_kprobe(
303 obj->progs.netdata_fs_2nd_file_open_probe, false, functions[NETDATA_KEY_BTF_OPEN2]);
304 if (libbpf_get_error(obj->links.netdata_fs_2nd_file_open_probe))
305 goto cleanup_getattr_ret;
306
307 obj->links.netdata_fs_2nd_file_open_retprobe = bpf_program__attach_kprobe(
308 obj->progs.netdata_fs_2nd_file_open_retprobe, false, functions[NETDATA_KEY_BTF_OPEN2]);
309 if (libbpf_get_error(obj->links.netdata_fs_2nd_file_open_retprobe))
310 goto cleanup_2nd_open_probe;
311 }
312
313 return 0;
314
315 cleanup_2nd_open_probe:
316 if (obj->links.netdata_fs_2nd_file_open_probe)
317 bpf_link__destroy(obj->links.netdata_fs_2nd_file_open_probe);
318
319 cleanup_getattr_ret:
320 if (obj->links.netdata_fs_getattr_retprobe)
321 bpf_link__destroy(obj->links.netdata_fs_getattr_retprobe);
322
323 cleanup_file_open_ret:
324 if (obj->links.netdata_fs_file_open_retprobe)
325 bpf_link__destroy(obj->links.netdata_fs_file_open_retprobe);
326
327 cleanup_file_write_ret:
328 if (obj->links.netdata_fs_file_write_retprobe)
329 bpf_link__destroy(obj->links.netdata_fs_file_write_retprobe);
330
331 cleanup_file_read_ret:
332 if (obj->links.netdata_fs_file_read_retprobe)
333 bpf_link__destroy(obj->links.netdata_fs_file_read_retprobe);
334
335 cleanup_getattr:
336 if (obj->links.netdata_fs_getattr_probe)
337 bpf_link__destroy(obj->links.netdata_fs_getattr_probe);
338
339 cleanup_file_open:
340 if (obj->links.netdata_fs_file_open_probe)
341 bpf_link__destroy(obj->links.netdata_fs_file_open_probe);
342
343 cleanup_file_write:
344 if (obj->links.netdata_fs_file_write_probe)
345 bpf_link__destroy(obj->links.netdata_fs_file_write_probe);
346
347 cleanup_file_read:
348 if (obj->links.netdata_fs_file_read_probe)
349 bpf_link__destroy(obj->links.netdata_fs_file_read_probe);
350
351 return -1;
352 }
353
354 /**
355 * Load and Attach
356 *
357 * Load binary and attach to targets.
358 *
359 * @param map Structure with information about maps.
360 * @param obj FS object loaded.
361 * @param functions array with function names.
362 * @param bf sttruct with btf file loaded.
363 */
364 static inline int
365 ebpf_fs_load_and_attach(ebpf_local_maps_t *map, struct filesystem_bpf *obj, const char **functions, struct btf *bf)
366 {
367 if (bf) {
368 ebpf_fs_disable_kprobe(obj);
369 ebpf_fs_set_target(obj, functions);
370 } else {
371 ebpf_fs_disable_trampoline(obj);
372 }
373
374 int ret = filesystem_bpf__load(obj);
375 if (ret) {
376 fprintf(stderr, "failed to load BPF object: %d\n", ret);
377 return -1;
378 }
379
380 if (bf)
381 ret = filesystem_bpf__attach(obj);
382 else
383 ret = ebpf_fs_attach_kprobe(obj, functions);
384
385 if (!ret)
386 map->map_fd = bpf_map__fd(obj->maps.tbl_fs);
387
388 return ret;
389 }
390 #endif
391
392 /*****************************************************************
393 *
394 * COMMON FUNCTIONS
395 *
396 *****************************************************************/
397
398 static void ebpf_cleanup_fs_histogram(netdata_ebpf_histogram_t *hist)
399 {
400 freez(hist->name);
401 hist->name = NULL;
402 freez(hist->title);
403 hist->title = NULL;
404 freez(hist->ctx);
405 hist->ctx = NULL;
406 }
407
408 static void ebpf_cleanup_fs_partition(ebpf_filesystem_partitions_t *efp)
409 {
410 ebpf_cleanup_fs_histogram(&efp->hread);
411 ebpf_cleanup_fs_histogram(&efp->hwrite);
412 ebpf_cleanup_fs_histogram(&efp->hopen);
413 ebpf_cleanup_fs_histogram(&efp->hadditional);
414 freez(efp->family_name);
415 efp->family_name = NULL;
416 }
417
418 /**
419 * Create Filesystem chart
420 *
421 * Create latency charts
422 *
423 * @param update_every value to overwrite the update frequency set by the server.
424 */
425 static void ebpf_obsolete_fs_charts(int update_every)
426 {
427 int i;
428 uint32_t test = NETDATA_FILESYSTEM_FLAG_CHART_CREATED | NETDATA_FILESYSTEM_REMOVE_CHARTS;
429 for (i = 0; localfs[i].filesystem; i++) {
430 ebpf_filesystem_partitions_t *efp = &localfs[i];
431 uint32_t flags = efp->flags;
432 if ((flags & test) == test) {
433 flags &= ~test;
434
435 ebpf_write_chart_obsolete(
436 NETDATA_FILESYSTEM_FAMILY,
437 efp->hread.name,
438 "",
439 efp->hread.title,
440 EBPF_COMMON_UNITS_CALLS_PER_SEC,
441 efp->family_name,
442 NULL,
443 NETDATA_EBPF_CHART_TYPE_STACKED,
444 efp->hread.order,
445 update_every);
446
447 ebpf_write_chart_obsolete(
448 NETDATA_FILESYSTEM_FAMILY,
449 efp->hwrite.name,
450 "",
451 efp->hwrite.title,
452 EBPF_COMMON_UNITS_CALLS_PER_SEC,
453 efp->family_name,
454 NULL,
455 NETDATA_EBPF_CHART_TYPE_STACKED,
456 efp->hwrite.order,
457 update_every);
458
459 ebpf_write_chart_obsolete(
460 NETDATA_FILESYSTEM_FAMILY,
461 efp->hopen.name,
462 "",
463 efp->hopen.title,
464 EBPF_COMMON_UNITS_CALLS_PER_SEC,
465 efp->family_name,
466 NULL,
467 NETDATA_EBPF_CHART_TYPE_STACKED,
468 efp->hopen.order,
469 update_every);
470
471 ebpf_write_chart_obsolete(
472 NETDATA_FILESYSTEM_FAMILY,
473 efp->hadditional.name,
474 "",
475 efp->hadditional.title,
476 EBPF_COMMON_UNITS_CALLS_PER_SEC,
477 efp->family_name,
478 NULL,
479 NETDATA_EBPF_CHART_TYPE_STACKED,
480 efp->hadditional.order,
481 update_every);
482
483 ebpf_cleanup_fs_partition(efp);
484 }
485 efp->flags = flags;
486 }
487 }
488
489 /**
490 * Create Filesystem chart
491 *
492 * Create latency charts
493 *
494 * @param update_every value to overwrite the update frequency set by the server.
495 */
496 static void ebpf_create_fs_charts(int update_every)
497 {
498 static int order = NETDATA_CHART_PRIO_EBPF_FILESYSTEM_CHARTS;
499 char chart_name[64], title[256], family[64], ctx[64];
500 int i;
501 uint32_t test = NETDATA_FILESYSTEM_FLAG_CHART_CREATED | NETDATA_FILESYSTEM_REMOVE_CHARTS;
502 for (i = 0; localfs[i].filesystem; i++) {
503 ebpf_filesystem_partitions_t *efp = &localfs[i];
504 uint32_t flags = efp->flags;
505 if (flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION && !(flags & test)) {
506 snprintfz(title, sizeof(title) - 1, "%s latency for each read request.", efp->filesystem);
507 snprintfz(family, sizeof(family) - 1, "%s_latency", efp->family);
508 snprintfz(chart_name, sizeof(chart_name) - 1, "%s_read_latency", efp->filesystem);
509 snprintfz(ctx, sizeof(ctx) - 1, "filesystem.read_latency");
510 efp->hread.name = strdupz(chart_name);
511 efp->hread.title = strdupz(title);
512 efp->hread.ctx = strdupz(ctx);
513 efp->hread.order = order;
514 efp->family_name = strdupz(family);
515
516 ebpf_create_chart(
517 NETDATA_FILESYSTEM_FAMILY,
518 efp->hread.name,
519 efp->hread.title,
520 EBPF_COMMON_UNITS_CALLS_PER_SEC,
521 efp->family_name,
522 "filesystem.read_latency",
523 NETDATA_EBPF_CHART_TYPE_STACKED,
524 order,
525 ebpf_create_global_dimension,
526 filesystem_publish_aggregated,
527 NETDATA_EBPF_HIST_MAX_BINS,
528 update_every,
529 NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
530 order++;
531
532 snprintfz(title, sizeof(title) - 1, "%s latency for each write request.", efp->filesystem);
533 snprintfz(chart_name, sizeof(chart_name) - 1, "%s_write_latency", efp->filesystem);
534 snprintfz(ctx, sizeof(ctx) - 1, "filesystem.write_latency");
535 efp->hwrite.name = strdupz(chart_name);
536 efp->hwrite.title = strdupz(title);
537 efp->hwrite.ctx = strdupz(ctx);
538 efp->hwrite.order = order;
539 ebpf_create_chart(
540 NETDATA_FILESYSTEM_FAMILY,
541 efp->hwrite.name,
542 efp->hwrite.title,
543 EBPF_COMMON_UNITS_CALLS_PER_SEC,
544 efp->family_name,
545 "filesystem.write_latency",
546 NETDATA_EBPF_CHART_TYPE_STACKED,
547 order,
548 ebpf_create_global_dimension,
549 filesystem_publish_aggregated,
550 NETDATA_EBPF_HIST_MAX_BINS,
551 update_every,
552 NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
553 order++;
554
555 snprintfz(title, sizeof(title) - 1, "%s latency for each open request.", efp->filesystem);
556 snprintfz(chart_name, sizeof(chart_name) - 1, "%s_open_latency", efp->filesystem);
557 snprintfz(ctx, sizeof(ctx) - 1, "filesystem.open_latency");
558 efp->hopen.name = strdupz(chart_name);
559 efp->hopen.title = strdupz(title);
560 efp->hopen.ctx = strdupz(ctx);
561 efp->hopen.order = order;
562 ebpf_create_chart(
563 NETDATA_FILESYSTEM_FAMILY,
564 efp->hopen.name,
565 efp->hopen.title,
566 EBPF_COMMON_UNITS_CALLS_PER_SEC,
567 efp->family_name,
568 "filesystem.open_latency",
569 NETDATA_EBPF_CHART_TYPE_STACKED,
570 order,
571 ebpf_create_global_dimension,
572 filesystem_publish_aggregated,
573 NETDATA_EBPF_HIST_MAX_BINS,
574 update_every,
575 NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
576 order++;
577
578 char *type = (efp->flags & NETDATA_FILESYSTEM_ATTR_CHARTS) ? "attribute" : "sync";
579 snprintfz(title, sizeof(title) - 1, "%s latency for each %s request.", efp->filesystem, type);
580 snprintfz(chart_name, sizeof(chart_name) - 1, "%s_%s_latency", efp->filesystem, type);
581 snprintfz(ctx, sizeof(ctx) - 1, "filesystem.%s_latency", efp->filesystem);
582 efp->hadditional.name = strdupz(chart_name);
583 efp->hadditional.title = strdupz(title);
584 efp->hadditional.ctx = strdupz(ctx);
585 efp->hadditional.order = order;
586 ebpf_create_chart(
587 NETDATA_FILESYSTEM_FAMILY,
588 efp->hadditional.name,
589 efp->hadditional.title,
590 EBPF_COMMON_UNITS_CALLS_PER_SEC,
591 efp->family_name,
592 ctx,
593 NETDATA_EBPF_CHART_TYPE_STACKED,
594 order,
595 ebpf_create_global_dimension,
596 filesystem_publish_aggregated,
597 NETDATA_EBPF_HIST_MAX_BINS,
598 update_every,
599 NETDATA_EBPF_MODULE_NAME_FILESYSTEM);
600 order++;
601 efp->flags |= NETDATA_FILESYSTEM_FLAG_CHART_CREATED;
602 }
603 }
604
605 fflush(stdout);
606 }
607
608 /**
609 * Initialize eBPF data
610 *
611 * @param em main thread structure.
612 *
613 * @return it returns 0 on success and -1 otherwise.
614 */
615 int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
616 {
617 netdata_mutex_lock(&lock);
618 int i;
619 const char *saved_name = em->info.thread_name;
620 uint64_t kernels = em->kernels;
621 bool loaded_any = false;
622 for (i = 0; localfs[i].filesystem; i++) {
623 ebpf_filesystem_partitions_t *efp = &localfs[i];
624 if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION)
625 loaded_any = true;
626
627 if (efp->load_failed)
628 continue;
629
630 if (!efp->probe_links && efp->flags & NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM) {
631 em->info.thread_name = efp->filesystem;
632 em->kernels = efp->kernels;
633 em->maps = efp->fs_maps;
634 #ifdef LIBBPF_MAJOR_VERSION
635 ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
636 #endif
637 if (em->load & EBPF_LOAD_LEGACY) {
638 efp->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &efp->objects);
639 if (!efp->probe_links) {
640 efp->load_failed = true;
641 em->info.thread_name = saved_name;
642 em->kernels = kernels;
643 em->maps = NULL;
644 continue;
645 }
646 }
647 #ifdef LIBBPF_MAJOR_VERSION
648 else {
649 efp->fs_obj = filesystem_bpf__open();
650 if (!efp->fs_obj) {
651 efp->load_failed = true;
652 em->info.thread_name = saved_name;
653 em->kernels = kernels;
654 em->maps = NULL;
655 continue;
656 } else if (ebpf_fs_load_and_attach(em->maps, efp->fs_obj, efp->functions, NULL)) {
657 efp->load_failed = true;
658 filesystem_bpf__destroy(efp->fs_obj);
659 efp->fs_obj = NULL;
660 em->info.thread_name = saved_name;
661 em->kernels = kernels;
662 em->maps = NULL;
663 continue;
664 }
665 }
666 #endif
667 efp->flags |= NETDATA_FILESYSTEM_FLAG_HAS_PARTITION;
668 loaded_any = true;
669 ebpf_mark_program_loaded();
670 ebpf_update_kernel_memory(&plugin_statistics, efp->fs_maps, EBPF_ACTION_STAT_ADD);
671
672 // Needed for filesystems like btrfs
673 if ((efp->flags & NETDATA_FILESYSTEM_FILL_ADDRESS_TABLE) && (efp->addresses.function)) {
674 if (efp->fs_maps && efp->fs_maps[NETDATA_ADDR_FS_TABLE].map_fd >= 0)
675 ebpf_load_addresses(&efp->addresses, efp->fs_maps[NETDATA_ADDR_FS_TABLE].map_fd);
676 }
677 }
678 efp->flags &= ~NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
679 }
680 em->info.thread_name = saved_name;
681 netdata_mutex_unlock(&lock);
682 em->kernels = kernels;
683 em->maps = NULL;
684
685 if (!dimensions) {
686 dimensions = ebpf_fill_histogram_dimension(NETDATA_EBPF_HIST_MAX_BINS);
687
688 memset(filesystem_aggregated_data, 0, NETDATA_EBPF_HIST_MAX_BINS * sizeof(netdata_syscall_stat_t));
689 memset(filesystem_publish_aggregated, 0, NETDATA_EBPF_HIST_MAX_BINS * sizeof(netdata_publish_syscall_t));
690
691 filesystem_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
692 }
693
694 return loaded_any ? 0 : -1;
695 }
696
697 /**
698 * Read Local partitions
699 *
700 * @return the total of partitions that will be monitored
701 */
702 static int ebpf_read_local_partitions(void)
703 {
704 char filename[FILENAME_MAX + 1];
705 snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", netdata_configured_host_prefix);
706 procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
707 if (unlikely(!ff)) {
708 snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", netdata_configured_host_prefix);
709 ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
710 if (unlikely(!ff))
711 return 0;
712 }
713
714 ff = procfile_readall(ff);
715 if (unlikely(!ff))
716 return 0;
717
718 int count = 0;
719 unsigned long l, i, lines = procfile_lines(ff);
720 for (i = 0; localfs[i].filesystem; i++) {
721 localfs[i].flags |= NETDATA_FILESYSTEM_REMOVE_CHARTS;
722 }
723
724 for (l = 0; l < lines; l++) {
725 // In "normal" situation the expected value is at column 7
726 // When `shared` options is added to mount information, the filesystem is at column 8
727 // Finally when we have systemd starting netdata, it will be at column 9
728 unsigned long index = procfile_linewords(ff, l) - 3;
729
730 char *fs = procfile_lineword(ff, l, index);
731
732 for (i = 0; localfs[i].filesystem; i++) {
733 ebpf_filesystem_partitions_t *w = &localfs[i];
734 if (w->enabled &&
735 (!strcmp(fs, w->filesystem) || (w->optional_filesystem && !strcmp(fs, w->optional_filesystem)))) {
736 if (!(localfs[i].flags & NETDATA_FILESYSTEM_FLAG_CHART_CREATED))
737 localfs[i].flags |= NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
738
739 localfs[i].flags &= ~NETDATA_FILESYSTEM_REMOVE_CHARTS;
740 count++;
741 break;
742 }
743 }
744 }
745 procfile_close(ff);
746
747 return count;
748 }
749
750 /**
751 * Update partition
752 *
753 * Update the partition structures before to plot
754 *
755 * @param em main thread structure
756 *
757 * @return 0 on success and -1 otherwise.
758 */
759 static int ebpf_update_partitions(ebpf_module_t *em)
760 {
761 static time_t update_every = 0;
762 time_t curr = now_realtime_sec();
763 if (curr < update_every)
764 return 0;
765
766 update_every = curr + (NETDATA_PARTITION_UPDATE_INTERVAL_MULTIPLIER * em->update_every);
767 if (!ebpf_read_local_partitions()) {
768 em->optional = -1;
769 return -1;
770 }
771
772 if (ebpf_filesystem_initialize_ebpf_data(em)) {
773 return -1;
774 }
775
776 return 0;
777 }
778
779 /*****************************************************************
780 *
781 * CLEANUP FUNCTIONS
782 *
783 *****************************************************************/
784
785 /*
786 * Cleanup eBPF data
787 */
788 void ebpf_filesystem_cleanup_ebpf_data(void)
789 {
790 int i;
791 for (i = 0; localfs[i].filesystem; i++) {
792 ebpf_filesystem_partitions_t *efp = &localfs[i];
793 if (efp->probe_links)
794 ebpf_cleanup_fs_partition(efp);
795 }
796 }
797
798 /**
799 * Obsolete global
800 *
801 * Obsolete global charts created by thread.
802 *
803 * @param em a pointer to `struct ebpf_module`
804 */
805 static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
806 {
807 int i;
808 for (i = 0; localfs[i].filesystem; i++) {
809 ebpf_filesystem_partitions_t *efp = &localfs[i];
810 if (!efp->objects)
811 continue;
812
813 if (!(efp->flags & NETDATA_FILESYSTEM_FLAG_CHART_CREATED))
814 continue;
815
816 ebpf_write_chart_obsolete(
817 NETDATA_FILESYSTEM_FAMILY,
818 efp->hread.name,
819 "",
820 efp->hread.title,
821 EBPF_COMMON_UNITS_CALLS_PER_SEC,
822 efp->family_name,
823 NETDATA_EBPF_CHART_TYPE_STACKED,
824 "filesystem.read_latency",
825 efp->hread.order,
826 em->update_every);
827 ebpf_cleanup_fs_histogram(&efp->hread);
828
829 ebpf_write_chart_obsolete(
830 NETDATA_FILESYSTEM_FAMILY,
831 efp->hwrite.name,
832 "",
833 efp->hwrite.title,
834 EBPF_COMMON_UNITS_CALLS_PER_SEC,
835 efp->family_name,
836 NETDATA_EBPF_CHART_TYPE_STACKED,
837 "filesystem.write_latency",
838 efp->hwrite.order,
839 em->update_every);
840 ebpf_cleanup_fs_histogram(&efp->hwrite);
841
842 ebpf_write_chart_obsolete(
843 NETDATA_FILESYSTEM_FAMILY,
844 efp->hopen.name,
845 "",
846 efp->hopen.title,
847 EBPF_COMMON_UNITS_CALLS_PER_SEC,
848 efp->family_name,
849 NETDATA_EBPF_CHART_TYPE_STACKED,
850 "filesystem.open_latency",
851 efp->hopen.order,
852 em->update_every);
853 ebpf_cleanup_fs_histogram(&efp->hopen);
854
855 ebpf_write_chart_obsolete(
856 NETDATA_FILESYSTEM_FAMILY,
857 efp->hadditional.name,
858 "",
859 efp->hadditional.title,
860 EBPF_COMMON_UNITS_CALLS_PER_SEC,
861 efp->family_name,
862 NETDATA_EBPF_CHART_TYPE_STACKED,
863 efp->hadditional.ctx,
864 efp->hadditional.order,
865 em->update_every);
866 ebpf_cleanup_fs_histogram(&efp->hadditional);
867
868 efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
869 }
870 }
871
872 /**
873 * Filesystem exit
874 *
875 * Cancel child thread.
876 *
877 * @param ptr thread data.
878 */
879 void ebpf_filesystem_unload_bpf(ebpf_module_t *em)
880 {
881 int i;
882 for (i = 0; localfs[i].filesystem; i++) {
883 ebpf_filesystem_partitions_t *efp = &localfs[i];
884 #ifdef LIBBPF_MAJOR_VERSION
885 if (efp->fs_obj) {
886 filesystem_bpf__destroy(efp->fs_obj);
887 efp->fs_obj = NULL;
888 efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
889 }
890 #endif
891 if ((em->load & EBPF_LOAD_LEGACY) && efp->probe_links) {
892 ebpf_unload_legacy_code(efp->objects, efp->probe_links);
893 efp->objects = NULL;
894 efp->probe_links = NULL;
895 efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
896 }
897 }
898 }
899
900 static void ebpf_filesystem_exit(void *pptr)
901 {
902 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
903 if (!em)
904 return;
905
906 int i;
907 bool has_resources = false;
908 for (i = 0; localfs[i].filesystem; i++) {
909 ebpf_filesystem_partitions_t *efp = &localfs[i];
910 if (efp->probe_links || efp->objects || efp->fs_obj) {
911 has_resources = true;
912 break;
913 }
914 }
915 if (!dimensions && !filesystem_hash_values && !has_resources) {
916 netdata_mutex_lock(&ebpf_exit_cleanup);
917 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
918 netdata_mutex_unlock(&ebpf_exit_cleanup);
919 return;
920 }
921
922 if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
923 netdata_mutex_lock(&lock);
924 ebpf_obsolete_filesystem_global(em);
925
926 fflush(stdout);
927 netdata_mutex_unlock(&lock);
928 }
929
930 ebpf_filesystem_cleanup_ebpf_data();
931 if (dimensions) {
932 ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS);
933 dimensions = NULL;
934 }
935
936 freez(filesystem_hash_values);
937
938 if (!ebpf_plugin_stop() && em->functions.bpf_unload)
939 em->functions.bpf_unload(em);
940
941 netdata_mutex_lock(&ebpf_exit_cleanup);
942 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
943 netdata_mutex_unlock(&ebpf_exit_cleanup);
944 }
945
946 /*****************************************************************
947 *
948 * MAIN THREAD
949 *
950 *****************************************************************/
951
952 /**
953 * Select histogram
954 *
955 * Select histogram based on operation type
956 *
957 * @param efp pointer to filesystem partition
958 * @param id histogram selector
959 *
960 * @return pointer to histogram and sets idx
961 */
962 static inline netdata_ebpf_histogram_t *select_hist(ebpf_filesystem_partitions_t *efp, uint32_t *idx, uint32_t id)
963 {
964 uint32_t hist_idx = id / NETDATA_FS_HISTOGRAM_BINS;
965 if (hist_idx >= 4)
966 return NULL;
967
968 *idx = id % NETDATA_FS_HISTOGRAM_BINS;
969
970 switch (hist_idx) {
971 case 0:
972 return &efp->hread;
973 case 1:
974 return &efp->hwrite;
975 case 2:
976 return &efp->hopen;
977 default:
978 return &efp->hadditional;
979 }
980 }
981
982 /**
983 * Read filesystem table
984 *
985 * @param efp structure with filesystem monitored
986 * @param fd file descriptor to get data.
987 * @param maps_per_core do I need to read all cores?
988 *
989 * Read the table with number of calls for all functions
990 */
991 static void read_filesystem_table(ebpf_filesystem_partitions_t *efp, int fd, int maps_per_core)
992 {
993 if (!efp || !efp->fs_maps)
994 return;
995
996 netdata_idx_t *values = filesystem_hash_values;
997 uint32_t key;
998 uint32_t idx;
999 for (key = 0; key < NETDATA_KEY_CALLS_SYNC; key++) {
1000 netdata_ebpf_histogram_t *w = select_hist(efp, &idx, key);
1001 if (!w) {
1002 continue;
1003 }
1004
1005 int test = bpf_map_lookup_elem(fd, &key, values);
1006 if (test < 0) {
1007 continue;
1008 }
1009
1010 uint64_t total = 0;
1011 int i;
1012 int end = (maps_per_core) ? ebpf_nprocs : 1;
1013 for (i = 0; i < end; i++) {
1014 total += values[i];
1015 }
1016
1017 if (idx >= NETDATA_EBPF_HIST_MAX_BINS)
1018 idx = NETDATA_EBPF_HIST_MAX_BINS - 1;
1019 w->histogram[idx] = total;
1020 }
1021 }
1022
1023 /**
1024 * Read filesystem table
1025 *
1026 * Read the table with number of calls for all functions
1027 *
1028 * @param maps_per_core do I need to read all cores?
1029 */
1030 static void read_filesystem_tables(int maps_per_core)
1031 {
1032 int i;
1033 for (i = 0; localfs[i].filesystem; i++) {
1034 ebpf_filesystem_partitions_t *efp = &localfs[i];
1035 if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION && efp->fs_maps) {
1036 read_filesystem_table(efp, efp->fs_maps[NETDATA_MAIN_FS_TABLE].map_fd, maps_per_core);
1037 }
1038 }
1039 }
1040
1041 /**
1042 * Filesystem read hash
1043 *
1044 * This is the thread callback.
1045 * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy filesystem.
1046 *
1047 * @param ptr It is a NULL value for this thread.
1048 *
1049 * @return It always returns NULL.
1050 */
1051 void ebpf_filesystem_read_hash(ebpf_module_t *em)
1052 {
1053 (void)ebpf_update_partitions(em);
1054
1055 ebpf_obsolete_fs_charts(em->update_every);
1056
1057 if (em->optional)
1058 return;
1059
1060 read_filesystem_tables(em->maps_per_core);
1061 }
1062
1063 /**
1064 * Send Hard disk data
1065 *
1066 * Send hard disk information to Netdata.
1067 */
1068 static void ebpf_histogram_send_data(void)
1069 {
1070 uint32_t i;
1071 uint32_t test = NETDATA_FILESYSTEM_FLAG_HAS_PARTITION | NETDATA_FILESYSTEM_REMOVE_CHARTS;
1072 for (i = 0; localfs[i].filesystem; i++) {
1073 ebpf_filesystem_partitions_t *efp = &localfs[i];
1074 if ((efp->flags & test) == NETDATA_FILESYSTEM_FLAG_HAS_PARTITION) {
1075 write_histogram_chart(
1076 NETDATA_FILESYSTEM_FAMILY,
1077 efp->hread.name,
1078 efp->hread.histogram,
1079 dimensions,
1080 NETDATA_EBPF_HIST_MAX_BINS);
1081
1082 write_histogram_chart(
1083 NETDATA_FILESYSTEM_FAMILY,
1084 efp->hwrite.name,
1085 efp->hwrite.histogram,
1086 dimensions,
1087 NETDATA_EBPF_HIST_MAX_BINS);
1088
1089 write_histogram_chart(
1090 NETDATA_FILESYSTEM_FAMILY,
1091 efp->hopen.name,
1092 efp->hopen.histogram,
1093 dimensions,
1094 NETDATA_EBPF_HIST_MAX_BINS);
1095
1096 write_histogram_chart(
1097 NETDATA_FILESYSTEM_FAMILY,
1098 efp->hadditional.name,
1099 efp->hadditional.histogram,
1100 dimensions,
1101 NETDATA_EBPF_HIST_MAX_BINS);
1102 }
1103 }
1104 }
1105
1106 /**
1107 * Main loop for this collector.
1108 *
1109 * @param em main structure for this thread
1110 */
1111 static void filesystem_collector(ebpf_module_t *em)
1112 {
1113 int update_every = em->update_every;
1114 int counter = update_every - 1;
1115 uint32_t running_time = 0;
1116 uint32_t lifetime = em->lifetime;
1117 heartbeat_t hb;
1118 heartbeat_init(&hb, USEC_PER_SEC);
1119 while (!ebpf_plugin_stop() && running_time < lifetime) {
1120 if (ebpf_plugin_stop())
1121 break;
1122
1123 heartbeat_next(&hb);
1124 if (ebpf_plugin_stop())
1125 break;
1126
1127 if (++counter != update_every)
1128 continue;
1129
1130 counter = 0;
1131 ebpf_filesystem_read_hash(em);
1132 netdata_mutex_lock(&lock);
1133
1134 ebpf_create_fs_charts(update_every);
1135 ebpf_histogram_send_data();
1136
1137 netdata_mutex_unlock(&lock);
1138
1139 if (ebpf_plugin_stop())
1140 break;
1141
1142 netdata_mutex_lock(&ebpf_exit_cleanup);
1143 if (running_time)
1144 running_time += update_every;
1145 else
1146 running_time = update_every;
1147
1148 em->running_time = running_time;
1149 netdata_mutex_unlock(&ebpf_exit_cleanup);
1150 }
1151 }
1152
1153 /*****************************************************************
1154 *
1155 * ENTRY THREAD
1156 *
1157 *****************************************************************/
1158
1159 /**
1160 * Update Filesystem
1161 *
1162 * Update file system structure using values read from configuration file.
1163 */
1164 static void ebpf_update_filesystem(void)
1165 {
1166 char dist[NETDATA_FS_MAX_DIST_NAME + 1];
1167 int i;
1168 for (i = 0; localfs[i].filesystem; i++) {
1169 snprintfz(dist, NETDATA_FS_MAX_DIST_NAME, "%sdist", localfs[i].filesystem);
1170
1171 localfs[i].enabled = inicfg_get_boolean(&fs_config, NETDATA_FILESYSTEM_CONFIG_NAME, dist, CONFIG_BOOLEAN_YES);
1172 }
1173 }
1174
1175 /**
1176 * Set maps
1177 *
1178 * When thread is initialized the variable fs_maps is set as null,
1179 * this function fills the variable before to use.
1180 */
1181 static void ebpf_set_maps(void)
1182 {
1183 localfs[NETDATA_FS_LOCALFS_EXT4].fs_maps = ext4_maps;
1184 localfs[NETDATA_FS_LOCALFS_XFS].fs_maps = xfs_maps;
1185 localfs[NETDATA_FS_LOCALFS_NFS].fs_maps = nfs_maps;
1186 localfs[NETDATA_FS_LOCALFS_ZFS].fs_maps = zfs_maps;
1187 localfs[NETDATA_FS_LOCALFS_BTRFS].fs_maps = btrfs_maps;
1188 }
1189
1190 /**
1191 * Filesystem thread
1192 *
1193 * Thread used to generate filesystem charts.
1194 *
1195 * @param ptr a pointer to `struct ebpf_module`
1196 *
1197 * @return It always return NULL
1198 */
1199 void ebpf_filesystem_thread(void *ptr)
1200 {
1201 ebpf_module_t *em = (ebpf_module_t *)ptr;
1202
1203 CLEANUP_FUNCTION_REGISTER(ebpf_filesystem_exit) cleanup_ptr = em;
1204
1205 if (!ebpf_module_thread_has_valid_state(em)) {
1206 goto endfilesystem;
1207 }
1208
1209 ebpf_set_maps();
1210 ebpf_update_filesystem();
1211
1212 // Initialize optional as zero, to identify when there are not partitions to monitor
1213 em->optional = 0;
1214
1215 #ifdef LIBBPF_MAJOR_VERSION
1216 ebpf_adjust_thread_load(em, default_btf);
1217 #endif
1218 if (ebpf_update_partitions(em)) {
1219 if (em->optional)
1220 netdata_log_info("Netdata cannot monitor the filesystems used on this host.");
1221
1222 goto endfilesystem;
1223 }
1224
1225 int algorithms[NETDATA_EBPF_HIST_MAX_BINS];
1226 ebpf_fill_algorithms(algorithms, NETDATA_EBPF_HIST_MAX_BINS, NETDATA_EBPF_INCREMENTAL_IDX);
1227 ebpf_global_labels(
1228 filesystem_aggregated_data,
1229 filesystem_publish_aggregated,
1230 dimensions,
1231 dimensions,
1232 algorithms,
1233 NETDATA_EBPF_HIST_MAX_BINS);
1234
1235 netdata_mutex_lock(&lock);
1236 ebpf_create_fs_charts(em->update_every);
1237 ebpf_update_stats(&plugin_statistics, em);
1238 netdata_mutex_unlock(&lock);
1239
1240 filesystem_collector(em);
1241
1242 endfilesystem:
1243 ebpf_update_disabled_plugin_stats(em);
1244 }