master
c 1,142 lines 45 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugin_proc.h"
4
5 #define PLUGIN_PROC_MODULE_BTRFS_NAME "/sys/fs/btrfs"
6
7 typedef struct btrfs_disk {
8 char *name;
9 uint32_t hash;
10 int exists;
11
12 char *size_filename;
13 unsigned long long size;
14
15 struct btrfs_disk *next;
16 } BTRFS_DISK;
17
18 typedef struct btrfs_device {
19 int id;
20 int exists;
21
22 char *error_stats_filename;
23 RRDSET *st_error_stats;
24 RRDDIM *rd_write_errs;
25 RRDDIM *rd_read_errs;
26 RRDDIM *rd_flush_errs;
27 RRDDIM *rd_corruption_errs;
28 RRDDIM *rd_generation_errs;
29 collected_number write_errs;
30 collected_number read_errs;
31 collected_number flush_errs;
32 collected_number corruption_errs;
33 collected_number generation_errs;
34
35 struct btrfs_device *next;
36 } BTRFS_DEVICE;
37
38 typedef struct btrfs_node {
39 int exists;
40 int logged_error;
41
42 char *id;
43 uint32_t hash;
44
45 char *label;
46
47 #define declare_btrfs_allocation_section_field(SECTION, FIELD) \
48 char *allocation_ ## SECTION ## _ ## FIELD ## _filename; \
49 unsigned long long int allocation_ ## SECTION ## _ ## FIELD;
50
51 #define declare_btrfs_allocation_field(FIELD) \
52 char *allocation_ ## FIELD ## _filename; \
53 unsigned long long int allocation_ ## FIELD;
54
55 RRDSET *st_allocation_disks;
56 RRDDIM *rd_allocation_disks_unallocated;
57 RRDDIM *rd_allocation_disks_data_used;
58 RRDDIM *rd_allocation_disks_data_free;
59 RRDDIM *rd_allocation_disks_metadata_used;
60 RRDDIM *rd_allocation_disks_metadata_free;
61 RRDDIM *rd_allocation_disks_system_used;
62 RRDDIM *rd_allocation_disks_system_free;
63 unsigned long long all_disks_total;
64
65 RRDSET *st_allocation_data;
66 RRDDIM *rd_allocation_data_free;
67 RRDDIM *rd_allocation_data_used;
68 declare_btrfs_allocation_section_field(data, total_bytes)
69 declare_btrfs_allocation_section_field(data, bytes_used)
70 declare_btrfs_allocation_section_field(data, disk_total)
71 declare_btrfs_allocation_section_field(data, disk_used)
72
73 RRDSET *st_allocation_metadata;
74 RRDDIM *rd_allocation_metadata_free;
75 RRDDIM *rd_allocation_metadata_used;
76 RRDDIM *rd_allocation_metadata_reserved;
77 declare_btrfs_allocation_section_field(metadata, total_bytes)
78 declare_btrfs_allocation_section_field(metadata, bytes_used)
79 declare_btrfs_allocation_section_field(metadata, disk_total)
80 declare_btrfs_allocation_section_field(metadata, disk_used)
81 //declare_btrfs_allocation_field(global_rsv_reserved)
82 declare_btrfs_allocation_field(global_rsv_size)
83
84 RRDSET *st_allocation_system;
85 RRDDIM *rd_allocation_system_free;
86 RRDDIM *rd_allocation_system_used;
87 declare_btrfs_allocation_section_field(system, total_bytes)
88 declare_btrfs_allocation_section_field(system, bytes_used)
89 declare_btrfs_allocation_section_field(system, disk_total)
90 declare_btrfs_allocation_section_field(system, disk_used)
91
92 // --------------------------------------------------------------------
93 // commit stats
94
95 char *commit_stats_filename;
96
97 RRDSET *st_commits;
98 RRDDIM *rd_commits;
99 long long commits_total;
100 collected_number commits_new;
101
102 RRDSET *st_commits_percentage_time;
103 RRDDIM *rd_commits_percentage_time;
104 long long commit_timings_total;
105 long long commits_percentage_time;
106
107 RRDSET *st_commit_timings;
108 RRDDIM *rd_commit_timings_last;
109 RRDDIM *rd_commit_timings_max;
110 collected_number commit_timings_last;
111 collected_number commit_timings_max;
112
113 BTRFS_DISK *disks;
114
115 BTRFS_DEVICE *devices;
116
117 struct btrfs_node *next;
118 } BTRFS_NODE;
119
120 static BTRFS_NODE *nodes = NULL;
121
122 static inline int collect_btrfs_error_stats(BTRFS_DEVICE *device){
123 char buffer[120 + 1];
124
125 int ret = read_txt_file(device->error_stats_filename, buffer, sizeof(buffer));
126 if(unlikely(ret)) {
127 collector_error("BTRFS: failed to read '%s'", device->error_stats_filename);
128 device->write_errs = 0;
129 device->read_errs = 0;
130 device->flush_errs = 0;
131 device->corruption_errs = 0;
132 device->generation_errs = 0;
133 return ret;
134 }
135
136 char *p = buffer;
137 while(p){
138 char *val = strsep_skip_consecutive_separators(&p, "\n");
139 if(unlikely(!val || !*val)) break;
140 char *key = strsep_skip_consecutive_separators(&val, " ");
141
142 if(!strcmp(key, "write_errs")) device->write_errs = str2ull(val, NULL);
143 else if(!strcmp(key, "read_errs")) device->read_errs = str2ull(val, NULL);
144 else if(!strcmp(key, "flush_errs")) device->flush_errs = str2ull(val, NULL);
145 else if(!strcmp(key, "corruption_errs")) device->corruption_errs = str2ull(val, NULL);
146 else if(!strcmp(key, "generation_errs")) device->generation_errs = str2ull(val, NULL);
147 }
148 return 0;
149 }
150
151 static inline int collect_btrfs_commits_stats(BTRFS_NODE *node, int update_every){
152 char buffer[120 + 1];
153
154 int ret = read_txt_file(node->commit_stats_filename, buffer, sizeof(buffer));
155 if(unlikely(ret)) {
156 collector_error("BTRFS: failed to read '%s'", node->commit_stats_filename);
157 node->commits_total = 0;
158 node->commits_new = 0;
159 node->commit_timings_last = 0;
160 node->commit_timings_max = 0;
161 node->commit_timings_total = 0;
162 node->commits_percentage_time = 0;
163
164 return ret;
165 }
166
167 char *p = buffer;
168 while(p){
169 char *val = strsep_skip_consecutive_separators(&p, "\n");
170 if(unlikely(!val || !*val)) break;
171 char *key = strsep_skip_consecutive_separators(&val, " ");
172
173 if(!strcmp(key, "commits")){
174 long long commits_total_new = str2ull(val, NULL);
175 if(likely(node->commits_total)){
176 if((node->commits_new = commits_total_new - node->commits_total))
177 node->commits_total = commits_total_new;
178 } else node->commits_total = commits_total_new;
179 }
180 else if(!strcmp(key, "last_commit_ms")) node->commit_timings_last = str2ull(val, NULL);
181 else if(!strcmp(key, "max_commit_ms")) node->commit_timings_max = str2ull(val, NULL);
182 else if(!strcmp(key, "total_commit_ms")) {
183 long long commit_timings_total_new = str2ull(val, NULL);
184 if(likely(node->commit_timings_total)){
185 long time_delta = commit_timings_total_new - node->commit_timings_total;
186 if(time_delta){
187 node->commits_percentage_time = time_delta * 10 / update_every;
188 node->commit_timings_total = commit_timings_total_new;
189 } else node->commits_percentage_time = 0;
190
191 } else node->commit_timings_total = commit_timings_total_new;
192 }
193 }
194 return 0;
195 }
196
197 static inline void btrfs_free_commits_stats(BTRFS_NODE *node){
198 if(node->st_commits){
199 rrdset_is_obsolete___safe_from_collector_thread(node->st_commits);
200 rrdset_is_obsolete___safe_from_collector_thread(node->st_commit_timings);
201 }
202 freez(node->commit_stats_filename);
203 node->commit_stats_filename = NULL;
204 }
205
206 static inline void btrfs_free_disk(BTRFS_DISK *d) {
207 freez(d->name);
208 freez(d->size_filename);
209 freez(d);
210 }
211
212 static inline void btrfs_free_device(BTRFS_DEVICE *d) {
213 if(d->st_error_stats)
214 rrdset_is_obsolete___safe_from_collector_thread(d->st_error_stats);
215 freez(d->error_stats_filename);
216 freez(d);
217 }
218
219 static inline void btrfs_free_node(BTRFS_NODE *node) {
220 // collector_info("BTRFS: destroying '%s'", node->id);
221
222 if(node->st_allocation_disks)
223 rrdset_is_obsolete___safe_from_collector_thread(node->st_allocation_disks);
224
225 if(node->st_allocation_data)
226 rrdset_is_obsolete___safe_from_collector_thread(node->st_allocation_data);
227
228 if(node->st_allocation_metadata)
229 rrdset_is_obsolete___safe_from_collector_thread(node->st_allocation_metadata);
230
231 if(node->st_allocation_system)
232 rrdset_is_obsolete___safe_from_collector_thread(node->st_allocation_system);
233
234 freez(node->allocation_data_bytes_used_filename);
235 freez(node->allocation_data_total_bytes_filename);
236
237 freez(node->allocation_metadata_bytes_used_filename);
238 freez(node->allocation_metadata_total_bytes_filename);
239
240 freez(node->allocation_system_bytes_used_filename);
241 freez(node->allocation_system_total_bytes_filename);
242
243 btrfs_free_commits_stats(node);
244
245 while(node->disks) {
246 BTRFS_DISK *d = node->disks;
247 node->disks = node->disks->next;
248 btrfs_free_disk(d);
249 }
250
251 while(node->devices) {
252 BTRFS_DEVICE *d = node->devices;
253 node->devices = node->devices->next;
254 btrfs_free_device(d);
255 }
256
257 freez(node->label);
258 freez(node->id);
259 freez(node);
260 }
261
262 static inline int find_btrfs_disks(BTRFS_NODE *node, const char *path) {
263 char filename[FILENAME_MAX + 1];
264
265 node->all_disks_total = 0;
266
267 BTRFS_DISK *d;
268 for(d = node->disks ; d ; d = d->next)
269 d->exists = 0;
270
271 DIR *dir = opendir(path);
272 if (!dir) {
273 if (!node->logged_error) {
274 nd_log(NDLS_COLLECTORS, errno == ENOENT ? NDLP_INFO : NDLP_ERR, "BTRFS: Cannot open directory '%s'.", path);
275 node->logged_error = 1;
276 }
277 return 1;
278 }
279 node->logged_error = 0;
280
281 struct dirent *de = NULL;
282 while ((de = readdir(dir))) {
283 if (de->d_type != DT_LNK
284 || !strcmp(de->d_name, ".")
285 || !strcmp(de->d_name, "..")
286 ) {
287 // collector_info("BTRFS: ignoring '%s'", de->d_name);
288 continue;
289 }
290
291 uint32_t hash = simple_hash(de->d_name);
292
293 // --------------------------------------------------------------------
294 // search for it
295
296 for(d = node->disks ; d ; d = d->next) {
297 if(hash == d->hash && !strcmp(de->d_name, d->name))
298 break;
299 }
300
301 // --------------------------------------------------------------------
302 // did we find it?
303
304 if(!d) {
305 d = callocz(sizeof(BTRFS_DISK), 1);
306
307 d->name = strdupz(de->d_name);
308 d->hash = simple_hash(d->name);
309
310 snprintfz(filename, FILENAME_MAX, "%s/%s/size", path, de->d_name);
311 d->size_filename = strdupz(filename);
312
313 // link it
314 d->next = node->disks;
315 node->disks = d;
316 }
317
318 d->exists = 1;
319
320
321 // --------------------------------------------------------------------
322 // update the values
323
324 if(read_single_number_file(d->size_filename, &d->size) != 0) {
325 collector_error("BTRFS: failed to read '%s'", d->size_filename);
326 d->exists = 0;
327 continue;
328 }
329
330 // /sys/block/<name>/size is in fixed-size sectors of 512 bytes
331 // https://github.com/torvalds/linux/blob/v6.2/block/genhd.c#L946-L950
332 // https://github.com/torvalds/linux/blob/v6.2/include/linux/types.h#L120-L121
333 // (also see #3481, #3483)
334 node->all_disks_total += d->size * 512;
335 }
336 closedir(dir);
337
338 // ------------------------------------------------------------------------
339 // cleanup
340
341 BTRFS_DISK *last = NULL;
342 d = node->disks;
343
344 while(d) {
345 if(unlikely(!d->exists)) {
346 if(unlikely(node->disks == d)) {
347 node->disks = d->next;
348 btrfs_free_disk(d);
349 d = node->disks;
350 last = NULL;
351 }
352 else {
353 last->next = d->next;
354 btrfs_free_disk(d);
355 d = last->next;
356 }
357
358 continue;
359 }
360
361 last = d;
362 d = d->next;
363 }
364
365 return 0;
366 }
367
368 static inline int find_btrfs_devices(BTRFS_NODE *node, const char *path) {
369 char filename[FILENAME_MAX + 1];
370
371 BTRFS_DEVICE *d;
372 for(d = node->devices ; d ; d = d->next)
373 d->exists = 0;
374
375 DIR *dir = opendir(path);
376 if (!dir) {
377 if (!node->logged_error) {
378 nd_log(NDLS_COLLECTORS, errno == ENOENT ? NDLP_INFO : NDLP_ERR, "BTRFS: Cannot open directory '%s'.", path);
379 node->logged_error = 1;
380 }
381 return 1;
382 }
383 node->logged_error = 0;
384
385 struct dirent *de = NULL;
386 while ((de = readdir(dir))) {
387 if (de->d_type != DT_DIR
388 || !strcmp(de->d_name, ".")
389 || !strcmp(de->d_name, "..")
390 ) {
391 // collector_info("BTRFS: ignoring '%s'", de->d_name);
392 continue;
393 }
394
395 // internal_error("BTRFS: device found '%s'", de->d_name);
396
397 // --------------------------------------------------------------------
398 // search for it
399
400 for(d = node->devices ; d ; d = d->next) {
401 if(str2ll(de->d_name, NULL) == d->id){
402 // collector_info("BTRFS: existing device id '%d'", d->id);
403 break;
404 }
405 }
406
407 // --------------------------------------------------------------------
408 // did we find it?
409
410 if(!d) {
411 d = callocz(sizeof(BTRFS_DEVICE), 1);
412
413 d->id = str2ll(de->d_name, NULL);
414 // collector_info("BTRFS: new device with id '%d'", d->id);
415
416 snprintfz(filename, FILENAME_MAX, "%s/%d/error_stats", path, d->id);
417 d->error_stats_filename = strdupz(filename);
418 // collector_info("BTRFS: error_stats_filename '%s'", filename);
419
420 // link it
421 d->next = node->devices;
422 node->devices = d;
423 }
424
425 d->exists = 1;
426
427
428 // --------------------------------------------------------------------
429 // update the values
430
431 if(unlikely(collect_btrfs_error_stats(d)))
432 d->exists = 0; // 'd' will be garbaged collected in loop below
433 }
434 closedir(dir);
435
436 // ------------------------------------------------------------------------
437 // cleanup
438
439 BTRFS_DEVICE *last = NULL;
440 d = node->devices;
441
442 while(d) {
443 if(unlikely(!d->exists)) {
444 if(unlikely(node->devices == d)) {
445 node->devices = d->next;
446 btrfs_free_device(d);
447 d = node->devices;
448 last = NULL;
449 }
450 else {
451 last->next = d->next;
452 btrfs_free_device(d);
453 d = last->next;
454 }
455
456 continue;
457 }
458
459 last = d;
460 d = d->next;
461 }
462
463 return 0;
464 }
465
466
467 static inline int find_all_btrfs_pools(const char *path, int update_every) {
468 static int logged_error = 0;
469 char filename[FILENAME_MAX + 1];
470
471 BTRFS_NODE *node;
472 for(node = nodes ; node ; node = node->next)
473 node->exists = 0;
474
475 DIR *dir = opendir(path);
476 if (!dir) {
477 if (!logged_error) {
478 nd_log(NDLS_COLLECTORS, errno == ENOENT ? NDLP_INFO : NDLP_ERR, "BTRFS: Cannot open directory '%s'.", path);
479 logged_error = 1;
480 }
481 return 1;
482 }
483 logged_error = 0;
484
485 struct dirent *de = NULL;
486 while ((de = readdir(dir))) {
487 if(de->d_type != DT_DIR
488 || !strcmp(de->d_name, ".")
489 || !strcmp(de->d_name, "..")
490 || !strcmp(de->d_name, "features")
491 ) {
492 // collector_info("BTRFS: ignoring '%s'", de->d_name);
493 continue;
494 }
495
496 uint32_t hash = simple_hash(de->d_name);
497
498 // search for it
499 for(node = nodes ; node ; node = node->next) {
500 if(hash == node->hash && !strcmp(de->d_name, node->id))
501 break;
502 }
503
504 // did we find it?
505 if(node) {
506 // collector_info("BTRFS: already exists '%s'", de->d_name);
507 node->exists = 1;
508
509 // update the disk sizes
510 snprintfz(filename, FILENAME_MAX, "%s/%s/devices", path, de->d_name);
511 find_btrfs_disks(node, filename);
512
513 // update devices
514 snprintfz(filename, FILENAME_MAX, "%s/%s/devinfo", path, de->d_name);
515 find_btrfs_devices(node, filename);
516
517 continue;
518 }
519
520 // collector_info("BTRFS: adding '%s'", de->d_name);
521
522 // not found, create it
523 node = callocz(sizeof(BTRFS_NODE), 1);
524
525 node->id = strdupz(de->d_name);
526 node->hash = simple_hash(node->id);
527 node->exists = 1;
528
529 {
530 char label[FILENAME_MAX + 1] = "";
531
532 snprintfz(filename, FILENAME_MAX, "%s/%s/label", path, de->d_name);
533 if(read_txt_file(filename, label, sizeof(label)) != 0) {
534 collector_error("BTRFS: failed to read '%s'", filename);
535 btrfs_free_node(node);
536 continue;
537 }
538
539 char *s = label;
540 if (s[0])
541 s = trim(label);
542
543 if(s && s[0])
544 node->label = strdupz(s);
545 else
546 node->label = strdupz(node->id);
547 }
548
549 // --------------------------------------------------------------------
550 // macros to simplify our life
551
552 #define init_btrfs_allocation_field(FIELD) {\
553 snprintfz(filename, FILENAME_MAX, "%s/%s/allocation/" #FIELD, path, de->d_name); \
554 if(read_single_number_file(filename, &node->allocation_ ## FIELD) != 0) {\
555 collector_error("BTRFS: failed to read '%s'", filename);\
556 btrfs_free_node(node);\
557 continue;\
558 }\
559 if(!node->allocation_ ## FIELD ## _filename)\
560 node->allocation_ ## FIELD ## _filename = strdupz(filename);\
561 }
562
563 #define init_btrfs_allocation_section_field(SECTION, FIELD) {\
564 snprintfz(filename, FILENAME_MAX, "%s/%s/allocation/" #SECTION "/" #FIELD, path, de->d_name); \
565 if(read_single_number_file(filename, &node->allocation_ ## SECTION ## _ ## FIELD) != 0) {\
566 collector_error("BTRFS: failed to read '%s'", filename);\
567 btrfs_free_node(node);\
568 continue;\
569 }\
570 if(!node->allocation_ ## SECTION ## _ ## FIELD ## _filename)\
571 node->allocation_ ## SECTION ## _ ## FIELD ## _filename = strdupz(filename);\
572 }
573
574 // --------------------------------------------------------------------
575 // allocation/data
576
577 init_btrfs_allocation_section_field(data, total_bytes);
578 init_btrfs_allocation_section_field(data, bytes_used);
579 init_btrfs_allocation_section_field(data, disk_total);
580 init_btrfs_allocation_section_field(data, disk_used);
581
582
583 // --------------------------------------------------------------------
584 // allocation/metadata
585
586 init_btrfs_allocation_section_field(metadata, total_bytes);
587 init_btrfs_allocation_section_field(metadata, bytes_used);
588 init_btrfs_allocation_section_field(metadata, disk_total);
589 init_btrfs_allocation_section_field(metadata, disk_used);
590
591 init_btrfs_allocation_field(global_rsv_size);
592 // init_btrfs_allocation_field(global_rsv_reserved);
593
594
595 // --------------------------------------------------------------------
596 // allocation/system
597
598 init_btrfs_allocation_section_field(system, total_bytes);
599 init_btrfs_allocation_section_field(system, bytes_used);
600 init_btrfs_allocation_section_field(system, disk_total);
601 init_btrfs_allocation_section_field(system, disk_used);
602
603 // --------------------------------------------------------------------
604 // commit stats
605
606 snprintfz(filename, FILENAME_MAX, "%s/%s/commit_stats", path, de->d_name);
607 if(!node->commit_stats_filename) node->commit_stats_filename = strdupz(filename);
608 if(unlikely(collect_btrfs_commits_stats(node, update_every))){
609 collector_error("BTRFS: failed to collect commit stats for '%s'", node->id);
610 btrfs_free_commits_stats(node);
611 }
612
613 // --------------------------------------------------------------------
614 // find all disks related to this node
615 // and collect their sizes
616
617 snprintfz(filename, FILENAME_MAX, "%s/%s/devices", path, de->d_name);
618 find_btrfs_disks(node, filename);
619
620 // --------------------------------------------------------------------
621 // find all devices related to this node
622
623 snprintfz(filename, FILENAME_MAX, "%s/%s/devinfo", path, de->d_name);
624 find_btrfs_devices(node, filename);
625
626 // --------------------------------------------------------------------
627 // link it
628
629 // collector_info("BTRFS: linking '%s'", node->id);
630 node->next = nodes;
631 nodes = node;
632 }
633 closedir(dir);
634
635
636 // ------------------------------------------------------------------------
637 // cleanup
638
639 BTRFS_NODE *last = NULL;
640 node = nodes;
641
642 while(node) {
643 if(unlikely(!node->exists)) {
644 if(unlikely(nodes == node)) {
645 nodes = node->next;
646 btrfs_free_node(node);
647 node = nodes;
648 last = NULL;
649 }
650 else {
651 last->next = node->next;
652 btrfs_free_node(node);
653 node = last->next;
654 }
655
656 continue;
657 }
658
659 last = node;
660 node = node->next;
661 }
662
663 return 0;
664 }
665
666 static void add_labels_to_btrfs(BTRFS_NODE *n, RRDSET *st) {
667 rrdlabels_add(st->rrdlabels, "filesystem_uuid", n->id, RRDLABEL_SRC_AUTO);
668 rrdlabels_add(st->rrdlabels, "filesystem_label", n->label, RRDLABEL_SRC_AUTO);
669 }
670
671 int do_sys_fs_btrfs(int update_every, usec_t dt) {
672 static int initialized = 0
673 , do_allocation_disks = CONFIG_BOOLEAN_AUTO
674 , do_allocation_system = CONFIG_BOOLEAN_AUTO
675 , do_allocation_data = CONFIG_BOOLEAN_AUTO
676 , do_allocation_metadata = CONFIG_BOOLEAN_AUTO
677 , do_commit_stats = CONFIG_BOOLEAN_AUTO
678 , do_error_stats = CONFIG_BOOLEAN_AUTO;
679
680 static usec_t refresh_delta = 0, refresh_every = 60 * USEC_PER_SEC;
681 static const char *btrfs_path = NULL;
682
683 (void)dt;
684
685 if(unlikely(!initialized)) {
686 initialized = 1;
687
688 char filename[FILENAME_MAX + 1];
689 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/fs/btrfs");
690 btrfs_path = inicfg_get(&netdata_config, "plugin:proc:/sys/fs/btrfs", "path to monitor", filename);
691
692 refresh_every = inicfg_get_duration_seconds(&netdata_config, "plugin:proc:/sys/fs/btrfs", "check for btrfs changes every", refresh_every / USEC_PER_SEC) * USEC_PER_SEC;
693 refresh_delta = refresh_every;
694
695 do_allocation_disks = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/sys/fs/btrfs", "physical disks allocation", do_allocation_disks);
696 do_allocation_data = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/sys/fs/btrfs", "data allocation", do_allocation_data);
697 do_allocation_metadata = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/sys/fs/btrfs", "metadata allocation", do_allocation_metadata);
698 do_allocation_system = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/sys/fs/btrfs", "system allocation", do_allocation_system);
699 do_commit_stats = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/sys/fs/btrfs", "commit stats", do_commit_stats);
700 do_error_stats = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/sys/fs/btrfs", "error stats", do_error_stats);
701 }
702
703 refresh_delta += dt;
704 if(refresh_delta >= refresh_every) {
705 refresh_delta = 0;
706 find_all_btrfs_pools(btrfs_path, update_every);
707 }
708
709 BTRFS_NODE *node;
710 for(node = nodes; node ; node = node->next) {
711 // --------------------------------------------------------------------
712 // allocation/system
713
714 #define collect_btrfs_allocation_field(FIELD) \
715 read_single_number_file(node->allocation_ ## FIELD ## _filename, &node->allocation_ ## FIELD)
716
717 #define collect_btrfs_allocation_section_field(SECTION, FIELD) \
718 read_single_number_file(node->allocation_ ## SECTION ## _ ## FIELD ## _filename, &node->allocation_ ## SECTION ## _ ## FIELD)
719
720 if(do_allocation_disks != CONFIG_BOOLEAN_NO) {
721 if( collect_btrfs_allocation_section_field(data, disk_total) != 0
722 || collect_btrfs_allocation_section_field(data, disk_used) != 0
723 || collect_btrfs_allocation_section_field(metadata, disk_total) != 0
724 || collect_btrfs_allocation_section_field(metadata, disk_used) != 0
725 || collect_btrfs_allocation_section_field(system, disk_total) != 0
726 || collect_btrfs_allocation_section_field(system, disk_used) != 0) {
727 collector_error("BTRFS: failed to collect physical disks allocation for '%s'", node->id);
728 // make it refresh btrfs at the next iteration
729 refresh_delta = refresh_every;
730 continue;
731 }
732 }
733
734 if(do_allocation_data != CONFIG_BOOLEAN_NO) {
735 if (collect_btrfs_allocation_section_field(data, total_bytes) != 0
736 || collect_btrfs_allocation_section_field(data, bytes_used) != 0) {
737 collector_error("BTRFS: failed to collect allocation/data for '%s'", node->id);
738 // make it refresh btrfs at the next iteration
739 refresh_delta = refresh_every;
740 continue;
741 }
742 }
743
744 if(do_allocation_metadata != CONFIG_BOOLEAN_NO) {
745 if (collect_btrfs_allocation_section_field(metadata, total_bytes) != 0
746 || collect_btrfs_allocation_section_field(metadata, bytes_used) != 0
747 || collect_btrfs_allocation_field(global_rsv_size) != 0
748 ) {
749 collector_error("BTRFS: failed to collect allocation/metadata for '%s'", node->id);
750 // make it refresh btrfs at the next iteration
751 refresh_delta = refresh_every;
752 continue;
753 }
754 }
755
756 if(do_allocation_system != CONFIG_BOOLEAN_NO) {
757 if (collect_btrfs_allocation_section_field(system, total_bytes) != 0
758 || collect_btrfs_allocation_section_field(system, bytes_used) != 0) {
759 collector_error("BTRFS: failed to collect allocation/system for '%s'", node->id);
760 // make it refresh btrfs at the next iteration
761 refresh_delta = refresh_every;
762 continue;
763 }
764 }
765
766 if(do_commit_stats != CONFIG_BOOLEAN_NO && node->commit_stats_filename) {
767 if (unlikely(collect_btrfs_commits_stats(node, update_every))) {
768 collector_error("BTRFS: failed to collect commit stats for '%s'", node->id);
769 btrfs_free_commits_stats(node);
770 }
771 }
772
773 if(do_error_stats != CONFIG_BOOLEAN_NO) {
774 for(BTRFS_DEVICE *d = node->devices ; d ; d = d->next) {
775 if(unlikely(collect_btrfs_error_stats(d))){
776 collector_error("BTRFS: failed to collect error stats for '%s', devid:'%d'", node->id, d->id);
777 /* make it refresh btrfs at the next iteration,
778 * btrfs_free_device(d) will be called in
779 * find_btrfs_devices() as part of the garbage collection */
780 refresh_delta = refresh_every;
781 }
782 }
783 }
784
785 // --------------------------------------------------------------------
786 // allocation/disks
787
788 if (do_allocation_disks == CONFIG_BOOLEAN_YES || do_allocation_disks == CONFIG_BOOLEAN_AUTO) {
789 do_allocation_disks = CONFIG_BOOLEAN_YES;
790
791 if(unlikely(!node->st_allocation_disks)) {
792 char id[RRD_ID_LENGTH_MAX + 1], name[RRD_ID_LENGTH_MAX + 1], title[200 + 1];
793
794 snprintfz(id, RRD_ID_LENGTH_MAX, "disk_%s", node->id);
795 snprintfz(name, RRD_ID_LENGTH_MAX, "disk_%s", node->label);
796 snprintfz(title, sizeof(title) - 1, "BTRFS Physical Disk Allocation");
797
798 netdata_fix_chart_id(id);
799 netdata_fix_chart_name(name);
800
801 node->st_allocation_disks = rrdset_create_localhost(
802 "btrfs"
803 , id
804 , name
805 , node->label
806 , "btrfs.disk"
807 , title
808 , "MiB"
809 , PLUGIN_PROC_NAME
810 , PLUGIN_PROC_MODULE_BTRFS_NAME
811 , NETDATA_CHART_PRIO_BTRFS_DISK
812 , update_every
813 , RRDSET_TYPE_STACKED
814 );
815
816 node->rd_allocation_disks_unallocated = rrddim_add(node->st_allocation_disks, "unallocated", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
817 node->rd_allocation_disks_data_free = rrddim_add(node->st_allocation_disks, "data_free", "data free", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
818 node->rd_allocation_disks_data_used = rrddim_add(node->st_allocation_disks, "data_used", "data used", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
819 node->rd_allocation_disks_metadata_free = rrddim_add(node->st_allocation_disks, "meta_free", "meta free", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
820 node->rd_allocation_disks_metadata_used = rrddim_add(node->st_allocation_disks, "meta_used", "meta used", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
821 node->rd_allocation_disks_system_free = rrddim_add(node->st_allocation_disks, "sys_free", "sys free", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
822 node->rd_allocation_disks_system_used = rrddim_add(node->st_allocation_disks, "sys_used", "sys used", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
823
824 add_labels_to_btrfs(node, node->st_allocation_disks);
825 }
826
827 // unsigned long long disk_used = node->allocation_data_disk_used + node->allocation_metadata_disk_used + node->allocation_system_disk_used;
828 unsigned long long disk_total = node->allocation_data_disk_total + node->allocation_metadata_disk_total + node->allocation_system_disk_total;
829 unsigned long long disk_unallocated = node->all_disks_total - disk_total;
830
831 rrddim_set_by_pointer(node->st_allocation_disks, node->rd_allocation_disks_unallocated, disk_unallocated);
832 rrddim_set_by_pointer(node->st_allocation_disks, node->rd_allocation_disks_data_used, node->allocation_data_disk_used);
833 rrddim_set_by_pointer(node->st_allocation_disks, node->rd_allocation_disks_data_free, node->allocation_data_disk_total - node->allocation_data_disk_used);
834 rrddim_set_by_pointer(node->st_allocation_disks, node->rd_allocation_disks_metadata_used, node->allocation_metadata_disk_used);
835 rrddim_set_by_pointer(node->st_allocation_disks, node->rd_allocation_disks_metadata_free, node->allocation_metadata_disk_total - node->allocation_metadata_disk_used);
836 rrddim_set_by_pointer(node->st_allocation_disks, node->rd_allocation_disks_system_used, node->allocation_system_disk_used);
837 rrddim_set_by_pointer(node->st_allocation_disks, node->rd_allocation_disks_system_free, node->allocation_system_disk_total - node->allocation_system_disk_used);
838 rrdset_done(node->st_allocation_disks);
839 }
840
841 // --------------------------------------------------------------------
842 // allocation/data
843
844 if (do_allocation_data == CONFIG_BOOLEAN_YES || do_allocation_data == CONFIG_BOOLEAN_AUTO) {
845 do_allocation_data = CONFIG_BOOLEAN_YES;
846
847 if(unlikely(!node->st_allocation_data)) {
848 char id[RRD_ID_LENGTH_MAX + 1], name[RRD_ID_LENGTH_MAX + 1], title[200 + 1];
849
850 snprintfz(id, RRD_ID_LENGTH_MAX, "data_%s", node->id);
851 snprintfz(name, RRD_ID_LENGTH_MAX, "data_%s", node->label);
852 snprintfz(title, sizeof(title) - 1, "BTRFS Data Allocation");
853
854 netdata_fix_chart_id(id);
855 netdata_fix_chart_name(name);
856
857 node->st_allocation_data = rrdset_create_localhost(
858 "btrfs"
859 , id
860 , name
861 , node->label
862 , "btrfs.data"
863 , title
864 , "MiB"
865 , PLUGIN_PROC_NAME
866 , PLUGIN_PROC_MODULE_BTRFS_NAME
867 , NETDATA_CHART_PRIO_BTRFS_DATA
868 , update_every
869 , RRDSET_TYPE_STACKED
870 );
871
872 node->rd_allocation_data_free = rrddim_add(node->st_allocation_data, "free", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
873 node->rd_allocation_data_used = rrddim_add(node->st_allocation_data, "used", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
874
875 add_labels_to_btrfs(node, node->st_allocation_data);
876 }
877
878 rrddim_set_by_pointer(node->st_allocation_data, node->rd_allocation_data_free, node->allocation_data_total_bytes - node->allocation_data_bytes_used);
879 rrddim_set_by_pointer(node->st_allocation_data, node->rd_allocation_data_used, node->allocation_data_bytes_used);
880 rrdset_done(node->st_allocation_data);
881 }
882
883 // --------------------------------------------------------------------
884 // allocation/metadata
885
886 if (do_allocation_metadata == CONFIG_BOOLEAN_YES || do_allocation_metadata == CONFIG_BOOLEAN_AUTO) {
887 do_allocation_metadata = CONFIG_BOOLEAN_YES;
888
889 if(unlikely(!node->st_allocation_metadata)) {
890 char id[RRD_ID_LENGTH_MAX + 1], name[RRD_ID_LENGTH_MAX + 1], title[200 + 1];
891
892 snprintfz(id, RRD_ID_LENGTH_MAX, "metadata_%s", node->id);
893 snprintfz(name, RRD_ID_LENGTH_MAX, "metadata_%s", node->label);
894 snprintfz(title, sizeof(title) - 1, "BTRFS Metadata Allocation");
895
896 netdata_fix_chart_id(id);
897 netdata_fix_chart_name(name);
898
899 node->st_allocation_metadata = rrdset_create_localhost(
900 "btrfs"
901 , id
902 , name
903 , node->label
904 , "btrfs.metadata"
905 , title
906 , "MiB"
907 , PLUGIN_PROC_NAME
908 , PLUGIN_PROC_MODULE_BTRFS_NAME
909 , NETDATA_CHART_PRIO_BTRFS_METADATA
910 , update_every
911 , RRDSET_TYPE_STACKED
912 );
913
914 node->rd_allocation_metadata_free = rrddim_add(node->st_allocation_metadata, "free", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
915 node->rd_allocation_metadata_used = rrddim_add(node->st_allocation_metadata, "used", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
916 node->rd_allocation_metadata_reserved = rrddim_add(node->st_allocation_metadata, "reserved", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
917
918 add_labels_to_btrfs(node, node->st_allocation_metadata);
919 }
920
921 rrddim_set_by_pointer(node->st_allocation_metadata, node->rd_allocation_metadata_free, node->allocation_metadata_total_bytes - node->allocation_metadata_bytes_used - node->allocation_global_rsv_size);
922 rrddim_set_by_pointer(node->st_allocation_metadata, node->rd_allocation_metadata_used, node->allocation_metadata_bytes_used);
923 rrddim_set_by_pointer(node->st_allocation_metadata, node->rd_allocation_metadata_reserved, node->allocation_global_rsv_size);
924 rrdset_done(node->st_allocation_metadata);
925 }
926
927 // --------------------------------------------------------------------
928 // allocation/system
929
930 if (do_allocation_system == CONFIG_BOOLEAN_YES || do_allocation_system == CONFIG_BOOLEAN_AUTO) {
931 do_allocation_system = CONFIG_BOOLEAN_YES;
932
933 if(unlikely(!node->st_allocation_system)) {
934 char id[RRD_ID_LENGTH_MAX + 1], name[RRD_ID_LENGTH_MAX + 1], title[200 + 1];
935
936 snprintfz(id, RRD_ID_LENGTH_MAX, "system_%s", node->id);
937 snprintfz(name, RRD_ID_LENGTH_MAX, "system_%s", node->label);
938 snprintfz(title, sizeof(title) - 1, "BTRFS System Allocation");
939
940 netdata_fix_chart_id(id);
941 netdata_fix_chart_name(name);
942
943 node->st_allocation_system = rrdset_create_localhost(
944 "btrfs"
945 , id
946 , name
947 , node->label
948 , "btrfs.system"
949 , title
950 , "MiB"
951 , PLUGIN_PROC_NAME
952 , PLUGIN_PROC_MODULE_BTRFS_NAME
953 , NETDATA_CHART_PRIO_BTRFS_SYSTEM
954 , update_every
955 , RRDSET_TYPE_STACKED
956 );
957
958 node->rd_allocation_system_free = rrddim_add(node->st_allocation_system, "free", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
959 node->rd_allocation_system_used = rrddim_add(node->st_allocation_system, "used", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
960
961 add_labels_to_btrfs(node, node->st_allocation_system);
962 }
963
964 rrddim_set_by_pointer(node->st_allocation_system, node->rd_allocation_system_free, node->allocation_system_total_bytes - node->allocation_system_bytes_used);
965 rrddim_set_by_pointer(node->st_allocation_system, node->rd_allocation_system_used, node->allocation_system_bytes_used);
966 rrdset_done(node->st_allocation_system);
967 }
968
969 // --------------------------------------------------------------------
970 // commit_stats
971
972 if (do_commit_stats == CONFIG_BOOLEAN_YES || do_commit_stats == CONFIG_BOOLEAN_AUTO) {
973 do_commit_stats = CONFIG_BOOLEAN_YES;
974
975 if(unlikely(!node->st_commits)) {
976 char id[RRD_ID_LENGTH_MAX + 1], name[RRD_ID_LENGTH_MAX + 1], title[200 + 1];
977
978 snprintfz(id, RRD_ID_LENGTH_MAX, "commits_%s", node->id);
979 snprintfz(name, RRD_ID_LENGTH_MAX, "commits_%s", node->label);
980 snprintfz(title, sizeof(title) - 1, "BTRFS Commits");
981
982 netdata_fix_chart_id(id);
983 netdata_fix_chart_name(name);
984
985 node->st_commits = rrdset_create_localhost(
986 "btrfs"
987 , id
988 , name
989 , node->label
990 , "btrfs.commits"
991 , title
992 , "commits"
993 , PLUGIN_PROC_NAME
994 , PLUGIN_PROC_MODULE_BTRFS_NAME
995 , NETDATA_CHART_PRIO_BTRFS_COMMITS
996 , update_every
997 , RRDSET_TYPE_LINE
998 );
999
1000 node->rd_commits = rrddim_add(node->st_commits, "commits", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1001
1002 add_labels_to_btrfs(node, node->st_commits);
1003 }
1004
1005 rrddim_set_by_pointer(node->st_commits, node->rd_commits, node->commits_new);
1006 rrdset_done(node->st_commits);
1007
1008 if(unlikely(!node->st_commits_percentage_time)) {
1009 char id[RRD_ID_LENGTH_MAX + 1], name[RRD_ID_LENGTH_MAX + 1], title[200 + 1];
1010
1011 snprintfz(id, RRD_ID_LENGTH_MAX, "commits_perc_time_%s", node->id);
1012 snprintfz(name, RRD_ID_LENGTH_MAX, "commits_perc_time_%s", node->label);
1013 snprintfz(title, sizeof(title) - 1, "BTRFS Commits Time Share");
1014
1015 netdata_fix_chart_id(id);
1016 netdata_fix_chart_name(name);
1017
1018 node->st_commits_percentage_time = rrdset_create_localhost(
1019 "btrfs"
1020 , id
1021 , name
1022 , node->label
1023 , "btrfs.commits_perc_time"
1024 , title
1025 , "percentage"
1026 , PLUGIN_PROC_NAME
1027 , PLUGIN_PROC_MODULE_BTRFS_NAME
1028 , NETDATA_CHART_PRIO_BTRFS_COMMITS_PERC_TIME
1029 , update_every
1030 , RRDSET_TYPE_LINE
1031 );
1032
1033 node->rd_commits_percentage_time = rrddim_add(node->st_commits_percentage_time, "commits", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
1034
1035 add_labels_to_btrfs(node, node->st_commits_percentage_time);
1036 }
1037
1038 rrddim_set_by_pointer(node->st_commits_percentage_time, node->rd_commits_percentage_time, node->commits_percentage_time);
1039 rrdset_done(node->st_commits_percentage_time);
1040
1041
1042 if(unlikely(!node->st_commit_timings)) {
1043 char id[RRD_ID_LENGTH_MAX + 1], name[RRD_ID_LENGTH_MAX + 1], title[200 + 1];
1044
1045 snprintfz(id, RRD_ID_LENGTH_MAX, "commit_timings_%s", node->id);
1046 snprintfz(name, RRD_ID_LENGTH_MAX, "commit_timings_%s", node->label);
1047 snprintfz(title, sizeof(title) - 1, "BTRFS Commit Timings");
1048
1049 netdata_fix_chart_id(id);
1050 netdata_fix_chart_name(name);
1051
1052 node->st_commit_timings = rrdset_create_localhost(
1053 "btrfs"
1054 , id
1055 , name
1056 , node->label
1057 , "btrfs.commit_timings"
1058 , title
1059 , "ms"
1060 , PLUGIN_PROC_NAME
1061 , PLUGIN_PROC_MODULE_BTRFS_NAME
1062 , NETDATA_CHART_PRIO_BTRFS_COMMIT_TIMINGS
1063 , update_every
1064 , RRDSET_TYPE_LINE
1065 );
1066
1067 node->rd_commit_timings_last = rrddim_add(node->st_commit_timings, "last", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1068 node->rd_commit_timings_max = rrddim_add(node->st_commit_timings, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1069
1070 add_labels_to_btrfs(node, node->st_commit_timings);
1071 }
1072
1073 rrddim_set_by_pointer(node->st_commit_timings, node->rd_commit_timings_last, node->commit_timings_last);
1074 rrddim_set_by_pointer(node->st_commit_timings, node->rd_commit_timings_max, node->commit_timings_max);
1075 rrdset_done(node->st_commit_timings);
1076 }
1077
1078 // --------------------------------------------------------------------
1079 // error_stats per device
1080
1081 if (do_error_stats == CONFIG_BOOLEAN_YES || do_error_stats == CONFIG_BOOLEAN_AUTO) {
1082 do_error_stats = CONFIG_BOOLEAN_YES;
1083
1084 for(BTRFS_DEVICE *d = node->devices ; d ; d = d->next) {
1085
1086 if(unlikely(!d->st_error_stats)) {
1087 char id[RRD_ID_LENGTH_MAX + 1], name[RRD_ID_LENGTH_MAX + 1], title[200 + 1];
1088
1089 snprintfz(id, RRD_ID_LENGTH_MAX, "device_errors_dev%d_%s", d->id, node->id);
1090 snprintfz(name, RRD_ID_LENGTH_MAX, "device_errors_dev%d_%s", d->id, node->label);
1091 snprintfz(title, sizeof(title) - 1, "BTRFS Device Errors");
1092
1093 netdata_fix_chart_id(id);
1094 netdata_fix_chart_name(name);
1095
1096 d->st_error_stats = rrdset_create_localhost(
1097 "btrfs"
1098 , id
1099 , name
1100 , node->label
1101 , "btrfs.device_errors"
1102 , title
1103 , "errors"
1104 , PLUGIN_PROC_NAME
1105 , PLUGIN_PROC_MODULE_BTRFS_NAME
1106 , NETDATA_CHART_PRIO_BTRFS_ERRORS
1107 , update_every
1108 , RRDSET_TYPE_LINE
1109 );
1110
1111 char rd_id[RRD_ID_LENGTH_MAX + 1];
1112 snprintfz(rd_id, RRD_ID_LENGTH_MAX, "write_errs");
1113 d->rd_write_errs = rrddim_add(d->st_error_stats, rd_id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1114 snprintfz(rd_id, RRD_ID_LENGTH_MAX, "read_errs");
1115 d->rd_read_errs = rrddim_add(d->st_error_stats, rd_id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1116 snprintfz(rd_id, RRD_ID_LENGTH_MAX, "flush_errs");
1117 d->rd_flush_errs = rrddim_add(d->st_error_stats, rd_id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1118 snprintfz(rd_id, RRD_ID_LENGTH_MAX, "corruption_errs");
1119 d->rd_corruption_errs = rrddim_add(d->st_error_stats, rd_id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1120 snprintfz(rd_id, RRD_ID_LENGTH_MAX, "generation_errs");
1121 d->rd_generation_errs = rrddim_add(d->st_error_stats, rd_id, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1122
1123 char dev_id[5];
1124 snprintfz(dev_id, 4, "%d", d->id);
1125 rrdlabels_add(d->st_error_stats->rrdlabels, "device_id", dev_id, RRDLABEL_SRC_AUTO);
1126 add_labels_to_btrfs(node, d->st_error_stats);
1127 }
1128
1129 rrddim_set_by_pointer(d->st_error_stats, d->rd_write_errs, d->write_errs);
1130 rrddim_set_by_pointer(d->st_error_stats, d->rd_read_errs, d->read_errs);
1131 rrddim_set_by_pointer(d->st_error_stats, d->rd_flush_errs, d->flush_errs);
1132 rrddim_set_by_pointer(d->st_error_stats, d->rd_corruption_errs, d->corruption_errs);
1133 rrddim_set_by_pointer(d->st_error_stats, d->rd_generation_errs, d->generation_errs);
1134
1135 rrdset_done(d->st_error_stats);
1136 }
1137 }
1138 }
1139
1140 return 0;
1141 }
1142