| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "plugin_proc.h" |
| 4 | |
| 5 | #define PLUGIN_PROC_MODULE_MDSTAT_NAME "/proc/mdstat" |
| 6 | |
| 7 | struct raid { |
| 8 | int redundant; |
| 9 | char *name; |
| 10 | uint32_t hash; |
| 11 | char *level; |
| 12 | |
| 13 | RRDDIM *rd_health; |
| 14 | unsigned long long failed_disks; |
| 15 | |
| 16 | RRDSET *st_disks; |
| 17 | RRDDIM *rd_down; |
| 18 | RRDDIM *rd_inuse; |
| 19 | unsigned long long total_disks; |
| 20 | unsigned long long inuse_disks; |
| 21 | |
| 22 | RRDSET *st_operation; |
| 23 | RRDDIM *rd_check; |
| 24 | RRDDIM *rd_resync; |
| 25 | RRDDIM *rd_recovery; |
| 26 | RRDDIM *rd_reshape; |
| 27 | unsigned long long check; |
| 28 | unsigned long long resync; |
| 29 | unsigned long long recovery; |
| 30 | unsigned long long reshape; |
| 31 | |
| 32 | RRDSET *st_finish; |
| 33 | RRDDIM *rd_finish_in; |
| 34 | unsigned long long finish_in; |
| 35 | |
| 36 | RRDSET *st_speed; |
| 37 | RRDDIM *rd_speed; |
| 38 | unsigned long long speed; |
| 39 | |
| 40 | char *mismatch_cnt_filename; |
| 41 | RRDSET *st_mismatch_cnt; |
| 42 | RRDDIM *rd_mismatch_cnt; |
| 43 | unsigned long long mismatch_cnt; |
| 44 | |
| 45 | RRDSET *st_nonredundant; |
| 46 | RRDDIM *rd_nonredundant; |
| 47 | }; |
| 48 | |
| 49 | struct old_raid { |
| 50 | int redundant; |
| 51 | char *name; |
| 52 | uint32_t hash; |
| 53 | int found; |
| 54 | }; |
| 55 | |
| 56 | static inline char *remove_trailing_chars(char *s, char c) |
| 57 | { |
| 58 | while (*s) { |
| 59 | if (unlikely(*s == c)) { |
| 60 | *s = '\0'; |
| 61 | } |
| 62 | s++; |
| 63 | } |
| 64 | return s; |
| 65 | } |
| 66 | |
| 67 | static inline void make_chart_obsolete(char *name, const char *id_modifier) |
| 68 | { |
| 69 | char id[50 + 1]; |
| 70 | RRDSET *st = NULL; |
| 71 | |
| 72 | if (likely(name && id_modifier)) { |
| 73 | snprintfz(id, sizeof(id) - 1, "%s_%s", name, id_modifier); |
| 74 | st = rrdset_find_active_bytype_localhost("mdstat", id); |
| 75 | if (likely(st)) |
| 76 | rrdset_is_obsolete___safe_from_collector_thread(st); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | static void add_labels_to_mdstat(struct raid *raid, RRDSET *st) { |
| 81 | rrdlabels_add(st->rrdlabels, "device", raid->name, RRDLABEL_SRC_AUTO); |
| 82 | rrdlabels_add(st->rrdlabels, "raid_level", raid->level, RRDLABEL_SRC_AUTO); |
| 83 | } |
| 84 | |
| 85 | int do_proc_mdstat(int update_every, usec_t dt) |
| 86 | { |
| 87 | (void)dt; |
| 88 | static procfile *ff = NULL; |
| 89 | static int do_health = -1, do_nonredundant = -1, do_disks = -1, do_operations = -1, do_mismatch = -1, |
| 90 | do_mismatch_config = -1; |
| 91 | static int make_charts_obsolete = -1; |
| 92 | static const char *mdstat_filename = NULL, *mismatch_cnt_filename = NULL; |
| 93 | static struct raid *raids = NULL; |
| 94 | static size_t raids_allocated = 0; |
| 95 | size_t raids_num = 0, raid_idx = 0, redundant_num = 0; |
| 96 | static struct old_raid *old_raids = NULL; |
| 97 | static size_t old_raids_allocated = 0; |
| 98 | size_t old_raid_idx = 0; |
| 99 | |
| 100 | if (unlikely(do_health == -1)) { |
| 101 | do_health = |
| 102 | inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/mdstat", "faulty devices", CONFIG_BOOLEAN_YES); |
| 103 | do_nonredundant = |
| 104 | inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/mdstat", "nonredundant arrays availability", CONFIG_BOOLEAN_YES); |
| 105 | do_mismatch_config = |
| 106 | inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/proc/mdstat", "mismatch count", CONFIG_BOOLEAN_AUTO); |
| 107 | do_disks = |
| 108 | inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/mdstat", "disk stats", CONFIG_BOOLEAN_YES); |
| 109 | do_operations = |
| 110 | inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/mdstat", "operation status", CONFIG_BOOLEAN_YES); |
| 111 | |
| 112 | make_charts_obsolete = |
| 113 | inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/mdstat", "make charts obsolete", CONFIG_BOOLEAN_YES); |
| 114 | |
| 115 | char filename[FILENAME_MAX + 1]; |
| 116 | |
| 117 | snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/mdstat"); |
| 118 | mdstat_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/mdstat", "filename to monitor", filename); |
| 119 | |
| 120 | snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/block/%s/md/mismatch_cnt"); |
| 121 | mismatch_cnt_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/mdstat", "mismatch_cnt filename to monitor", filename); |
| 122 | } |
| 123 | |
| 124 | if (unlikely(!ff)) { |
| 125 | ff = procfile_open(mdstat_filename, " \t:", PROCFILE_FLAG_DEFAULT); |
| 126 | if (unlikely(!ff)) |
| 127 | return 1; |
| 128 | } |
| 129 | |
| 130 | ff = procfile_readall(ff); |
| 131 | if (unlikely(!ff)) |
| 132 | return 0; // we return 0, so that we will retry opening it next time |
| 133 | |
| 134 | size_t lines = procfile_lines(ff); |
| 135 | size_t words = 0; |
| 136 | |
| 137 | if (unlikely(lines < 2)) { |
| 138 | collector_error("Cannot read /proc/mdstat. Expected 2 or more lines, read %zu.", lines); |
| 139 | return 1; |
| 140 | } |
| 141 | |
| 142 | // find how many raids are there |
| 143 | size_t l; |
| 144 | raids_num = 0; |
| 145 | for (l = 1; l < lines - 2; l++) { |
| 146 | if (unlikely(procfile_lineword(ff, l, 1)[0] == 'a')) // check if the raid is active |
| 147 | raids_num++; |
| 148 | } |
| 149 | |
| 150 | if (unlikely(!raids_num && !old_raids_allocated)) |
| 151 | return 0; // we return 0, so that we will retry searching for raids next time |
| 152 | |
| 153 | // allocate the memory we need; |
| 154 | if (unlikely(raids_num != raids_allocated)) { |
| 155 | for (raid_idx = 0; raid_idx < raids_allocated; raid_idx++) { |
| 156 | struct raid *raid = &raids[raid_idx]; |
| 157 | freez(raid->name); |
| 158 | freez(raid->level); |
| 159 | freez(raid->mismatch_cnt_filename); |
| 160 | } |
| 161 | if (raids_num) { |
| 162 | raids = (struct raid *)reallocz(raids, raids_num * sizeof(struct raid)); |
| 163 | memset(raids, 0, raids_num * sizeof(struct raid)); |
| 164 | } else { |
| 165 | freez(raids); |
| 166 | raids = NULL; |
| 167 | } |
| 168 | raids_allocated = raids_num; |
| 169 | } |
| 170 | |
| 171 | // loop through all lines except the first and the last ones |
| 172 | for (l = 1, raid_idx = 0; l < (lines - 2) && raid_idx < raids_num; l++) { |
| 173 | struct raid *raid = &raids[raid_idx]; |
| 174 | raid->redundant = 0; |
| 175 | |
| 176 | words = procfile_linewords(ff, l); |
| 177 | |
| 178 | if (unlikely(words < 3)) |
| 179 | continue; |
| 180 | |
| 181 | if (unlikely(procfile_lineword(ff, l, 1)[0] != 'a')) |
| 182 | continue; |
| 183 | |
| 184 | if (unlikely(!raid->name)) { |
| 185 | raid->name = strdupz(procfile_lineword(ff, l, 0)); |
| 186 | raid->hash = simple_hash(raid->name); |
| 187 | raid->level = strdupz(procfile_lineword(ff, l, 2)); |
| 188 | } else if (unlikely(strcmp(raid->name, procfile_lineword(ff, l, 0)))) { |
| 189 | freez(raid->name); |
| 190 | freez(raid->mismatch_cnt_filename); |
| 191 | freez(raid->level); |
| 192 | memset(raid, 0, sizeof(struct raid)); |
| 193 | raid->name = strdupz(procfile_lineword(ff, l, 0)); |
| 194 | raid->hash = simple_hash(raid->name); |
| 195 | raid->level = strdupz(procfile_lineword(ff, l, 2)); |
| 196 | } |
| 197 | |
| 198 | if (unlikely(!raid->name || !raid->name[0])) |
| 199 | continue; |
| 200 | |
| 201 | raid_idx++; |
| 202 | |
| 203 | // check if raid has disk status |
| 204 | l++; |
| 205 | words = procfile_linewords(ff, l); |
| 206 | if (words < 2 || procfile_lineword(ff, l, words - 1)[0] != '[') |
| 207 | continue; |
| 208 | |
| 209 | // split inuse and total number of disks |
| 210 | if (likely(do_health || do_disks)) { |
| 211 | char *s = NULL, *str_total = NULL, *str_inuse = NULL; |
| 212 | |
| 213 | s = procfile_lineword(ff, l, words - 2); |
| 214 | if (unlikely(s[0] != '[')) { |
| 215 | collector_error("Cannot read /proc/mdstat raid health status. Unexpected format: missing opening bracket."); |
| 216 | continue; |
| 217 | } |
| 218 | str_total = ++s; |
| 219 | while (*s) { |
| 220 | if (unlikely(*s == '/')) { |
| 221 | *s = '\0'; |
| 222 | str_inuse = s + 1; |
| 223 | } else if (unlikely(*s == ']')) { |
| 224 | *s = '\0'; |
| 225 | break; |
| 226 | } |
| 227 | s++; |
| 228 | } |
| 229 | if (unlikely(str_total[0] == '\0' || !str_inuse || str_inuse[0] == '\0')) { |
| 230 | collector_error("Cannot read /proc/mdstat raid health status. Unexpected format."); |
| 231 | continue; |
| 232 | } |
| 233 | |
| 234 | raid->inuse_disks = str2ull(str_inuse, NULL); |
| 235 | raid->total_disks = str2ull(str_total, NULL); |
| 236 | raid->failed_disks = raid->total_disks - raid->inuse_disks; |
| 237 | } |
| 238 | |
| 239 | raid->redundant = 1; |
| 240 | redundant_num++; |
| 241 | l++; |
| 242 | |
| 243 | // check if any operation is performed on the raid |
| 244 | if (likely(do_operations)) { |
| 245 | char *s = NULL; |
| 246 | |
| 247 | raid->check = 0; |
| 248 | raid->resync = 0; |
| 249 | raid->recovery = 0; |
| 250 | raid->reshape = 0; |
| 251 | raid->finish_in = 0; |
| 252 | raid->speed = 0; |
| 253 | |
| 254 | words = procfile_linewords(ff, l); |
| 255 | |
| 256 | if (likely(words < 2)) |
| 257 | continue; |
| 258 | |
| 259 | if (unlikely(procfile_lineword(ff, l, 0)[0] != '[')) |
| 260 | continue; |
| 261 | |
| 262 | if (unlikely(words < 7)) { |
| 263 | collector_error("Cannot read /proc/mdstat line. Expected 7 params, read %zu.", words); |
| 264 | continue; |
| 265 | } |
| 266 | |
| 267 | char *word; |
| 268 | word = procfile_lineword(ff, l, 3); |
| 269 | remove_trailing_chars(word, '%'); |
| 270 | |
| 271 | unsigned long long percentage = (unsigned long long)(str2ndd(word, NULL) * 100); |
| 272 | // possible operations: check, resync, recovery, reshape |
| 273 | // 4-th character is unique for each operation so it is checked |
| 274 | switch (procfile_lineword(ff, l, 1)[3]) { |
| 275 | case 'c': // check |
| 276 | raid->check = percentage; |
| 277 | break; |
| 278 | case 'y': // resync |
| 279 | raid->resync = percentage; |
| 280 | break; |
| 281 | case 'o': // recovery |
| 282 | raid->recovery = percentage; |
| 283 | break; |
| 284 | case 'h': // reshape |
| 285 | raid->reshape = percentage; |
| 286 | break; |
| 287 | } |
| 288 | |
| 289 | word = procfile_lineword(ff, l, 5); |
| 290 | s = remove_trailing_chars(word, 'm'); // remove trailing "min" |
| 291 | |
| 292 | word += 7; // skip leading "finish=" |
| 293 | |
| 294 | if (likely(s > word)) |
| 295 | raid->finish_in = (unsigned long long)(str2ndd(word, NULL) * 60); |
| 296 | |
| 297 | word = procfile_lineword(ff, l, 6); |
| 298 | s = remove_trailing_chars(word, 'K'); // remove trailing "K/sec" |
| 299 | |
| 300 | word += 6; // skip leading "speed=" |
| 301 | |
| 302 | if (likely(s > word)) |
| 303 | raid->speed = str2ull(word, NULL); |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | // read mismatch_cnt files |
| 308 | if (do_mismatch == -1) { |
| 309 | if (do_mismatch_config == CONFIG_BOOLEAN_AUTO) { |
| 310 | if (raids_num > 50) |
| 311 | do_mismatch = CONFIG_BOOLEAN_NO; |
| 312 | else |
| 313 | do_mismatch = CONFIG_BOOLEAN_YES; |
| 314 | } else |
| 315 | do_mismatch = do_mismatch_config; |
| 316 | } |
| 317 | |
| 318 | if (likely(do_mismatch)) { |
| 319 | for (raid_idx = 0; raid_idx < raids_num; raid_idx++) { |
| 320 | char filename[FILENAME_MAX + 1]; |
| 321 | struct raid *raid = &raids[raid_idx]; |
| 322 | |
| 323 | if (likely(raid->redundant)) { |
| 324 | if (unlikely(!raid->mismatch_cnt_filename)) { |
| 325 | snprintfz(filename, FILENAME_MAX, mismatch_cnt_filename, raid->name); |
| 326 | raid->mismatch_cnt_filename = strdupz(filename); |
| 327 | } |
| 328 | if (unlikely(read_single_number_file(raid->mismatch_cnt_filename, &raid->mismatch_cnt))) { |
| 329 | collector_error("Cannot read file '%s'", raid->mismatch_cnt_filename); |
| 330 | do_mismatch = CONFIG_BOOLEAN_NO; |
| 331 | collector_error("Monitoring for mismatch count has been disabled"); |
| 332 | break; |
| 333 | } |
| 334 | } |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | // check for disappeared raids |
| 339 | for (old_raid_idx = 0; old_raid_idx < old_raids_allocated; old_raid_idx++) { |
| 340 | struct old_raid *old_raid = &old_raids[old_raid_idx]; |
| 341 | int found = 0; |
| 342 | |
| 343 | for (raid_idx = 0; raid_idx < raids_num; raid_idx++) { |
| 344 | struct raid *raid = &raids[raid_idx]; |
| 345 | |
| 346 | if (unlikely( |
| 347 | raid->hash == old_raid->hash && !strcmp(raid->name, old_raid->name) && |
| 348 | raid->redundant == old_raid->redundant)) |
| 349 | found = 1; |
| 350 | } |
| 351 | |
| 352 | old_raid->found = found; |
| 353 | } |
| 354 | |
| 355 | int raid_disappeared = 0; |
| 356 | for (old_raid_idx = 0; old_raid_idx < old_raids_allocated; old_raid_idx++) { |
| 357 | struct old_raid *old_raid = &old_raids[old_raid_idx]; |
| 358 | |
| 359 | if (unlikely(!old_raid->found)) { |
| 360 | if (likely(make_charts_obsolete)) { |
| 361 | make_chart_obsolete(old_raid->name, "disks"); |
| 362 | make_chart_obsolete(old_raid->name, "mismatch"); |
| 363 | make_chart_obsolete(old_raid->name, "operation"); |
| 364 | make_chart_obsolete(old_raid->name, "finish"); |
| 365 | make_chart_obsolete(old_raid->name, "speed"); |
| 366 | make_chart_obsolete(old_raid->name, "availability"); |
| 367 | } |
| 368 | raid_disappeared = 1; |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | // allocate memory for nonredundant arrays |
| 373 | if (unlikely(raid_disappeared || old_raids_allocated != raids_num)) { |
| 374 | for (old_raid_idx = 0; old_raid_idx < old_raids_allocated; old_raid_idx++) { |
| 375 | freez(old_raids[old_raid_idx].name); |
| 376 | } |
| 377 | if (likely(raids_num)) { |
| 378 | old_raids = reallocz(old_raids, sizeof(struct old_raid) * raids_num); |
| 379 | memset(old_raids, 0, sizeof(struct old_raid) * raids_num); |
| 380 | } else { |
| 381 | freez(old_raids); |
| 382 | old_raids = NULL; |
| 383 | } |
| 384 | old_raids_allocated = raids_num; |
| 385 | for (old_raid_idx = 0; old_raid_idx < old_raids_allocated; old_raid_idx++) { |
| 386 | struct old_raid *old_raid = &old_raids[old_raid_idx]; |
| 387 | struct raid *raid = &raids[old_raid_idx]; |
| 388 | |
| 389 | old_raid->name = strdupz(raid->name); |
| 390 | old_raid->hash = raid->hash; |
| 391 | old_raid->redundant = raid->redundant; |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | if (likely(do_health && redundant_num)) { |
| 396 | static RRDSET *st_mdstat_health = NULL; |
| 397 | if (unlikely(!st_mdstat_health)) { |
| 398 | st_mdstat_health = rrdset_create_localhost( |
| 399 | "mdstat", |
| 400 | "mdstat_health", |
| 401 | NULL, |
| 402 | "health", |
| 403 | "md.health", |
| 404 | "Faulty Devices In MD", |
| 405 | "failed disks", |
| 406 | PLUGIN_PROC_NAME, |
| 407 | PLUGIN_PROC_MODULE_MDSTAT_NAME, |
| 408 | NETDATA_CHART_PRIO_MDSTAT_HEALTH, |
| 409 | update_every, |
| 410 | RRDSET_TYPE_LINE); |
| 411 | } |
| 412 | |
| 413 | if (!redundant_num) { |
| 414 | if (likely(make_charts_obsolete)) |
| 415 | make_chart_obsolete("mdstat", "health"); |
| 416 | } else { |
| 417 | for (raid_idx = 0; raid_idx < raids_num; raid_idx++) { |
| 418 | struct raid *raid = &raids[raid_idx]; |
| 419 | |
| 420 | if (likely(raid->redundant)) { |
| 421 | if (unlikely(!raid->rd_health && !(raid->rd_health = rrddim_find_active(st_mdstat_health, raid->name)))) |
| 422 | raid->rd_health = rrddim_add(st_mdstat_health, raid->name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 423 | |
| 424 | rrddim_set_by_pointer(st_mdstat_health, raid->rd_health, raid->failed_disks); |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | rrdset_done(st_mdstat_health); |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | for (raid_idx = 0; raid_idx < raids_num; raid_idx++) { |
| 433 | struct raid *raid = &raids[raid_idx]; |
| 434 | char id[50 + 1]; |
| 435 | char family[50 + 1]; |
| 436 | |
| 437 | if (likely(raid->redundant)) { |
| 438 | if (likely(do_disks)) { |
| 439 | snprintfz(id, sizeof(id) - 1, "%s_disks", raid->name); |
| 440 | |
| 441 | if (unlikely(!raid->st_disks && !(raid->st_disks = rrdset_find_active_byname_localhost(id)))) { |
| 442 | snprintfz(family, sizeof(family) - 1, "%s (%s)", raid->name, raid->level); |
| 443 | |
| 444 | raid->st_disks = rrdset_create_localhost( |
| 445 | "mdstat", |
| 446 | id, |
| 447 | NULL, |
| 448 | family, |
| 449 | "md.disks", |
| 450 | "Disks Stats", |
| 451 | "disks", |
| 452 | PLUGIN_PROC_NAME, |
| 453 | PLUGIN_PROC_MODULE_MDSTAT_NAME, |
| 454 | NETDATA_CHART_PRIO_MDSTAT_DISKS + raid_idx * 10, |
| 455 | update_every, |
| 456 | RRDSET_TYPE_STACKED); |
| 457 | |
| 458 | add_labels_to_mdstat(raid, raid->st_disks); |
| 459 | } |
| 460 | |
| 461 | if (unlikely(!raid->rd_inuse && !(raid->rd_inuse = rrddim_find_active(raid->st_disks, "inuse")))) |
| 462 | raid->rd_inuse = rrddim_add(raid->st_disks, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 463 | if (unlikely(!raid->rd_down && !(raid->rd_down = rrddim_find_active(raid->st_disks, "down")))) |
| 464 | raid->rd_down = rrddim_add(raid->st_disks, "down", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 465 | |
| 466 | rrddim_set_by_pointer(raid->st_disks, raid->rd_inuse, raid->inuse_disks); |
| 467 | rrddim_set_by_pointer(raid->st_disks, raid->rd_down, raid->failed_disks); |
| 468 | rrdset_done(raid->st_disks); |
| 469 | } |
| 470 | |
| 471 | if (likely(do_mismatch)) { |
| 472 | snprintfz(id, sizeof(id) - 1, "%s_mismatch", raid->name); |
| 473 | |
| 474 | if (unlikely(!raid->st_mismatch_cnt && !(raid->st_mismatch_cnt = rrdset_find_active_byname_localhost(id)))) { |
| 475 | snprintfz(family, sizeof(family) - 1, "%s (%s)", raid->name, raid->level); |
| 476 | |
| 477 | raid->st_mismatch_cnt = rrdset_create_localhost( |
| 478 | "mdstat", |
| 479 | id, |
| 480 | NULL, |
| 481 | family, |
| 482 | "md.mismatch_cnt", |
| 483 | "Mismatch Count", |
| 484 | "unsynchronized blocks", |
| 485 | PLUGIN_PROC_NAME, |
| 486 | PLUGIN_PROC_MODULE_MDSTAT_NAME, |
| 487 | NETDATA_CHART_PRIO_MDSTAT_MISMATCH + raid_idx * 10, |
| 488 | update_every, |
| 489 | RRDSET_TYPE_LINE); |
| 490 | |
| 491 | add_labels_to_mdstat(raid, raid->st_mismatch_cnt); |
| 492 | } |
| 493 | |
| 494 | if (unlikely(!raid->rd_mismatch_cnt && !(raid->rd_mismatch_cnt = rrddim_find_active(raid->st_mismatch_cnt, "count")))) |
| 495 | raid->rd_mismatch_cnt = rrddim_add(raid->st_mismatch_cnt, "count", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 496 | |
| 497 | rrddim_set_by_pointer(raid->st_mismatch_cnt, raid->rd_mismatch_cnt, raid->mismatch_cnt); |
| 498 | rrdset_done(raid->st_mismatch_cnt); |
| 499 | } |
| 500 | |
| 501 | if (likely(do_operations)) { |
| 502 | snprintfz(id, sizeof(id) - 1, "%s_operation", raid->name); |
| 503 | |
| 504 | if (unlikely(!raid->st_operation && !(raid->st_operation = rrdset_find_active_byname_localhost(id)))) { |
| 505 | snprintfz(family, sizeof(family) - 1, "%s (%s)", raid->name, raid->level); |
| 506 | |
| 507 | raid->st_operation = rrdset_create_localhost( |
| 508 | "mdstat", |
| 509 | id, |
| 510 | NULL, |
| 511 | family, |
| 512 | "md.status", |
| 513 | "Current Status", |
| 514 | "percent", |
| 515 | PLUGIN_PROC_NAME, |
| 516 | PLUGIN_PROC_MODULE_MDSTAT_NAME, |
| 517 | NETDATA_CHART_PRIO_MDSTAT_OPERATION + raid_idx * 10, |
| 518 | update_every, |
| 519 | RRDSET_TYPE_LINE); |
| 520 | |
| 521 | add_labels_to_mdstat(raid, raid->st_operation); |
| 522 | } |
| 523 | |
| 524 | if(unlikely(!raid->rd_check && !(raid->rd_check = rrddim_find_active(raid->st_operation, "check")))) |
| 525 | raid->rd_check = rrddim_add(raid->st_operation, "check", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); |
| 526 | if(unlikely(!raid->rd_resync && !(raid->rd_resync = rrddim_find_active(raid->st_operation, "resync")))) |
| 527 | raid->rd_resync = rrddim_add(raid->st_operation, "resync", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); |
| 528 | if(unlikely(!raid->rd_recovery && !(raid->rd_recovery = rrddim_find_active(raid->st_operation, "recovery")))) |
| 529 | raid->rd_recovery = rrddim_add(raid->st_operation, "recovery", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); |
| 530 | if(unlikely(!raid->rd_reshape && !(raid->rd_reshape = rrddim_find_active(raid->st_operation, "reshape")))) |
| 531 | raid->rd_reshape = rrddim_add(raid->st_operation, "reshape", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); |
| 532 | |
| 533 | rrddim_set_by_pointer(raid->st_operation, raid->rd_check, raid->check); |
| 534 | rrddim_set_by_pointer(raid->st_operation, raid->rd_resync, raid->resync); |
| 535 | rrddim_set_by_pointer(raid->st_operation, raid->rd_recovery, raid->recovery); |
| 536 | rrddim_set_by_pointer(raid->st_operation, raid->rd_reshape, raid->reshape); |
| 537 | rrdset_done(raid->st_operation); |
| 538 | |
| 539 | snprintfz(id, sizeof(id) - 1, "%s_finish", raid->name); |
| 540 | if (unlikely(!raid->st_finish && !(raid->st_finish = rrdset_find_active_byname_localhost(id)))) { |
| 541 | snprintfz(family, sizeof(family) - 1, "%s (%s)", raid->name, raid->level); |
| 542 | |
| 543 | raid->st_finish = rrdset_create_localhost( |
| 544 | "mdstat", |
| 545 | id, |
| 546 | NULL, |
| 547 | family, |
| 548 | "md.expected_time_until_operation_finish", |
| 549 | "Approximate Time Until Finish", |
| 550 | "seconds", |
| 551 | PLUGIN_PROC_NAME, |
| 552 | PLUGIN_PROC_MODULE_MDSTAT_NAME, |
| 553 | NETDATA_CHART_PRIO_MDSTAT_FINISH + raid_idx * 10, |
| 554 | update_every, RRDSET_TYPE_LINE); |
| 555 | |
| 556 | add_labels_to_mdstat(raid, raid->st_finish); |
| 557 | } |
| 558 | |
| 559 | if(unlikely(!raid->rd_finish_in && !(raid->rd_finish_in = rrddim_find_active(raid->st_finish, "finish_in")))) |
| 560 | raid->rd_finish_in = rrddim_add(raid->st_finish, "finish_in", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 561 | |
| 562 | rrddim_set_by_pointer(raid->st_finish, raid->rd_finish_in, raid->finish_in); |
| 563 | rrdset_done(raid->st_finish); |
| 564 | |
| 565 | snprintfz(id, sizeof(id) - 1, "%s_speed", raid->name); |
| 566 | if (unlikely(!raid->st_speed && !(raid->st_speed = rrdset_find_active_byname_localhost(id)))) { |
| 567 | snprintfz(family, sizeof(family) - 1, "%s (%s)", raid->name, raid->level); |
| 568 | |
| 569 | raid->st_speed = rrdset_create_localhost( |
| 570 | "mdstat", |
| 571 | id, |
| 572 | NULL, |
| 573 | family, |
| 574 | "md.operation_speed", |
| 575 | "Operation Speed", |
| 576 | "KiB/s", |
| 577 | PLUGIN_PROC_NAME, |
| 578 | PLUGIN_PROC_MODULE_MDSTAT_NAME, |
| 579 | NETDATA_CHART_PRIO_MDSTAT_SPEED + raid_idx * 10, |
| 580 | update_every, |
| 581 | RRDSET_TYPE_LINE); |
| 582 | |
| 583 | add_labels_to_mdstat(raid, raid->st_speed); |
| 584 | } |
| 585 | |
| 586 | if (unlikely(!raid->rd_speed && !(raid->rd_speed = rrddim_find_active(raid->st_speed, "speed")))) |
| 587 | raid->rd_speed = rrddim_add(raid->st_speed, "speed", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 588 | |
| 589 | rrddim_set_by_pointer(raid->st_speed, raid->rd_speed, raid->speed); |
| 590 | rrdset_done(raid->st_speed); |
| 591 | } |
| 592 | } else { |
| 593 | if (likely(do_nonredundant)) { |
| 594 | snprintfz(id, sizeof(id) - 1, "%s_availability", raid->name); |
| 595 | |
| 596 | if (unlikely(!raid->st_nonredundant && !(raid->st_nonredundant = rrdset_find_active_localhost(id)))) { |
| 597 | snprintfz(family, sizeof(family) - 1, "%s (%s)", raid->name, raid->level); |
| 598 | |
| 599 | raid->st_nonredundant = rrdset_create_localhost( |
| 600 | "mdstat", |
| 601 | id, |
| 602 | NULL, |
| 603 | family, |
| 604 | "md.nonredundant", |
| 605 | "Nonredundant Array Availability", |
| 606 | "boolean", |
| 607 | PLUGIN_PROC_NAME, |
| 608 | PLUGIN_PROC_MODULE_MDSTAT_NAME, |
| 609 | NETDATA_CHART_PRIO_MDSTAT_NONREDUNDANT + raid_idx * 10, |
| 610 | update_every, |
| 611 | RRDSET_TYPE_LINE); |
| 612 | |
| 613 | add_labels_to_mdstat(raid, raid->st_nonredundant); |
| 614 | } |
| 615 | |
| 616 | if (unlikely(!raid->rd_nonredundant && !(raid->rd_nonredundant = rrddim_find_active(raid->st_nonredundant, "available")))) |
| 617 | raid->rd_nonredundant = rrddim_add(raid->st_nonredundant, "available", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 618 | |
| 619 | rrddim_set_by_pointer(raid->st_nonredundant, raid->rd_nonredundant, 1); |
| 620 | rrdset_done(raid->st_nonredundant); |
| 621 | } |
| 622 | } |
| 623 | } |
| 624 | |
| 625 | return 0; |
| 626 | } |