sys_fs_btrfs.c 46 KB

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