1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "ebpf_filesystem.h"
4
+
5
+struct config fs_config = { .first_section = NULL,
6
+ .last_section = NULL,
7
+ .mutex = NETDATA_MUTEX_INITIALIZER,
8
+ .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
9
+ .rwlock = AVL_LOCK_INITIALIZER } };
10
+
11
+ebpf_filesystem_partitions_t localfs[] =
12
+ {{.filesystem = "ext4",
13
+ .family = "EXT4",
14
+ .objects = NULL,
15
+ .probe_links = NULL,
16
+ .flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION,
17
+ .enabled = CONFIG_BOOLEAN_YES,
18
+ .addresses = {.function = NULL, .addr = 0}},
19
+ {.filesystem = NULL,
20
+ .family = NULL,
21
+ .objects = NULL,
22
+ .probe_links = NULL,
23
+ .flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION,
24
+ .enabled = CONFIG_BOOLEAN_YES,
25
+ .addresses = {.function = NULL, .addr = 0}}};
26
+
27
+struct netdata_static_thread filesystem_threads = {"EBPF FS READ",
28
+ NULL, NULL, 1, NULL,
29
+ NULL, NULL };
30
+
31
+static int read_thread_closed = 1;
32
+static netdata_syscall_stat_t filesystem_aggregated_data[NETDATA_FILESYSTEM_MAX_BINS];
33
+static netdata_publish_syscall_t filesystem_publish_aggregated[NETDATA_FILESYSTEM_MAX_BINS];
34
+
35
+char **dimensions = NULL;
36
+static netdata_idx_t *filesystem_hash_values = NULL;
37
+
38
+/*****************************************************************
39
+ *
40
+ * COMMON FUNCTIONS
41
+ *
42
+ *****************************************************************/
43
+
44
+/**
45
+ * Create Filesystem chart
46
+ *
47
+ * Create latency charts
48
+ */
49
+static void ebpf_obsolete_fs_charts()
50
+{
51
+ int i;
52
+ uint32_t test = NETDATA_FILESYSTEM_FLAG_CHART_CREATED | NETDATA_FILESYSTEM_REMOVE_CHARTS;
53
+ for (i = 0; localfs[i].filesystem; i++) {
54
+ ebpf_filesystem_partitions_t *efp = &localfs[i];
55
+ uint32_t flags = efp->flags;
56
+ if ((flags & test) == test) {
57
+ flags &= ~NETDATA_FILESYSTEM_FLAG_CHART_CREATED;
58
+
59
+ ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hread.name,
60
+ efp->hread.title,
61
+ EBPF_COMMON_DIMENSION_CALL, efp->family_name,
62
+ NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hread.order);
63
+
64
+ ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
65
+ efp->hwrite.title,
66
+ EBPF_COMMON_DIMENSION_CALL, efp->family_name,
67
+ NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hwrite.order);
68
+
69
+ ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name,
70
+ efp->hopen.title,
71
+ EBPF_COMMON_DIMENSION_CALL, efp->family_name,
72
+ NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hopen.order);
73
+
74
+ ebpf_write_chart_obsolete(NETDATA_FILESYSTEM_FAMILY, efp->hsync.name,
75
+ efp->hsync.title,
76
+ EBPF_COMMON_DIMENSION_CALL, efp->family_name,
77
+ NULL, NETDATA_EBPF_CHART_TYPE_STACKED, efp->hsync.order);
78
+ }
79
+ efp->flags = flags;
80
+ }
81
+}
82
+
83
+/**
84
+ * Create Filesystem chart
85
+ *
86
+ * Create latency charts
87
+ */
88
+static void ebpf_create_fs_charts()
89
+{
90
+ static int order = NETDATA_CHART_PRIO_EBPF_FILESYSTEM_CHARTS;
91
+ char chart_name[64], title[256], family[64];
92
+ int i;
93
+ uint32_t test = NETDATA_FILESYSTEM_FLAG_CHART_CREATED|NETDATA_FILESYSTEM_REMOVE_CHARTS;
94
+ for (i = 0; localfs[i].filesystem; i++) {
95
+ ebpf_filesystem_partitions_t *efp = &localfs[i];
96
+ uint32_t flags = efp->flags;
97
+ if (flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION && !(flags & test)) {
98
+ snprintfz(title, 255, "%s latency for each read request.", efp->filesystem);
99
+ snprintfz(family, 63, "%s latency (eBPF)", efp->family);
100
+ snprintfz(chart_name, 63, "%s_read_latency", efp->filesystem);
101
+ efp->hread.name = strdupz(chart_name);
102
+ efp->hread.title = strdupz(title);
103
+ efp->hread.order = order;
104
+ efp->family_name = strdupz(family);
105
+
106
+ ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hread.name,
107
+ title,
108
+ EBPF_COMMON_DIMENSION_CALL, family,
109
+ NULL, NETDATA_EBPF_CHART_TYPE_STACKED, order, ebpf_create_global_dimension,
110
+ filesystem_publish_aggregated, NETDATA_FILESYSTEM_MAX_BINS);
111
+ order++;
112
+
113
+ snprintfz(title, 255, "%s latency for each write request.", efp->filesystem);
114
+ snprintfz(chart_name, 63, "%s_write_latency", efp->filesystem);
115
+ efp->hwrite.name = strdupz(chart_name);
116
+ efp->hwrite.title = strdupz(title);
117
+ efp->hwrite.order = order;
118
+ ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
119
+ title,
120
+ EBPF_COMMON_DIMENSION_CALL, family,
121
+ NULL, NETDATA_EBPF_CHART_TYPE_STACKED, order, ebpf_create_global_dimension,
122
+ filesystem_publish_aggregated, NETDATA_FILESYSTEM_MAX_BINS);
123
+ order++;
124
+
125
+ snprintfz(title, 255, "%s latency for each open request.", efp->filesystem);
126
+ snprintfz(chart_name, 63, "%s_open_latency", efp->filesystem);
127
+ efp->hopen.name = strdupz(chart_name);
128
+ efp->hopen.title = strdupz(title);
129
+ efp->hopen.order = order;
130
+ ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name,
131
+ title,
132
+ EBPF_COMMON_DIMENSION_CALL, family,
133
+ NULL, NETDATA_EBPF_CHART_TYPE_STACKED, order, ebpf_create_global_dimension,
134
+ filesystem_publish_aggregated, NETDATA_FILESYSTEM_MAX_BINS);
135
+ order++;
136
+
137
+ snprintfz(title, 255, "%s latency for each sync request.", efp->filesystem);
138
+ snprintfz(chart_name, 63, "%s_sync_latency", efp->filesystem);
139
+ efp->hsync.name = strdupz(chart_name);
140
+ efp->hsync.title = strdupz(title);
141
+ efp->hsync.order = order;
142
+ ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, efp->hsync.name,
143
+ title,
144
+ EBPF_COMMON_DIMENSION_CALL, family,
145
+ NULL, NETDATA_EBPF_CHART_TYPE_STACKED, order, ebpf_create_global_dimension,
146
+ filesystem_publish_aggregated, NETDATA_FILESYSTEM_MAX_BINS);
147
+ order++;
148
+ efp->flags |= NETDATA_FILESYSTEM_FLAG_CHART_CREATED;
149
+ }
150
+ }
151
+}
152
+
153
+/**
154
+ * Initialize eBPF data
155
+ *
156
+ * @param em main thread structure.
157
+ *
158
+ * @return it returns 0 on success and -1 otherwise.
159
+ */
160
+int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
161
+{
162
+ int i;
163
+ const char *saved_name = em->thread_name;
164
+ for (i = 0; localfs[i].filesystem; i++) {
165
+ ebpf_filesystem_partitions_t *efp = &localfs[i];
166
+ if (!efp->probe_links && efp->flags & NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM) {
167
+ ebpf_data_t *ed = &efp->kernel_info;
168
+ fill_ebpf_data(ed);
169
+
170
+ if (ebpf_update_kernel(ed)) {
171
+ em->thread_name = saved_name;
172
+ return -1;
173
+ }
174
+
175
+ em->thread_name = efp->filesystem;
176
+ efp->probe_links = ebpf_load_program(ebpf_plugin_dir, em, kernel_string,
177
+ &efp->objects, ed->map_fd);
178
+ if (!efp->probe_links) {
179
+ em->thread_name = saved_name;
180
+ return -1;
181
+ }
182
+ efp->flags |= NETDATA_FILESYSTEM_FLAG_HAS_PARTITION;
183
+
184
+ // Nedeed for filesystems like btrfs
185
+ if ((efp->flags & NETDATA_FILESYSTEM_FILL_ADDRESS_TABLE) && (efp->addresses.function))
186
+ ebpf_load_addresses(&efp->addresses, efp->kernel_info.map_fd[NETDATA_ADDR_FS_TABLE]);
187
+ }
188
+ efp->flags &= ~NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
189
+ }
190
+ em->thread_name = saved_name;
191
+
192
+ if (!dimensions) {
193
+ dimensions = ebpf_fill_histogram_dimension(NETDATA_FILESYSTEM_MAX_BINS);
194
+
195
+ memset(filesystem_aggregated_data, 0 , NETDATA_FILESYSTEM_MAX_BINS * sizeof(netdata_syscall_stat_t));
196
+ memset(filesystem_publish_aggregated, 0 , NETDATA_FILESYSTEM_MAX_BINS * sizeof(netdata_publish_syscall_t));
197
+
198
+ filesystem_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
199
+ }
200
+
201
+ return 0;
202
+}
203
+
204
+/**
205
+ * Read Local partitions
206
+ *
207
+ * @return the total of partitions that will be monitored
208
+ */
209
+static int ebpf_read_local_partitions()
210
+{
211
+ char filename[FILENAME_MAX + 1];
212
+ snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", netdata_configured_host_prefix);
213
+ procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
214
+ if(unlikely(!ff)) {
215
+ snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", netdata_configured_host_prefix);
216
+ ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
217
+ if(unlikely(!ff)) return 0;
218
+ }
219
+
220
+ ff = procfile_readall(ff);
221
+ if(unlikely(!ff))
222
+ return 0;
223
+
224
+ int count = 0;
225
+ unsigned long l, i, lines = procfile_lines(ff);
226
+ for (i = 0; localfs[i].filesystem; i++) {
227
+ localfs[i].flags |= NETDATA_FILESYSTEM_REMOVE_CHARTS;
228
+ }
229
+
230
+ for(l = 0; l < lines ; l++) {
231
+ // In "normal" situation the expected value is at column 7
232
+ // When `shared` options is added to mount information, the filesystem is at column 8
233
+ // Finally when we have systemd starting netdata, it will be at column 9
234
+ unsigned long index = procfile_linewords(ff, l) - 3;
235
+
236
+ char *fs = procfile_lineword(ff, l, index);
237
+
238
+ for (i = 0; localfs[i].filesystem; i++) {
239
+ ebpf_filesystem_partitions_t *w = &localfs[i];
240
+ if (w->enabled && !strcmp(fs, w->filesystem)) {
241
+ localfs[i].flags |= NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
242
+ localfs[i].flags &= ~NETDATA_FILESYSTEM_REMOVE_CHARTS;
243
+ count++;
244
+ break;
245
+ }
246
+ }
247
+ }
248
+ procfile_close(ff);
249
+
250
+ return count;
251
+}
252
+
253
+/**
254
+ * Update partition
255
+ *
256
+ * Update the partition structures before to plot
257
+ *
258
+ * @param em main thread structure
259
+ *
260
+ * @return 0 on success and -1 otherwise.
261
+ */
262
+static int ebpf_update_partitions(ebpf_module_t *em)
263
+{
264
+ static time_t update_time = 0;
265
+ time_t curr = now_realtime_sec();
266
+ if (curr < update_time)
267
+ return 0;
268
+
269
+ update_time = curr + 5 * em->update_time;
270
+ if (!ebpf_read_local_partitions()) {
271
+ em->optional = -1;
272
+ return -1;
273
+ }
274
+
275
+ if (ebpf_filesystem_initialize_ebpf_data(em)) {
276
+ return -1;
277
+ }
278
+
279
+ return 0;
280
+}
281
+
282
+/*****************************************************************
283
+ *
284
+ * CLEANUP FUNCTIONS
285
+ *
286
+ *****************************************************************/
287
+
288
+/*
289
+ * Cleanup eBPF data
290
+ */
291
+void ebpf_filesystem_cleanup_ebpf_data()
292
+{
293
+ int i;
294
+ for (i = 0; localfs[i].filesystem; i++) {
295
+ ebpf_filesystem_partitions_t *efp = &localfs[i];
296
+ if (efp->probe_links) {
297
+ freez(efp->kernel_info.map_fd);
298
+ freez(efp->family_name);
299
+
300
+ freez(efp->hread.name);
301
+ freez(efp->hread.title);
302
+
303
+ freez(efp->hwrite.name);
304
+ freez(efp->hwrite.title);
305
+
306
+ freez(efp->hopen.name);
307
+ freez(efp->hopen.title);
308
+
309
+ freez(efp->hsync.name);
310
+ freez(efp->hsync.title);
311
+
312
+ struct bpf_link **probe_links = efp->probe_links;
313
+ size_t j = 0 ;
314
+ struct bpf_program *prog;
315
+ bpf_object__for_each_program(prog, efp->objects) {
316
+ bpf_link__destroy(probe_links[j]);
317
+ j++;
318
+ }
319
+ bpf_object__close(efp->objects);
320
+
321
+ ebpf_histogram_dimension_cleanup(dimensions, NETDATA_FILESYSTEM_MAX_BINS);
322
+ freez(filesystem_hash_values);
323
+ }
324
+ }
325
+}
326
+
327
+/**
328
+ * Clean up the main thread.
329
+ *
330
+ * @param ptr thread data.
331
+ */
332
+static void ebpf_filesystem_cleanup(void *ptr)
333
+{
334
+ ebpf_module_t *em = (ebpf_module_t *)ptr;
335
+ if (!em->enabled)
336
+ return;
337
+
338
+ heartbeat_t hb;
339
+ heartbeat_init(&hb);
340
+ uint32_t tick = 2*USEC_PER_MS;
341
+ while (!read_thread_closed) {
342
+ usec_t dt = heartbeat_next(&hb, tick);
343
+ UNUSED(dt);
344
+ }
345
+
346
+ freez(filesystem_threads.thread);
347
+ ebpf_cleanup_publish_syscall(filesystem_publish_aggregated);
348
+
349
+ ebpf_filesystem_cleanup_ebpf_data();
350
+}
351
+
352
+/*****************************************************************
353
+ *
354
+ * MAIN THREAD
355
+ *
356
+ *****************************************************************/
357
+
358
+/**
359
+ * Select hist
360
+ *
361
+ * Select a histogram to store data.
362
+ *
363
+ * @param efp pointer for the structure with pointers.
364
+ * @param id histogram selector
365
+ *
366
+ * @return It returns a pointer for the histogram
367
+ */
368
+static inline netdata_ebpf_histogram_t *select_hist(ebpf_filesystem_partitions_t *efp, uint32_t *idx, uint32_t id)
369
+{
370
+ if (id < NETDATA_KEY_CALLS_READ) {
371
+ *idx = id;
372
+ return &efp->hread;
373
+ } else if (id < NETDATA_KEY_CALLS_WRITE) {
374
+ *idx = id - NETDATA_KEY_CALLS_READ;
375
+ return &efp->hwrite;
376
+ } else if (id < NETDATA_KEY_CALLS_OPEN) {
377
+ *idx = id - NETDATA_KEY_CALLS_WRITE;
378
+ return &efp->hopen;
379
+ } else if (id < NETDATA_KEY_CALLS_SYNC ){
380
+ *idx = id - NETDATA_KEY_CALLS_OPEN;
381
+ return &efp->hsync;
382
+ }
383
+
384
+ return NULL;
385
+}
386
+
387
+/**
388
+ * Read hard disk table
389
+ *
390
+ * @param table index for the hash table
391
+ *
392
+ * Read the table with number of calls for all functions
393
+ */
394
+static void read_filesystem_table(ebpf_filesystem_partitions_t *efp)
395
+{
396
+ netdata_idx_t *values = filesystem_hash_values;
397
+ uint32_t key;
398
+ uint32_t idx;
399
+ int fd = efp->kernel_info.map_fd[NETDATA_MAIN_FS_TABLE];
400
+ for (key = 0; key < NETDATA_KEY_CALLS_SYNC; key++) {
401
+ netdata_ebpf_histogram_t *w = select_hist(efp, &idx, key);
402
+ if (!w) {
403
+ continue;
404
+ }
405
+
406
+ int test = bpf_map_lookup_elem(fd, &key, values);
407
+ if (test < 0) {
408
+ continue;
409
+ }
410
+
411
+ uint64_t total = 0;
412
+ int i;
413
+ int end = ebpf_nprocs;
414
+ for (i = 0; i < end; i++) {
415
+ total += values[i];
416
+ }
417
+
418
+ w->histogram[idx] = total;
419
+ }
420
+}
421
+
422
+/**
423
+ * Read hard disk table
424
+ *
425
+ * @param table index for the hash table
426
+ *
427
+ * Read the table with number of calls for all functions
428
+ */
429
+static void read_filesystem_tables()
430
+{
431
+ int i;
432
+ for (i = 0; localfs[i].filesystem; i++) {
433
+ ebpf_filesystem_partitions_t *efp = &localfs[i];
434
+ if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION) {
435
+ read_filesystem_table(efp);
436
+ }
437
+ }
438
+}
439
+
440
+/**
441
+ * Socket read hash
442
+ *
443
+ * This is the thread callback.
444
+ * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy socket.
445
+ *
446
+ * @param ptr It is a NULL value for this thread.
447
+ *
448
+ * @return It always returns NULL.
449
+ */
450
+void *ebpf_filesystem_read_hash(void *ptr)
451
+{
452
+ ebpf_module_t *em = (ebpf_module_t *)ptr;
453
+ read_thread_closed = 0;
454
+
455
+ heartbeat_t hb;
456
+ heartbeat_init(&hb);
457
+ usec_t step = NETDATA_FILESYSTEM_READ_SLEEP_MS * em->update_time;
458
+ while (!close_ebpf_plugin) {
459
+ usec_t dt = heartbeat_next(&hb, step);
460
+ (void)dt;
461
+
462
+ (void) ebpf_update_partitions(em);
463
+ ebpf_obsolete_fs_charts();
464
+
465
+ // No more partitions, it is not necessary to read tables
466
+ if (em->optional)
467
+ continue;
468
+
469
+ read_filesystem_tables();
470
+ }
471
+
472
+ read_thread_closed = 1;
473
+ return NULL;
474
+}
475
+
476
+/**
477
+ * Call the necessary functions to create a name.
478
+ *
479
+ * @param family family name
480
+ * @param name chart name
481
+ * @param hist0 histogram values
482
+ * @param end number of bins that will be sent to Netdata.
483
+ *
484
+ * @return It returns a variable tha maps the charts that did not have zero values.
485
+ */
486
+static void write_histogram_chart(char *family, char *name, const netdata_idx_t *hist, uint32_t end)
487
+{
488
+ write_begin_chart(family, name);
489
+
490
+ uint32_t i;
491
+ for (i = 0; i < end; i++) {
492
+ write_chart_dimension(dimensions[i], (long long) hist[i]);
493
+ }
494
+
495
+ write_end_chart();
496
+
497
+ fflush(stdout);
498
+}
499
+
500
+/**
501
+ * Send Hard disk data
502
+ *
503
+ * Send hard disk information to Netdata.
504
+ */
505
+static void ebpf_histogram_send_data()
506
+{
507
+ uint32_t i;
508
+ for (i = 0; localfs[i].filesystem; i++) {
509
+ ebpf_filesystem_partitions_t *efp = &localfs[i];
510
+ if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION) {
511
+ write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hread.name,
512
+ efp->hread.histogram, NETDATA_FILESYSTEM_MAX_BINS);
513
+
514
+ write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hwrite.name,
515
+ efp->hwrite.histogram, NETDATA_FILESYSTEM_MAX_BINS);
516
+
517
+ write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hopen.name,
518
+ efp->hopen.histogram, NETDATA_FILESYSTEM_MAX_BINS);
519
+
520
+ write_histogram_chart(NETDATA_FILESYSTEM_FAMILY, efp->hsync.name,
521
+ efp->hsync.histogram, NETDATA_FILESYSTEM_MAX_BINS);
522
+ }
523
+ }
524
+}
525
+
526
+/**
527
+ * Main loop for this collector.
528
+ *
529
+ * @param em main structure for this thread
530
+ */
531
+static void filesystem_collector(ebpf_module_t *em)
532
+{
533
+ filesystem_threads.thread = mallocz(sizeof(netdata_thread_t));
534
+ filesystem_threads.start_routine = ebpf_filesystem_read_hash;
535
+
536
+ netdata_thread_create(filesystem_threads.thread, filesystem_threads.name,
537
+ NETDATA_THREAD_OPTION_JOINABLE, ebpf_filesystem_read_hash, em);
538
+
539
+ while (!close_ebpf_plugin || em->optional) {
540
+ pthread_mutex_lock(&collect_data_mutex);
541
+ pthread_cond_wait(&collect_data_cond_var, &collect_data_mutex);
542
+
543
+ pthread_mutex_lock(&lock);
544
+
545
+ ebpf_create_fs_charts();
546
+ ebpf_histogram_send_data();
547
+
548
+ pthread_mutex_unlock(&collect_data_mutex);
549
+ pthread_mutex_unlock(&lock);
550
+ }
551
+}
552
+
553
+/*****************************************************************
554
+ *
555
+ * ENTRY THREAD
556
+ *
557
+ *****************************************************************/
558
+
559
+/**
560
+ * Update Filesystem
561
+ *
562
+ * Update file system structure using values read from configuration file.
563
+ */
564
+static void ebpf_update_filesystem()
565
+{
566
+ char dist[NETDATA_FS_MAX_DIST_NAME + 1];
567
+ int i;
568
+ for (i = 0; localfs[i].filesystem; i++) {
569
+ snprintfz(dist, NETDATA_FS_MAX_DIST_NAME, "%sdist", localfs[i].filesystem);
570
+
571
+ localfs[i].enabled = appconfig_get_boolean(&fs_config, NETDATA_FILESYSTEM_CONFIG_NAME, dist,
572
+ CONFIG_BOOLEAN_YES);
573
+ }
574
+}
575
+
576
+/**
577
+ * Filesystem thread
578
+ *
579
+ * Thread used to generate socket charts.
580
+ *
581
+ * @param ptr a pointer to `struct ebpf_module`
582
+ *
583
+ * @return It always return NULL
584
+ */
585
+void *ebpf_filesystem_thread(void *ptr)
586
+{
587
+ netdata_thread_cleanup_push(ebpf_filesystem_cleanup, ptr);
588
+
589
+ ebpf_module_t *em = (ebpf_module_t *)ptr;
590
+ ebpf_update_filesystem();
591
+
592
+ if (!em->enabled)
593
+ goto endfilesystem;
594
+
595
+ // Initialize optional as zero, to identify when there are not partitions to monitor
596
+ em->optional = 0;
597
+
598
+ if (ebpf_update_partitions(em)) {
599
+ if (em->optional)
600
+ info("Netdata cannot monitor the filesystems used on this host.");
601
+
602
+ em->enabled = 0;
603
+ goto endfilesystem;
604
+ }
605
+
606
+ int algorithms[NETDATA_FILESYSTEM_MAX_BINS];
607
+ ebpf_fill_algorithms(algorithms, NETDATA_FILESYSTEM_MAX_BINS, NETDATA_EBPF_INCREMENTAL_IDX);
608
+ ebpf_global_labels(filesystem_aggregated_data, filesystem_publish_aggregated, dimensions, dimensions,
609
+ algorithms, NETDATA_FILESYSTEM_MAX_BINS);
610
+
611
+ pthread_mutex_lock(&lock);
612
+ ebpf_create_fs_charts();
613
+ pthread_mutex_unlock(&lock);
614
+
615
+ filesystem_collector(em);
616
+
617
+endfilesystem:
618
+ netdata_thread_cleanup_pop(1);
619
+ return NULL;
620
+}