master
c 2,234 lines 97.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugin_proc.h"
4
5 #define PLUGIN_PROC_MODULE_DISKSTATS_NAME "/proc/diskstats"
6 #define CONFIG_SECTION_PLUGIN_PROC_DISKSTATS "plugin:" PLUGIN_PROC_CONFIG_NAME ":" PLUGIN_PROC_MODULE_DISKSTATS_NAME
7
8 #define _COMMON_PLUGIN_NAME PLUGIN_PROC_CONFIG_NAME
9 #define _COMMON_PLUGIN_MODULE_NAME PLUGIN_PROC_MODULE_DISKSTATS_NAME
10 #include "../common-contexts/common-contexts.h"
11
12 #define RRDFUNCTIONS_DISKSTATS_HELP "Displays block device I/O statistics including read/write throughput, operations, latency, and utilization."
13
14 #define DISK_TYPE_UNKNOWN 0
15 #define DISK_TYPE_PHYSICAL 1
16 #define DISK_TYPE_PARTITION 2
17 #define DISK_TYPE_VIRTUAL 3
18
19 #define DEFAULT_PREFERRED_IDS "*"
20 #define DEFAULT_EXCLUDED_DISKS "loop* ram*"
21
22 // always 512 on Linux (https://github.com/torvalds/linux/blob/daa121128a2d2ac6006159e2c47676e4fcd21eab/include/linux/blk_types.h#L25-L34)
23 #define SECTOR_SIZE 512
24
25 static netdata_mutex_t diskstats_dev_mutex;
26
27 static void __attribute__((constructor)) init_mutex(void) {
28 netdata_mutex_init(&diskstats_dev_mutex);
29 }
30
31 static void __attribute__((destructor)) destroy_mutex(void) {
32 netdata_mutex_destroy(&diskstats_dev_mutex);
33 }
34
35 static struct disk {
36 char *disk; // the name of the disk (sda, sdb, etc, after being looked up)
37 char *device; // the device of the disk (before being looked up)
38 char *disk_by_id;
39 char *model;
40 char *serial;
41 // bool rotational;
42 // bool removable;
43 uint32_t hash;
44 unsigned long major;
45 unsigned long minor;
46 int type;
47
48 bool excluded;
49 bool function_ready;
50
51 char *mount_point;
52
53 char *chart_id;
54
55 // disk options caching
56 int do_io;
57 int do_ops;
58 int do_mops;
59 int do_iotime;
60 int do_qops;
61 int do_util;
62 int do_ext;
63 int do_backlog;
64 int do_bcache;
65
66 int updated;
67
68 int device_is_bcache;
69
70 char *bcache_filename_dirty_data;
71 char *bcache_filename_writeback_rate;
72 char *bcache_filename_cache_congested;
73 char *bcache_filename_cache_available_percent;
74 char *bcache_filename_stats_five_minute_cache_hit_ratio;
75 char *bcache_filename_stats_hour_cache_hit_ratio;
76 char *bcache_filename_stats_day_cache_hit_ratio;
77 char *bcache_filename_stats_total_cache_hit_ratio;
78 char *bcache_filename_stats_total_cache_hits;
79 char *bcache_filename_stats_total_cache_misses;
80 char *bcache_filename_stats_total_cache_miss_collisions;
81 char *bcache_filename_stats_total_cache_bypass_hits;
82 char *bcache_filename_stats_total_cache_bypass_misses;
83 char *bcache_filename_stats_total_cache_readaheads;
84 char *bcache_filename_cache_read_races;
85 char *bcache_filename_cache_io_errors;
86 char *bcache_filename_priority_stats;
87
88 usec_t bcache_priority_stats_update_every_usec;
89 usec_t bcache_priority_stats_elapsed_usec;
90
91 ND_DISK_IO disk_io;
92 ND_DISK_OPS disk_ops;
93 ND_DISK_QOPS disk_qops;
94 ND_DISK_UTIL disk_util;
95 ND_DISK_BUSY disk_busy;
96 ND_DISK_IOTIME disk_iotime;
97 ND_DISK_AWAIT disk_await;
98 ND_DISK_SVCTM disk_svctm;
99 ND_DISK_AVGSZ disk_avgsz;
100
101 RRDSET *st_ext_io;
102 RRDDIM *rd_io_discards;
103
104 RRDSET *st_ext_ops;
105 RRDDIM *rd_ops_discards;
106 RRDDIM *rd_ops_flushes;
107
108 RRDSET *st_backlog;
109 RRDDIM *rd_backlog_backlog;
110
111 RRDSET *st_mops;
112 RRDDIM *rd_mops_reads;
113 RRDDIM *rd_mops_writes;
114
115 RRDSET *st_ext_mops;
116 RRDDIM *rd_mops_discards;
117
118 RRDSET *st_ext_iotime;
119 RRDDIM *rd_iotime_discards;
120 RRDDIM *rd_iotime_flushes;
121
122 RRDSET *st_ext_await;
123 RRDDIM *rd_await_discards;
124 RRDDIM *rd_await_flushes;
125
126 RRDSET *st_ext_avgsz;
127 RRDDIM *rd_avgsz_discards;
128
129 RRDSET *st_bcache_size;
130 RRDDIM *rd_bcache_dirty_size;
131
132 RRDSET *st_bcache_usage;
133 RRDDIM *rd_bcache_available_percent;
134
135 RRDSET *st_bcache_hit_ratio;
136 RRDDIM *rd_bcache_hit_ratio_5min;
137 RRDDIM *rd_bcache_hit_ratio_1hour;
138 RRDDIM *rd_bcache_hit_ratio_1day;
139 RRDDIM *rd_bcache_hit_ratio_total;
140
141 RRDSET *st_bcache;
142 RRDDIM *rd_bcache_hits;
143 RRDDIM *rd_bcache_misses;
144 RRDDIM *rd_bcache_miss_collisions;
145
146 RRDSET *st_bcache_bypass;
147 RRDDIM *rd_bcache_bypass_hits;
148 RRDDIM *rd_bcache_bypass_misses;
149
150 RRDSET *st_bcache_rates;
151 RRDDIM *rd_bcache_rate_congested;
152 RRDDIM *rd_bcache_readaheads;
153 RRDDIM *rd_bcache_rate_writeback;
154
155 RRDSET *st_bcache_cache_allocations;
156 RRDDIM *rd_bcache_cache_allocations_unused;
157 RRDDIM *rd_bcache_cache_allocations_clean;
158 RRDDIM *rd_bcache_cache_allocations_dirty;
159 RRDDIM *rd_bcache_cache_allocations_metadata;
160 RRDDIM *rd_bcache_cache_allocations_unknown;
161
162 RRDSET *st_bcache_cache_read_races;
163 RRDDIM *rd_bcache_cache_read_races;
164 RRDDIM *rd_bcache_cache_io_errors;
165
166 struct disk *next;
167 } *disk_root = NULL;
168
169 #define rrdset_obsolete_and_pointer_null(st) do { if(st) { rrdset_is_obsolete___safe_from_collector_thread(st); (st) = NULL; } } while(st)
170
171 static const char *path_to_sys_dev_block_major_minor_string = NULL;
172 static const char *path_to_sys_block_device = NULL;
173 static const char *path_to_sys_block_device_bcache = NULL;
174 static const char *path_to_sys_devices_virtual_block_device = NULL;
175 static const char *path_to_device_mapper = NULL;
176 static const char *path_to_dev_disk = NULL;
177 static const char *path_to_sys_block = NULL;
178 static const char *path_to_device_label = NULL;
179 static const char *path_to_device_id = NULL;
180 static const char *path_to_veritas_volume_groups = NULL;
181 static int name_disks_by_id = CONFIG_BOOLEAN_NO;
182 static int global_bcache_priority_stats_update_every = 0; // disabled by default
183
184 static int global_enable_new_disks_detected_at_runtime = CONFIG_BOOLEAN_YES,
185 global_enable_performance_for_physical_disks = CONFIG_BOOLEAN_AUTO,
186 global_enable_performance_for_virtual_disks = CONFIG_BOOLEAN_AUTO,
187 global_enable_performance_for_partitions = CONFIG_BOOLEAN_NO,
188 global_do_io = CONFIG_BOOLEAN_AUTO,
189 global_do_ops = CONFIG_BOOLEAN_AUTO,
190 global_do_mops = CONFIG_BOOLEAN_AUTO,
191 global_do_iotime = CONFIG_BOOLEAN_AUTO,
192 global_do_qops = CONFIG_BOOLEAN_AUTO,
193 global_do_util = CONFIG_BOOLEAN_AUTO,
194 global_do_ext = CONFIG_BOOLEAN_AUTO,
195 global_do_backlog = CONFIG_BOOLEAN_AUTO,
196 global_do_bcache = CONFIG_BOOLEAN_AUTO,
197 globals_initialized = 0,
198 global_cleanup_removed_disks = 1;
199
200 static SIMPLE_PATTERN *preferred_ids = NULL;
201 static SIMPLE_PATTERN *excluded_disks = NULL;
202
203 static unsigned long long int bcache_read_number_with_units(const char *filename) {
204 char buffer[50 + 1];
205 if(read_txt_file(filename, buffer, sizeof(buffer)) == 0) {
206 static int unknown_units_error = 10;
207
208 char *end = NULL;
209 NETDATA_DOUBLE value = str2ndd(buffer, &end);
210 if(end && *end) {
211 if(*end == 'k')
212 return (unsigned long long int)(value * 1024.0);
213 else if(*end == 'M')
214 return (unsigned long long int)(value * 1024.0 * 1024.0);
215 else if(*end == 'G')
216 return (unsigned long long int)(value * 1024.0 * 1024.0 * 1024.0);
217 else if(*end == 'T')
218 return (unsigned long long int)(value * 1024.0 * 1024.0 * 1024.0 * 1024.0);
219 else if(unknown_units_error > 0) {
220 collector_error("bcache file '%s' provides value '%s' with unknown units '%s'", filename, buffer, end);
221 unknown_units_error--;
222 }
223 }
224
225 return (unsigned long long int)value;
226 }
227
228 return 0;
229 }
230
231 void bcache_read_priority_stats(struct disk *d, const char *family, int update_every, usec_t dt) {
232 static procfile *ff = NULL;
233 static char *separators = " \t:%[]";
234
235 static ARL_BASE *arl_base = NULL;
236
237 static unsigned long long unused;
238 static unsigned long long clean;
239 static unsigned long long dirty;
240 static unsigned long long metadata;
241 static unsigned long long unknown;
242
243 // check if it is time to update this metric
244 d->bcache_priority_stats_elapsed_usec += dt;
245 if(likely(d->bcache_priority_stats_elapsed_usec < d->bcache_priority_stats_update_every_usec)) return;
246 d->bcache_priority_stats_elapsed_usec = 0;
247
248 // initialize ARL
249 if(unlikely(!arl_base)) {
250 arl_base = arl_create("bcache/priority_stats", NULL, 60);
251 arl_expect(arl_base, "Unused", &unused);
252 arl_expect(arl_base, "Clean", &clean);
253 arl_expect(arl_base, "Dirty", &dirty);
254 arl_expect(arl_base, "Metadata", &metadata);
255 }
256
257 ff = procfile_reopen(ff, d->bcache_filename_priority_stats, separators, PROCFILE_FLAG_DEFAULT);
258 if(likely(ff)) ff = procfile_readall(ff);
259 if(unlikely(!ff)) {
260 separators = " \t:%[]";
261 return;
262 }
263
264 // do not reset the separators on every iteration
265 separators = NULL;
266
267 arl_begin(arl_base);
268 unused = clean = dirty = metadata = unknown = 0;
269
270 size_t lines = procfile_lines(ff), l;
271
272 for(l = 0; l < lines ;l++) {
273 size_t words = procfile_linewords(ff, l);
274 if(unlikely(words < 2)) {
275 if(unlikely(words)) collector_error("Cannot read '%s' line %zu. Expected 2 params, read %zu.", d->bcache_filename_priority_stats, l, words);
276 continue;
277 }
278
279 if(unlikely(arl_check(arl_base,
280 procfile_lineword(ff, l, 0),
281 procfile_lineword(ff, l, 1)))) break;
282 }
283
284 unknown = 100 - unused - clean - dirty - metadata;
285
286 // create / update the cache allocations chart
287 {
288 if(unlikely(!d->st_bcache_cache_allocations)) {
289 d->st_bcache_cache_allocations = rrdset_create_localhost(
290 "disk_bcache_cache_alloc"
291 , d->chart_id
292 , d->disk
293 , family
294 , "disk.bcache_cache_alloc"
295 , "BCache Cache Allocations"
296 , "percentage"
297 , PLUGIN_PROC_NAME
298 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
299 , NETDATA_CHART_PRIO_BCACHE_CACHE_ALLOC
300 , update_every
301 , RRDSET_TYPE_STACKED
302 );
303
304 d->rd_bcache_cache_allocations_unused = rrddim_add(d->st_bcache_cache_allocations, "unused", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
305 d->rd_bcache_cache_allocations_dirty = rrddim_add(d->st_bcache_cache_allocations, "dirty", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
306 d->rd_bcache_cache_allocations_clean = rrddim_add(d->st_bcache_cache_allocations, "clean", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
307 d->rd_bcache_cache_allocations_metadata = rrddim_add(d->st_bcache_cache_allocations, "metadata", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
308 d->rd_bcache_cache_allocations_unknown = rrddim_add(d->st_bcache_cache_allocations, "undefined", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
309
310 d->bcache_priority_stats_update_every_usec = update_every * USEC_PER_SEC;
311 }
312
313 rrddim_set_by_pointer(d->st_bcache_cache_allocations, d->rd_bcache_cache_allocations_unused, unused);
314 rrddim_set_by_pointer(d->st_bcache_cache_allocations, d->rd_bcache_cache_allocations_dirty, dirty);
315 rrddim_set_by_pointer(d->st_bcache_cache_allocations, d->rd_bcache_cache_allocations_clean, clean);
316 rrddim_set_by_pointer(d->st_bcache_cache_allocations, d->rd_bcache_cache_allocations_metadata, metadata);
317 rrddim_set_by_pointer(d->st_bcache_cache_allocations, d->rd_bcache_cache_allocations_unknown, unknown);
318 rrdset_done(d->st_bcache_cache_allocations);
319 }
320 }
321
322 static inline int is_major_enabled(int major) {
323 static int8_t *major_configs = NULL;
324 static size_t major_size = 0;
325
326 if(major < 0) return 1;
327
328 size_t wanted_size = (size_t)major + 1;
329
330 if(major_size < wanted_size) {
331 major_configs = reallocz(major_configs, wanted_size * sizeof(int8_t));
332
333 size_t i;
334 for(i = major_size; i < wanted_size ; i++)
335 major_configs[i] = -1;
336
337 major_size = wanted_size;
338 }
339
340 if(major_configs[major] == -1) {
341 char buffer[CONFIG_MAX_NAME + 1];
342 snprintfz(buffer, CONFIG_MAX_NAME, "performance metrics for disks with major %d", major);
343 major_configs[major] = (char)inicfg_get_boolean(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, buffer, 1);
344 }
345
346 return (int)major_configs[major];
347 }
348
349 static inline int get_disk_name_from_path(const char *path, char *result, size_t result_size, unsigned long major, unsigned long minor, char *disk, char *prefix, int depth) {
350 //collector_info("DEVICE-MAPPER ('%s', %lu:%lu): examining directory '%s' (allowed depth %d).", disk, major, minor, path, depth);
351 int found = 0, preferred = 0;
352
353 char *first_result = mallocz(result_size + 1);
354
355 DIR *dir = opendir(path);
356 if (!dir) {
357 if (errno == ENOENT)
358 nd_log_collector(NDLP_DEBUG, "DEVICE-MAPPER ('%s', %lu:%lu): Cannot open directory '%s': no such file or directory.", disk, major, minor, path);
359 else
360 collector_error("DEVICE-MAPPER ('%s', %lu:%lu): Cannot open directory '%s'.", disk, major, minor, path);
361 goto failed;
362 }
363
364 struct dirent *de = NULL;
365 while ((de = readdir(dir))) {
366 if(de->d_type == DT_DIR) {
367 if((de->d_name[0] == '.' && de->d_name[1] == '\0') || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0'))
368 continue;
369
370 if(depth <= 0) {
371 collector_error("DEVICE-MAPPER ('%s', %lu:%lu): Depth limit reached for path '%s/%s'. Ignoring path.", disk, major, minor, path, de->d_name);
372 break;
373 }
374 else {
375 char *path_nested = NULL;
376 char *prefix_nested = NULL;
377
378 {
379 char buffer[FILENAME_MAX + 1];
380 snprintfz(buffer, FILENAME_MAX, "%s/%s", path, de->d_name);
381 path_nested = strdupz(buffer);
382
383 snprintfz(buffer, FILENAME_MAX, "%s%s%s", (prefix)?prefix:"", (prefix)?"_":"", de->d_name);
384 prefix_nested = strdupz(buffer);
385 }
386
387 found = get_disk_name_from_path(path_nested, result, result_size, major, minor, disk, prefix_nested, depth - 1);
388 freez(path_nested);
389 freez(prefix_nested);
390
391 if(found) break;
392 }
393 }
394 else if(de->d_type == DT_LNK || de->d_type == DT_BLK) {
395 char filename[FILENAME_MAX + 1];
396
397 if(de->d_type == DT_LNK) {
398 snprintfz(filename, FILENAME_MAX, "%s/%s", path, de->d_name);
399 ssize_t len = readlink(filename, result, result_size - 1);
400 if(len <= 0) {
401 collector_error("DEVICE-MAPPER ('%s', %lu:%lu): Cannot read link '%s'.", disk, major, minor, filename);
402 continue;
403 }
404
405 result[len] = '\0';
406 if(result[0] != '/')
407 snprintfz(filename, FILENAME_MAX, "%s/%s", path, result);
408 else
409 strncpyz(filename, result, FILENAME_MAX);
410 }
411 else
412 snprintfz(filename, FILENAME_MAX, "%s/%s", path, de->d_name);
413
414 struct stat sb;
415 if(stat(filename, &sb) == -1) {
416 collector_error("DEVICE-MAPPER ('%s', %lu:%lu): Cannot stat() file '%s'.", disk, major, minor, filename);
417 continue;
418 }
419
420 if((sb.st_mode & S_IFMT) != S_IFBLK) {
421 //collector_info("DEVICE-MAPPER ('%s', %lu:%lu): file '%s' is not a block device.", disk, major, minor, filename);
422 continue;
423 }
424
425 if(major(sb.st_rdev) != major || minor(sb.st_rdev) != minor || strcmp(basename(filename), disk)) {
426 //collector_info("DEVICE-MAPPER ('%s', %lu:%lu): filename '%s' does not match %lu:%lu.", disk, major, minor, filename, (unsigned long)major(sb.st_rdev), (unsigned long)minor(sb.st_rdev));
427 continue;
428 }
429
430 //collector_info("DEVICE-MAPPER ('%s', %lu:%lu): filename '%s' matches.", disk, major, minor, filename);
431
432 snprintfz(result, result_size - 1, "%s%s%s", (prefix)?prefix:"", (prefix)?"_":"", de->d_name);
433
434 if(!found) {
435 strncpyz(first_result, result, result_size - 1);
436 found = 1;
437 }
438
439 result[result_size - 1] = '\0';
440 if(simple_pattern_matches(preferred_ids, result)) {
441 preferred = 1;
442 break;
443 }
444 }
445 }
446 closedir(dir);
447
448 failed:
449 if(!found)
450 result[0] = '\0';
451 else if(!preferred)
452 strncpyz(result, first_result, result_size - 1);
453
454 freez(first_result);
455
456 return found;
457 }
458
459 static inline char *get_disk_name(unsigned long major, unsigned long minor, char *disk) {
460 char result[FILENAME_MAX + 2] = "";
461
462 if(!path_to_device_mapper || !*path_to_device_mapper || !get_disk_name_from_path(path_to_device_mapper, result, FILENAME_MAX + 1, major, minor, disk, NULL, 0))
463 if(!path_to_device_label || !*path_to_device_label || !get_disk_name_from_path(path_to_device_label, result, FILENAME_MAX + 1, major, minor, disk, NULL, 0))
464 if(!path_to_veritas_volume_groups || !*path_to_veritas_volume_groups || !get_disk_name_from_path(path_to_veritas_volume_groups, result, FILENAME_MAX + 1, major, minor, disk, "vx", 2))
465 if(name_disks_by_id != CONFIG_BOOLEAN_YES || !path_to_device_id || !*path_to_device_id || !get_disk_name_from_path(path_to_device_id, result, FILENAME_MAX + 1, major, minor, disk, NULL, 0))
466 strncpyz(result, disk, sizeof(result) - 1);
467
468 if(!result[0])
469 strncpyz(result, disk, sizeof(result) - 1);
470
471 netdata_fix_chart_name(result);
472 return strdup(result);
473 }
474
475 static inline bool ends_with(const char *str, const char *suffix) {
476 if (!str || !suffix)
477 return false;
478
479 size_t len_str = strlen(str);
480 size_t len_suffix = strlen(suffix);
481 if (len_suffix > len_str)
482 return false;
483
484 return strncmp(str + len_str - len_suffix, suffix, len_suffix) == 0;
485 }
486
487 static inline char *get_disk_by_id(char *device) {
488 char pathname[256 + 1];
489 snprintfz(pathname, sizeof(pathname) - 1, "%s/by-id", path_to_dev_disk);
490
491 struct dirent *entry;
492 DIR *dp = opendir(pathname);
493 if (dp == NULL) {
494 internal_error(true, "Cannot open '%s'", pathname);
495 return NULL;
496 }
497
498 while ((entry = readdir(dp))) {
499 // We ignore the '.' and '..' entries
500 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
501 continue;
502
503 if(strncmp(entry->d_name, "md-uuid-", 8) == 0 ||
504 strncmp(entry->d_name, "dm-uuid-", 8) == 0 ||
505 strncmp(entry->d_name, "nvme-eui.", 9) == 0 ||
506 strncmp(entry->d_name, "wwn-", 4) == 0 ||
507 strncmp(entry->d_name, "lvm-pv-uuid-", 12) == 0)
508 continue;
509
510 char link_target[256 + 1];
511 char full_path[256 + 1];
512 snprintfz(full_path, 256, "%s/%s", pathname, entry->d_name);
513
514 ssize_t len = readlink(full_path, link_target, 256);
515 if (len == -1)
516 continue;
517
518 link_target[len] = '\0';
519
520 if (ends_with(link_target, device)) {
521 char *s = strdupz(entry->d_name);
522 closedir(dp);
523 return s;
524 }
525 }
526
527 closedir(dp);
528 return NULL;
529 }
530
531 static inline char *get_disk_model(char *device) {
532 char path[256 + 1];
533 char buffer[256 + 1];
534
535 snprintfz(path, sizeof(path) - 1, "%s/%s/device/model", path_to_sys_block, device);
536 if(read_txt_file(path, buffer, sizeof(buffer)) != 0) {
537 snprintfz(path, sizeof(path) - 1, "%s/%s/device/name", path_to_sys_block, device);
538 if(read_txt_file(path, buffer, sizeof(buffer)) != 0)
539 return NULL;
540 }
541
542 char *clean = trim(buffer);
543 if (!clean)
544 return NULL;
545
546 return strdupz(clean);
547 }
548
549 static inline char *get_disk_serial(char *device) {
550 char path[256 + 1];
551 char buffer[256 + 1];
552
553 snprintfz(path, sizeof(path) - 1, "%s/%s/device/serial", path_to_sys_block, device);
554 if(read_txt_file(path, buffer, sizeof(buffer)) != 0)
555 return NULL;
556
557 return strdupz(buffer);
558 }
559
560 //static inline bool get_disk_rotational(char *device) {
561 // char path[256 + 1];
562 // char buffer[256 + 1];
563 //
564 // snprintfz(path, 256, "%s/%s/queue/rotational", path_to_sys_block, device);
565 // if(read_file(path, buffer, 256) != 0)
566 // return false;
567 //
568 // return buffer[0] == '1';
569 //}
570 //
571 //static inline bool get_disk_removable(char *device) {
572 // char path[256 + 1];
573 // char buffer[256 + 1];
574 //
575 // snprintfz(path, 256, "%s/%s/removable", path_to_sys_block, device);
576 // if(read_file(path, buffer, 256) != 0)
577 // return false;
578 //
579 // return buffer[0] == '1';
580 //}
581
582 static void get_disk_config(struct disk *d) {
583 int def_enable = global_enable_new_disks_detected_at_runtime;
584
585 if(def_enable != CONFIG_BOOLEAN_NO && (simple_pattern_matches(excluded_disks, d->device) || simple_pattern_matches(excluded_disks, d->disk))) {
586 d->excluded = true;
587 def_enable = CONFIG_BOOLEAN_NO;
588 }
589
590 char var_name[4096 + 1];
591 snprintfz(var_name, 4096, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS ":%s", d->disk);
592
593 if (inicfg_exists(&netdata_config, var_name, "enable"))
594 def_enable = inicfg_get_boolean_ondemand(&netdata_config, var_name, "enable", def_enable);
595
596 if(unlikely(def_enable == CONFIG_BOOLEAN_NO)) {
597 // the user does not want any metrics for this disk
598 d->do_io = CONFIG_BOOLEAN_NO;
599 d->do_ops = CONFIG_BOOLEAN_NO;
600 d->do_mops = CONFIG_BOOLEAN_NO;
601 d->do_iotime = CONFIG_BOOLEAN_NO;
602 d->do_qops = CONFIG_BOOLEAN_NO;
603 d->do_util = CONFIG_BOOLEAN_NO;
604 d->do_ext = CONFIG_BOOLEAN_NO;
605 d->do_backlog = CONFIG_BOOLEAN_NO;
606 d->do_bcache = CONFIG_BOOLEAN_NO;
607 }
608 else {
609 // this disk is enabled
610 // check its direct settings
611
612 int def_performance = CONFIG_BOOLEAN_AUTO;
613
614 // since this is 'on demand' we can figure the performance settings
615 // based on the type of disk
616
617 if(!d->device_is_bcache) {
618 switch(d->type) {
619 default:
620 case DISK_TYPE_UNKNOWN:
621 break;
622
623 case DISK_TYPE_PHYSICAL:
624 def_performance = global_enable_performance_for_physical_disks;
625 break;
626
627 case DISK_TYPE_PARTITION:
628 def_performance = global_enable_performance_for_partitions;
629 break;
630
631 case DISK_TYPE_VIRTUAL:
632 def_performance = global_enable_performance_for_virtual_disks;
633 break;
634 }
635 }
636
637 // check if we have to disable performance for this disk
638 if(def_performance)
639 def_performance = is_major_enabled((int)d->major);
640
641 // ------------------------------------------------------------
642 // now we have def_performance and def_space
643 // to work further
644
645 // def_performance
646 // check the user configuration (this will also show our 'on demand' decision)
647 if (inicfg_exists(&netdata_config, var_name, "enable performance metrics"))
648 def_performance = inicfg_get_boolean_ondemand(&netdata_config, var_name, "enable performance metrics", def_performance);
649
650 int ddo_io = CONFIG_BOOLEAN_NO,
651 ddo_ops = CONFIG_BOOLEAN_NO,
652 ddo_mops = CONFIG_BOOLEAN_NO,
653 ddo_iotime = CONFIG_BOOLEAN_NO,
654 ddo_qops = CONFIG_BOOLEAN_NO,
655 ddo_util = CONFIG_BOOLEAN_NO,
656 ddo_ext = CONFIG_BOOLEAN_NO,
657 ddo_backlog = CONFIG_BOOLEAN_NO,
658 ddo_bcache = CONFIG_BOOLEAN_NO;
659
660 // we enable individual performance charts only when def_performance is not disabled
661 if(unlikely(def_performance != CONFIG_BOOLEAN_NO)) {
662 ddo_io = global_do_io,
663 ddo_ops = global_do_ops,
664 ddo_mops = global_do_mops,
665 ddo_iotime = global_do_iotime,
666 ddo_qops = global_do_qops,
667 ddo_util = global_do_util,
668 ddo_ext = global_do_ext,
669 ddo_backlog = global_do_backlog,
670 ddo_bcache = global_do_bcache;
671 } else {
672 d->excluded = true;
673 }
674
675 d->do_io = ddo_io;
676 d->do_ops = ddo_ops;
677 d->do_mops = ddo_mops;
678 d->do_iotime = ddo_iotime;
679 d->do_qops = ddo_qops;
680 d->do_util = ddo_util;
681 d->do_ext = ddo_ext;
682 d->do_backlog = ddo_backlog;
683
684 if (inicfg_exists(&netdata_config, var_name, "bandwidth"))
685 d->do_io = inicfg_get_boolean_ondemand(&netdata_config, var_name, "bandwidth", ddo_io);
686 if (inicfg_exists(&netdata_config, var_name, "operations"))
687 d->do_ops = inicfg_get_boolean_ondemand(&netdata_config, var_name, "operations", ddo_ops);
688 if (inicfg_exists(&netdata_config, var_name, "merged operations"))
689 d->do_mops = inicfg_get_boolean_ondemand(&netdata_config, var_name, "merged operations", ddo_mops);
690 if (inicfg_exists(&netdata_config, var_name, "i/o time"))
691 d->do_iotime = inicfg_get_boolean_ondemand(&netdata_config, var_name, "i/o time", ddo_iotime);
692 if (inicfg_exists(&netdata_config, var_name, "queued operations"))
693 d->do_qops = inicfg_get_boolean_ondemand(&netdata_config, var_name, "queued operations", ddo_qops);
694 if (inicfg_exists(&netdata_config, var_name, "utilization percentage"))
695 d->do_util = inicfg_get_boolean_ondemand(&netdata_config, var_name, "utilization percentage", ddo_util);
696 if (inicfg_exists(&netdata_config, var_name, "extended operations"))
697 d->do_ext = inicfg_get_boolean_ondemand(&netdata_config, var_name, "extended operations", ddo_ext);
698 if (inicfg_exists(&netdata_config, var_name, "backlog"))
699 d->do_backlog = inicfg_get_boolean_ondemand(&netdata_config, var_name, "backlog", ddo_backlog);
700
701 d->do_bcache = ddo_bcache;
702
703 if (d->device_is_bcache) {
704 if (inicfg_exists(&netdata_config, var_name, "bcache"))
705 d->do_bcache = inicfg_get_boolean_ondemand(&netdata_config, var_name, "bcache", ddo_bcache);
706 } else {
707 d->do_bcache = 0;
708 }
709 }
710 }
711
712 static struct disk *get_disk(unsigned long major, unsigned long minor, char *disk) {
713 static struct mountinfo *disk_mountinfo_root = NULL;
714
715 struct disk *d;
716
717 uint32_t hash = simple_hash(disk);
718
719 // search for it in our RAM list.
720 // this is sequential, but since we just walk through
721 // and the number of disks / partitions in a system
722 // should not be that many, it should be acceptable
723 for(d = disk_root; d ; d = d->next){
724 if (unlikely(
725 d->major == major && d->minor == minor && d->hash == hash && !strcmp(d->device, disk)))
726 return d;
727 }
728
729 // not found
730 // create a new disk structure
731 d = (struct disk *)callocz(1, sizeof(struct disk));
732
733 d->excluded = false;
734 d->function_ready = false;
735 d->disk = get_disk_name(major, minor, disk);
736 d->device = strdupz(disk);
737 d->disk_by_id = get_disk_by_id(disk);
738 d->model = get_disk_model(disk);
739 d->serial = get_disk_serial(disk);
740 // d->rotational = get_disk_rotational(disk);
741 // d->removable = get_disk_removable(disk);
742 d->hash = simple_hash(d->device);
743 d->major = major;
744 d->minor = minor;
745 d->type = DISK_TYPE_UNKNOWN; // Default type. Changed later if not correct.
746 d->next = NULL;
747
748 // append it to the list
749 if(unlikely(!disk_root))
750 disk_root = d;
751 else {
752 struct disk *last;
753 for(last = disk_root; last->next ;last = last->next);
754 last->next = d;
755 }
756
757 d->chart_id = strdupz(d->device);
758
759 // read device uuid if it is an LVM volume
760 if (!strncmp(d->device, "dm-", 3)) {
761 char uuid_filename[FILENAME_MAX + 1];
762 int size = snprintfz(uuid_filename, FILENAME_MAX, path_to_sys_devices_virtual_block_device, disk);
763 strncat(uuid_filename, "/dm/uuid", FILENAME_MAX - size);
764
765 char device_uuid[RRD_ID_LENGTH_MAX + 1];
766 if (!read_txt_file(uuid_filename, device_uuid, sizeof(device_uuid)) && !strncmp(device_uuid, "LVM-", 4)) {
767 trim(device_uuid);
768
769 char chart_id[RRD_ID_LENGTH_MAX + 1];
770 snprintf(chart_id, RRD_ID_LENGTH_MAX, "%s-%s", d->device, device_uuid + 4);
771
772 freez(d->chart_id);
773 d->chart_id = strdupz(chart_id);
774 }
775 }
776
777 char buffer[FILENAME_MAX + 1];
778
779 // find if it is a physical disk
780 // by checking if /sys/block/DISK is readable.
781 snprintfz(buffer, FILENAME_MAX, path_to_sys_block_device, disk);
782 if(likely(access(buffer, R_OK) == 0)) {
783 // assign it here, but it will be overwritten if it is not a physical disk
784 d->type = DISK_TYPE_PHYSICAL;
785 }
786
787 // find if it is a partition
788 // by checking if /sys/dev/block/MAJOR:MINOR/partition is readable.
789 snprintfz(buffer, FILENAME_MAX, path_to_sys_dev_block_major_minor_string, major, minor, "partition");
790 if(likely(access(buffer, R_OK) == 0)) {
791 d->type = DISK_TYPE_PARTITION;
792 }
793 else {
794 // find if it is a virtual disk
795 // by checking if /sys/devices/virtual/block/DISK is readable.
796 snprintfz(buffer, FILENAME_MAX, path_to_sys_devices_virtual_block_device, disk);
797 if(likely(access(buffer, R_OK) == 0)) {
798 d->type = DISK_TYPE_VIRTUAL;
799 }
800 else {
801 // find if it is a virtual device
802 // by checking if /sys/dev/block/MAJOR:MINOR/slaves has entries
803 snprintfz(buffer, FILENAME_MAX, path_to_sys_dev_block_major_minor_string, major, minor, "slaves/");
804 DIR *dirp = opendir(buffer);
805 if (likely(dirp != NULL)) {
806 struct dirent *dp;
807 while ((dp = readdir(dirp))) {
808 // . and .. are also files in empty folders.
809 if (unlikely(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)) {
810 continue;
811 }
812
813 d->type = DISK_TYPE_VIRTUAL;
814
815 // Stop the loop after we found one file.
816 break;
817 }
818 if (unlikely(closedir(dirp) == -1))
819 collector_error("Unable to close dir %s", buffer);
820 }
821 }
822 }
823
824 // ------------------------------------------------------------------------
825 // check if we can find its mount point
826
827 // mountinfo_find() can be called with NULL disk_mountinfo_root
828 struct mountinfo *mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor, d->device);
829 if(unlikely(!mi)) {
830 // mountinfo_free_all can be called with NULL
831 mountinfo_free_all(disk_mountinfo_root);
832 disk_mountinfo_root = mountinfo_read(0);
833 mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor, d->device);
834 }
835
836 if(unlikely(mi))
837 d->mount_point = strdupz(mi->mount_point);
838 else
839 d->mount_point = NULL;
840
841 // ------------------------------------------------------------------------
842 // check if the device is a bcache
843
844 struct stat bcache;
845 snprintfz(buffer, FILENAME_MAX, path_to_sys_block_device_bcache, disk);
846 if(unlikely(stat(buffer, &bcache) == 0 && (bcache.st_mode & S_IFMT) == S_IFDIR)) {
847 // we have the 'bcache' directory
848 d->device_is_bcache = 1;
849
850 char buffer2[FILENAME_MAX + 1];
851
852 snprintfz(buffer2, FILENAME_MAX, "%s/cache/congested", buffer);
853 if(access(buffer2, R_OK) == 0)
854 d->bcache_filename_cache_congested = strdupz(buffer2);
855 else
856 collector_error("bcache file '%s' cannot be read.", buffer2);
857
858 snprintfz(buffer2, FILENAME_MAX, "%s/readahead", buffer);
859 if(access(buffer2, R_OK) == 0)
860 d->bcache_filename_stats_total_cache_readaheads = strdupz(buffer2);
861 else
862 collector_error("bcache file '%s' cannot be read.", buffer2);
863
864 snprintfz(buffer2, FILENAME_MAX, "%s/cache/cache0/priority_stats", buffer); // only one cache is supported by bcache
865 if(access(buffer2, R_OK) == 0)
866 d->bcache_filename_priority_stats = strdupz(buffer2);
867 else
868 collector_error("bcache file '%s' cannot be read.", buffer2);
869
870 snprintfz(buffer2, FILENAME_MAX, "%s/cache/internal/cache_read_races", buffer);
871 if(access(buffer2, R_OK) == 0)
872 d->bcache_filename_cache_read_races = strdupz(buffer2);
873 else
874 collector_error("bcache file '%s' cannot be read.", buffer2);
875
876 snprintfz(buffer2, FILENAME_MAX, "%s/cache/cache0/io_errors", buffer);
877 if(access(buffer2, R_OK) == 0)
878 d->bcache_filename_cache_io_errors = strdupz(buffer2);
879 else
880 collector_error("bcache file '%s' cannot be read.", buffer2);
881
882 snprintfz(buffer2, FILENAME_MAX, "%s/dirty_data", buffer);
883 if(access(buffer2, R_OK) == 0)
884 d->bcache_filename_dirty_data = strdupz(buffer2);
885 else
886 collector_error("bcache file '%s' cannot be read.", buffer2);
887
888 snprintfz(buffer2, FILENAME_MAX, "%s/writeback_rate", buffer);
889 if(access(buffer2, R_OK) == 0)
890 d->bcache_filename_writeback_rate = strdupz(buffer2);
891 else
892 collector_error("bcache file '%s' cannot be read.", buffer2);
893
894 snprintfz(buffer2, FILENAME_MAX, "%s/cache/cache_available_percent", buffer);
895 if(access(buffer2, R_OK) == 0)
896 d->bcache_filename_cache_available_percent = strdupz(buffer2);
897 else
898 collector_error("bcache file '%s' cannot be read.", buffer2);
899
900 snprintfz(buffer2, FILENAME_MAX, "%s/stats_total/cache_hits", buffer);
901 if(access(buffer2, R_OK) == 0)
902 d->bcache_filename_stats_total_cache_hits = strdupz(buffer2);
903 else
904 collector_error("bcache file '%s' cannot be read.", buffer2);
905
906 snprintfz(buffer2, FILENAME_MAX, "%s/stats_five_minute/cache_hit_ratio", buffer);
907 if(access(buffer2, R_OK) == 0)
908 d->bcache_filename_stats_five_minute_cache_hit_ratio = strdupz(buffer2);
909 else
910 collector_error("bcache file '%s' cannot be read.", buffer2);
911
912 snprintfz(buffer2, FILENAME_MAX, "%s/stats_hour/cache_hit_ratio", buffer);
913 if(access(buffer2, R_OK) == 0)
914 d->bcache_filename_stats_hour_cache_hit_ratio = strdupz(buffer2);
915 else
916 collector_error("bcache file '%s' cannot be read.", buffer2);
917
918 snprintfz(buffer2, FILENAME_MAX, "%s/stats_day/cache_hit_ratio", buffer);
919 if(access(buffer2, R_OK) == 0)
920 d->bcache_filename_stats_day_cache_hit_ratio = strdupz(buffer2);
921 else
922 collector_error("bcache file '%s' cannot be read.", buffer2);
923
924 snprintfz(buffer2, FILENAME_MAX, "%s/stats_total/cache_hit_ratio", buffer);
925 if(access(buffer2, R_OK) == 0)
926 d->bcache_filename_stats_total_cache_hit_ratio = strdupz(buffer2);
927 else
928 collector_error("bcache file '%s' cannot be read.", buffer2);
929
930 snprintfz(buffer2, FILENAME_MAX, "%s/stats_total/cache_misses", buffer);
931 if(access(buffer2, R_OK) == 0)
932 d->bcache_filename_stats_total_cache_misses = strdupz(buffer2);
933 else
934 collector_error("bcache file '%s' cannot be read.", buffer2);
935
936 snprintfz(buffer2, FILENAME_MAX, "%s/stats_total/cache_bypass_hits", buffer);
937 if(access(buffer2, R_OK) == 0)
938 d->bcache_filename_stats_total_cache_bypass_hits = strdupz(buffer2);
939 else
940 collector_error("bcache file '%s' cannot be read.", buffer2);
941
942 snprintfz(buffer2, FILENAME_MAX, "%s/stats_total/cache_bypass_misses", buffer);
943 if(access(buffer2, R_OK) == 0)
944 d->bcache_filename_stats_total_cache_bypass_misses = strdupz(buffer2);
945 else
946 collector_error("bcache file '%s' cannot be read.", buffer2);
947
948 snprintfz(buffer2, FILENAME_MAX, "%s/stats_total/cache_miss_collisions", buffer);
949 if(access(buffer2, R_OK) == 0)
950 d->bcache_filename_stats_total_cache_miss_collisions = strdupz(buffer2);
951 else
952 collector_error("bcache file '%s' cannot be read.", buffer2);
953 }
954
955 get_disk_config(d);
956
957 return d;
958 }
959
960 static const char *get_disk_type_string(int disk_type) {
961 switch (disk_type) {
962 case DISK_TYPE_PHYSICAL:
963 return "physical";
964 case DISK_TYPE_PARTITION:
965 return "partition";
966 case DISK_TYPE_VIRTUAL:
967 return "virtual";
968 default:
969 return "unknown";
970 }
971 }
972
973 static void add_labels_to_disk(struct disk *d, RRDSET *st) {
974 rrdlabels_add(st->rrdlabels, "device", d->disk, RRDLABEL_SRC_AUTO);
975 rrdlabels_add(st->rrdlabels, "mount_point", d->mount_point, RRDLABEL_SRC_AUTO);
976 rrdlabels_add(st->rrdlabels, "id", d->disk_by_id, RRDLABEL_SRC_AUTO);
977 rrdlabels_add(st->rrdlabels, "model", d->model, RRDLABEL_SRC_AUTO);
978 rrdlabels_add(st->rrdlabels, "serial", d->serial, RRDLABEL_SRC_AUTO);
979 rrdlabels_add(st->rrdlabels, "device_type", get_disk_type_string(d->type), RRDLABEL_SRC_AUTO);
980 }
981
982 static void disk_labels_cb(RRDSET *st, void *data) {
983 add_labels_to_disk(data, st);
984 }
985
986 static int diskstats_function_block_devices(BUFFER *wb, const char *function __maybe_unused, BUFFER *payload __maybe_unused, const char *source __maybe_unused) {
987 buffer_flush(wb);
988 wb->content_type = CT_APPLICATION_JSON;
989 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
990
991 buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(localhost));
992 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
993 buffer_json_member_add_string(wb, "type", "table");
994 buffer_json_member_add_time_t(wb, "update_every", 1);
995 buffer_json_member_add_boolean(wb, "has_history", false);
996 buffer_json_member_add_string(wb, "help", RRDFUNCTIONS_DISKSTATS_HELP);
997 buffer_json_member_add_array(wb, "data");
998
999 double max_io_reads = 0.0;
1000 double max_io_writes = 0.0;
1001 double max_io = 0.0;
1002 double max_backlog_time = 0.0;
1003 double max_busy_time = 0.0;
1004 double max_busy_perc = 0.0;
1005 double max_iops_reads = 0.0;
1006 double max_iops_writes = 0.0;
1007 double max_iops_time_reads = 0.0;
1008 double max_iops_time_writes = 0.0;
1009 double max_iops_avg_time_read = 0.0;
1010 double max_iops_avg_time_write = 0.0;
1011 double max_iops_avg_size_read = 0.0;
1012 double max_iops_avg_size_write = 0.0;
1013
1014 netdata_mutex_lock(&diskstats_dev_mutex);
1015
1016 for (struct disk *d = disk_root; d; d = d->next) {
1017 if (unlikely(!d->function_ready))
1018 continue;
1019
1020 buffer_json_add_array_item_array(wb);
1021
1022 buffer_json_add_array_item_string(wb, d->device);
1023 buffer_json_add_array_item_string(wb, get_disk_type_string(d->type));
1024 buffer_json_add_array_item_string(wb, d->disk_by_id);
1025 buffer_json_add_array_item_string(wb, d->model);
1026 buffer_json_add_array_item_string(wb, d->serial);
1027
1028 // IO
1029 double io_reads = rrddim_get_last_stored_value(d->disk_io.rd_io_reads, &max_io_reads, 1024.0);
1030 double io_writes = rrddim_get_last_stored_value(d->disk_io.rd_io_writes, &max_io_writes, 1024.0);
1031 double io_total = NAN;
1032 if (!isnan(io_reads) && !isnan(io_writes)) {
1033 io_total = io_reads + io_writes;
1034 max_io = MAX(max_io, io_total);
1035 }
1036 // Backlog and Busy Time
1037 double busy_perc = rrddim_get_last_stored_value(d->disk_util.rd_util, &max_busy_perc, 1);
1038 double busy_time = rrddim_get_last_stored_value(d->disk_busy.rd_busy, &max_busy_time, 1);
1039 double backlog_time = rrddim_get_last_stored_value(d->rd_backlog_backlog, &max_backlog_time, 1);
1040 // IOPS
1041 double iops_reads = rrddim_get_last_stored_value(d->disk_ops.rd_ops_reads, &max_iops_reads, 1);
1042 double iops_writes = rrddim_get_last_stored_value(d->disk_ops.rd_ops_writes, &max_iops_writes, 1);
1043 // IO Time
1044 double iops_time_reads = rrddim_get_last_stored_value(d->disk_iotime.rd_reads_ms, &max_iops_time_reads, 1);
1045 double iops_time_writes = rrddim_get_last_stored_value(d->disk_iotime.rd_writes_ms, &max_iops_time_writes, 1);
1046 // Avg IO Time
1047 double iops_avg_time_read = rrddim_get_last_stored_value(d->disk_await.rd_await_reads, &max_iops_avg_time_read, 1);
1048 double iops_avg_time_write = rrddim_get_last_stored_value(d->disk_await.rd_await_writes, &max_iops_avg_time_write, 1);
1049 // Avg IO Size
1050 double iops_avg_size_read = rrddim_get_last_stored_value(d->disk_avgsz.rd_avgsz_reads, &max_iops_avg_size_read, 1);
1051 double iops_avg_size_write = rrddim_get_last_stored_value(d->disk_avgsz.rd_avgsz_writes, &max_iops_avg_size_write, 1);
1052
1053
1054 buffer_json_add_array_item_double(wb, io_reads);
1055 buffer_json_add_array_item_double(wb, io_writes);
1056 buffer_json_add_array_item_double(wb, io_total);
1057 buffer_json_add_array_item_double(wb, busy_perc);
1058 buffer_json_add_array_item_double(wb, busy_time);
1059 buffer_json_add_array_item_double(wb, backlog_time);
1060 buffer_json_add_array_item_double(wb, iops_reads);
1061 buffer_json_add_array_item_double(wb, iops_writes);
1062 buffer_json_add_array_item_double(wb, iops_time_reads);
1063 buffer_json_add_array_item_double(wb, iops_time_writes);
1064 buffer_json_add_array_item_double(wb, iops_avg_time_read);
1065 buffer_json_add_array_item_double(wb, iops_avg_time_write);
1066 buffer_json_add_array_item_double(wb, iops_avg_size_read);
1067 buffer_json_add_array_item_double(wb, iops_avg_size_write);
1068
1069 // End
1070 buffer_json_array_close(wb);
1071 }
1072
1073 netdata_mutex_unlock(&diskstats_dev_mutex);
1074
1075 buffer_json_array_close(wb); // data
1076 buffer_json_member_add_object(wb, "columns");
1077 {
1078 size_t field_id = 0;
1079
1080 buffer_rrdf_table_add_field(wb, field_id++, "Device", "Device Name",
1081 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1082 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1083 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1084 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
1085 NULL);
1086 buffer_rrdf_table_add_field(wb, field_id++, "Type", "Device Type",
1087 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1088 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1089 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1090 RRDF_FIELD_OPTS_UNIQUE_KEY,
1091 NULL);
1092 buffer_rrdf_table_add_field(wb, field_id++, "ID", "Device ID",
1093 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1094 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1095 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1096 RRDF_FIELD_OPTS_UNIQUE_KEY,
1097 NULL);
1098 buffer_rrdf_table_add_field(wb, field_id++, "Model", "Device Model",
1099 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1100 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1101 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1102 RRDF_FIELD_OPTS_UNIQUE_KEY,
1103 NULL);
1104 buffer_rrdf_table_add_field(wb, field_id++, "Serial", "Device Serial Number",
1105 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1106 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1107 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1108 RRDF_FIELD_OPTS_UNIQUE_KEY,
1109 NULL);
1110
1111 buffer_rrdf_table_add_field(wb, field_id++, "Read", "Data Read from Device",
1112 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1113 2, "MiB", max_io_reads, RRDF_FIELD_SORT_DESCENDING, NULL,
1114 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1115 RRDF_FIELD_OPTS_VISIBLE,
1116 NULL);
1117 buffer_rrdf_table_add_field(wb, field_id++, "Written", "Data Writen to Device",
1118 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1119 2, "MiB", max_io_writes, RRDF_FIELD_SORT_DESCENDING, NULL,
1120 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1121 RRDF_FIELD_OPTS_VISIBLE,
1122 NULL);
1123 buffer_rrdf_table_add_field(wb, field_id++, "Total", "Data Transferred to and from Device",
1124 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1125 2, "MiB", max_io, RRDF_FIELD_SORT_DESCENDING, NULL,
1126 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1127 RRDF_FIELD_OPTS_NONE,
1128 NULL);
1129
1130 buffer_rrdf_table_add_field(wb, field_id++, "Busy%", "Disk Busy Percentage",
1131 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1132 2, "%", max_busy_perc, RRDF_FIELD_SORT_DESCENDING, NULL,
1133 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1134 RRDF_FIELD_OPTS_VISIBLE,
1135 NULL);
1136 buffer_rrdf_table_add_field(wb, field_id++, "Busy", "Disk Busy Time",
1137 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1138 2, "milliseconds", max_busy_time, RRDF_FIELD_SORT_DESCENDING, NULL,
1139 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1140 RRDF_FIELD_OPTS_VISIBLE,
1141 NULL);
1142 buffer_rrdf_table_add_field(wb, field_id++, "Backlog", "Disk Backlog",
1143 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1144 2, "milliseconds", max_backlog_time, RRDF_FIELD_SORT_DESCENDING, NULL,
1145 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1146 RRDF_FIELD_OPTS_VISIBLE,
1147 NULL);
1148
1149 buffer_rrdf_table_add_field(wb, field_id++, "Reads", "Completed Read Operations",
1150 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1151 2, "ops", max_iops_reads, RRDF_FIELD_SORT_DESCENDING, NULL,
1152 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1153 RRDF_FIELD_OPTS_VISIBLE,
1154 NULL);
1155 buffer_rrdf_table_add_field(wb, field_id++, "Writes", "Completed Write Operations",
1156 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1157 2, "ops", max_iops_writes, RRDF_FIELD_SORT_DESCENDING, NULL,
1158 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1159 RRDF_FIELD_OPTS_VISIBLE,
1160 NULL);
1161
1162 buffer_rrdf_table_add_field(wb, field_id++, "ReadsTime", "Read Operations Time",
1163 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1164 2, "milliseconds", max_iops_time_reads, RRDF_FIELD_SORT_DESCENDING, NULL,
1165 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1166 RRDF_FIELD_OPTS_VISIBLE,
1167 NULL);
1168 buffer_rrdf_table_add_field(wb, field_id++, "WritesTime", "Write Operations Time",
1169 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1170 2, "milliseconds", max_iops_time_writes, RRDF_FIELD_SORT_DESCENDING, NULL,
1171 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1172 RRDF_FIELD_OPTS_VISIBLE,
1173 NULL);
1174
1175 buffer_rrdf_table_add_field(wb, field_id++, "ReadAvgTime", "Average Read Operation Service Time",
1176 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1177 2, "milliseconds", max_iops_avg_time_read, RRDF_FIELD_SORT_DESCENDING, NULL,
1178 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1179 RRDF_FIELD_OPTS_VISIBLE,
1180 NULL);
1181 buffer_rrdf_table_add_field(wb, field_id++, "WriteAvgTime", "Average Write Operation Service Time",
1182 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1183 2, "milliseconds", max_iops_avg_time_write, RRDF_FIELD_SORT_DESCENDING, NULL,
1184 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1185 RRDF_FIELD_OPTS_VISIBLE,
1186 NULL);
1187
1188 buffer_rrdf_table_add_field(wb, field_id++, "ReadAvgSz", "Average Read Operation Size",
1189 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1190 2, "KiB", max_iops_avg_size_read, RRDF_FIELD_SORT_DESCENDING, NULL,
1191 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1192 RRDF_FIELD_OPTS_VISIBLE,
1193 NULL);
1194 buffer_rrdf_table_add_field(wb, field_id++, "WriteAvgSz", "Average Write Operation Size",
1195 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
1196 2, "KiB", max_iops_avg_size_write, RRDF_FIELD_SORT_DESCENDING, NULL,
1197 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1198 RRDF_FIELD_OPTS_VISIBLE,
1199 NULL);
1200 }
1201
1202 buffer_json_object_close(wb); // columns
1203 buffer_json_member_add_string(wb, "default_sort_column", "Total");
1204
1205 buffer_json_member_add_object(wb, "charts");
1206 {
1207 buffer_json_member_add_object(wb, "IO");
1208 {
1209 buffer_json_member_add_string(wb, "name", "IO");
1210 buffer_json_member_add_string(wb, "type", "stacked-bar");
1211 buffer_json_member_add_array(wb, "columns");
1212 {
1213 buffer_json_add_array_item_string(wb, "Read");
1214 buffer_json_add_array_item_string(wb, "Written");
1215 }
1216 buffer_json_array_close(wb);
1217 }
1218 buffer_json_object_close(wb);
1219
1220 buffer_json_member_add_object(wb, "Busy");
1221 {
1222 buffer_json_member_add_string(wb, "name", "Busy");
1223 buffer_json_member_add_string(wb, "type", "stacked-bar");
1224 buffer_json_member_add_array(wb, "columns");
1225 {
1226 buffer_json_add_array_item_string(wb, "Busy");
1227 }
1228 buffer_json_array_close(wb);
1229 }
1230 buffer_json_object_close(wb);
1231 }
1232 buffer_json_object_close(wb); // charts
1233
1234 buffer_json_member_add_array(wb, "default_charts");
1235 {
1236 buffer_json_add_array_item_array(wb);
1237 buffer_json_add_array_item_string(wb, "IO");
1238 buffer_json_add_array_item_string(wb, "Device");
1239 buffer_json_array_close(wb);
1240
1241 buffer_json_add_array_item_array(wb);
1242 buffer_json_add_array_item_string(wb, "Busy");
1243 buffer_json_add_array_item_string(wb, "Device");
1244 buffer_json_array_close(wb);
1245 }
1246 buffer_json_array_close(wb);
1247
1248 buffer_json_member_add_object(wb, "group_by");
1249 {
1250 buffer_json_member_add_object(wb, "Type");
1251 {
1252 buffer_json_member_add_string(wb, "name", "Type");
1253 buffer_json_member_add_array(wb, "columns");
1254 {
1255 buffer_json_add_array_item_string(wb, "Type");
1256 }
1257 buffer_json_array_close(wb);
1258 }
1259 buffer_json_object_close(wb);
1260 }
1261 buffer_json_object_close(wb); // group_by
1262
1263 buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1);
1264 buffer_json_finalize(wb);
1265
1266 return HTTP_RESP_OK;
1267 }
1268
1269 static void diskstats_cleanup_disks() {
1270 struct disk *d = disk_root, *last = NULL;
1271 while (d) {
1272 if (unlikely(global_cleanup_removed_disks && !d->updated)) {
1273 struct disk *t = d;
1274
1275 rrdset_obsolete_and_pointer_null(d->disk_io.st_io);
1276 rrdset_obsolete_and_pointer_null(d->disk_ops.st_ops);
1277 rrdset_obsolete_and_pointer_null(d->disk_qops.st_qops);
1278 rrdset_obsolete_and_pointer_null(d->disk_util.st_util);
1279 rrdset_obsolete_and_pointer_null(d->disk_busy.st_busy);
1280 rrdset_obsolete_and_pointer_null(d->disk_iotime.st_iotime);
1281 rrdset_obsolete_and_pointer_null(d->disk_await.st_await);
1282 rrdset_obsolete_and_pointer_null(d->disk_svctm.st_svctm);
1283
1284 rrdset_obsolete_and_pointer_null(d->disk_avgsz.st_avgsz);
1285 rrdset_obsolete_and_pointer_null(d->st_ext_avgsz);
1286 rrdset_obsolete_and_pointer_null(d->st_ext_await);
1287 rrdset_obsolete_and_pointer_null(d->st_backlog);
1288 rrdset_obsolete_and_pointer_null(d->disk_io.st_io);
1289 rrdset_obsolete_and_pointer_null(d->st_ext_io);
1290 rrdset_obsolete_and_pointer_null(d->st_ext_iotime);
1291 rrdset_obsolete_and_pointer_null(d->st_mops);
1292 rrdset_obsolete_and_pointer_null(d->st_ext_mops);
1293 rrdset_obsolete_and_pointer_null(d->st_ext_ops);
1294 rrdset_obsolete_and_pointer_null(d->st_bcache);
1295 rrdset_obsolete_and_pointer_null(d->st_bcache_bypass);
1296 rrdset_obsolete_and_pointer_null(d->st_bcache_rates);
1297 rrdset_obsolete_and_pointer_null(d->st_bcache_size);
1298 rrdset_obsolete_and_pointer_null(d->st_bcache_usage);
1299 rrdset_obsolete_and_pointer_null(d->st_bcache_hit_ratio);
1300 rrdset_obsolete_and_pointer_null(d->st_bcache_cache_allocations);
1301 rrdset_obsolete_and_pointer_null(d->st_bcache_cache_read_races);
1302
1303 if (d == disk_root) {
1304 disk_root = d = d->next;
1305 last = NULL;
1306 } else if (last) {
1307 last->next = d = d->next;
1308 }
1309
1310 freez(t->bcache_filename_dirty_data);
1311 freez(t->bcache_filename_writeback_rate);
1312 freez(t->bcache_filename_cache_congested);
1313 freez(t->bcache_filename_cache_available_percent);
1314 freez(t->bcache_filename_stats_five_minute_cache_hit_ratio);
1315 freez(t->bcache_filename_stats_hour_cache_hit_ratio);
1316 freez(t->bcache_filename_stats_day_cache_hit_ratio);
1317 freez(t->bcache_filename_stats_total_cache_hit_ratio);
1318 freez(t->bcache_filename_stats_total_cache_hits);
1319 freez(t->bcache_filename_stats_total_cache_misses);
1320 freez(t->bcache_filename_stats_total_cache_miss_collisions);
1321 freez(t->bcache_filename_stats_total_cache_bypass_hits);
1322 freez(t->bcache_filename_stats_total_cache_bypass_misses);
1323 freez(t->bcache_filename_stats_total_cache_readaheads);
1324 freez(t->bcache_filename_cache_read_races);
1325 freez(t->bcache_filename_cache_io_errors);
1326 freez(t->bcache_filename_priority_stats);
1327
1328 freez(t->disk);
1329 freez(t->device);
1330 freez(t->disk_by_id);
1331 freez(t->model);
1332 freez(t->serial);
1333 freez(t->mount_point);
1334 freez(t->chart_id);
1335 freez(t);
1336 } else {
1337 d->updated = 0;
1338 last = d;
1339 d = d->next;
1340 }
1341 }
1342 }
1343
1344 int do_proc_diskstats(int update_every, usec_t dt) {
1345 static procfile *ff = NULL;
1346
1347 if(unlikely(!globals_initialized)) {
1348 globals_initialized = 1;
1349
1350 global_enable_new_disks_detected_at_runtime = inicfg_get_boolean(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "enable new disks detected at runtime", global_enable_new_disks_detected_at_runtime);
1351 global_enable_performance_for_physical_disks = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "performance metrics for physical disks", global_enable_performance_for_physical_disks);
1352 global_enable_performance_for_virtual_disks = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "performance metrics for virtual disks", global_enable_performance_for_virtual_disks);
1353 global_enable_performance_for_partitions = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "performance metrics for partitions", global_enable_performance_for_partitions);
1354
1355 global_do_io = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "bandwidth for all disks", global_do_io);
1356 global_do_ops = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "operations for all disks", global_do_ops);
1357 global_do_mops = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "merged operations for all disks", global_do_mops);
1358 global_do_iotime = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "i/o time for all disks", global_do_iotime);
1359 global_do_qops = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "queued operations for all disks", global_do_qops);
1360 global_do_util = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "utilization percentage for all disks", global_do_util);
1361 global_do_ext = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "extended operations for all disks", global_do_ext);
1362 global_do_backlog = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "backlog for all disks", global_do_backlog);
1363 global_do_bcache = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "bcache for all disks", global_do_bcache);
1364 global_bcache_priority_stats_update_every = (int)inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "bcache priority stats update every", global_bcache_priority_stats_update_every);
1365
1366 global_cleanup_removed_disks = inicfg_get_boolean(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "remove charts of removed disks" , global_cleanup_removed_disks);
1367
1368 char buffer[FILENAME_MAX + 1];
1369
1370 snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/block/%s");
1371 path_to_sys_block_device = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to get block device", buffer);
1372
1373 snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/block/%s/bcache");
1374 path_to_sys_block_device_bcache = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to get block device bcache", buffer);
1375
1376 snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/virtual/block/%s");
1377 path_to_sys_devices_virtual_block_device = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to get virtual block device", buffer);
1378
1379 snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/dev/block/%lu:%lu/%s");
1380 path_to_sys_dev_block_major_minor_string = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to get block device infos", buffer);
1381
1382 snprintfz(buffer, FILENAME_MAX, "%s/dev/mapper", netdata_configured_host_prefix);
1383 path_to_device_mapper = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to device mapper", buffer);
1384
1385 snprintfz(buffer, FILENAME_MAX, "%s/dev/disk", netdata_configured_host_prefix);
1386 path_to_dev_disk = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to /dev/disk", buffer);
1387
1388 snprintfz(buffer, FILENAME_MAX, "%s/sys/block", netdata_configured_host_prefix);
1389 path_to_sys_block = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to /sys/block", buffer);
1390
1391 snprintfz(buffer, FILENAME_MAX, "%s/dev/disk/by-label", netdata_configured_host_prefix);
1392 path_to_device_label = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to /dev/disk/by-label", buffer);
1393
1394 snprintfz(buffer, FILENAME_MAX, "%s/dev/disk/by-id", netdata_configured_host_prefix);
1395 path_to_device_id = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to /dev/disk/by-id", buffer);
1396
1397 snprintfz(buffer, FILENAME_MAX, "%s/dev/vx/dsk", netdata_configured_host_prefix);
1398 path_to_veritas_volume_groups = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "path to /dev/vx/dsk", buffer);
1399
1400 name_disks_by_id = inicfg_get_boolean(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "name disks by id", name_disks_by_id);
1401
1402 preferred_ids = simple_pattern_create(
1403 inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "preferred disk ids", DEFAULT_PREFERRED_IDS), NULL,
1404 SIMPLE_PATTERN_EXACT, true);
1405
1406 excluded_disks = simple_pattern_create(
1407 inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "exclude disks", DEFAULT_EXCLUDED_DISKS), NULL,
1408 SIMPLE_PATTERN_EXACT, true);
1409
1410 rrd_function_add_inline(localhost, NULL, "block-devices", 10,
1411 RRDFUNCTIONS_PRIORITY_DEFAULT, RRDFUNCTIONS_VERSION_DEFAULT,
1412 RRDFUNCTIONS_DISKSTATS_HELP,
1413 "top", HTTP_ACCESS_ANONYMOUS_DATA,
1414 diskstats_function_block_devices);
1415 }
1416
1417 // --------------------------------------------------------------------------
1418
1419 if(unlikely(!ff)) {
1420 char filename[FILENAME_MAX + 1];
1421 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/diskstats");
1422 ff = procfile_open(inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_DISKSTATS, "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
1423 }
1424 if(unlikely(!ff)) return 0;
1425
1426 ff = procfile_readall(ff);
1427 if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
1428
1429 size_t lines = procfile_lines(ff), l;
1430
1431 collected_number system_read_kb = 0, system_write_kb = 0;
1432
1433 int do_dc_stats = 0, do_fl_stats = 0;
1434
1435 netdata_mutex_lock(&diskstats_dev_mutex);
1436
1437 for(l = 0; l < lines ;l++) {
1438 // --------------------------------------------------------------------------
1439 // Read parameters
1440
1441 char *disk;
1442 unsigned long major = 0, minor = 0;
1443
1444 collected_number rd_ios = 0, mreads = 0, readsectors = 0, readms = 0, wr_ios = 0, mwrites = 0, writesectors = 0, writems = 0,
1445 queued_ios = 0, busy_ms = 0, backlog_ms = 0,
1446 discards = 0, mdiscards = 0, discardsectors = 0, discardms = 0,
1447 flushes = 0, flushms = 0;
1448
1449
1450 collected_number last_rd_ios = 0, last_readsectors = 0, last_readms = 0,
1451 last_wr_ios = 0, last_writesectors = 0, last_writems = 0,
1452 last_busy_ms = 0,
1453 last_discards = 0, last_discardsectors = 0, last_discardms = 0,
1454 last_flushes = 0, last_flushms = 0;
1455
1456 size_t words = procfile_linewords(ff, l);
1457 if(unlikely(words < 14)) continue;
1458
1459 major = str2ul(procfile_lineword(ff, l, 0));
1460 minor = str2ul(procfile_lineword(ff, l, 1));
1461 disk = procfile_lineword(ff, l, 2);
1462
1463 // # of reads completed # of writes completed
1464 // This is the total number of reads or writes completed successfully.
1465 rd_ios = str2ull(procfile_lineword(ff, l, 3), NULL); // rd_ios
1466 wr_ios = str2ull(procfile_lineword(ff, l, 7), NULL); // wr_ios
1467
1468 // # of reads merged # of writes merged
1469 // Reads and writes which are adjacent to each other may be merged for
1470 // efficiency. Thus two 4K reads may become one 8K read before it is
1471 // ultimately handed to the disk, and so it will be counted (and queued)
1472 mreads = str2ull(procfile_lineword(ff, l, 4), NULL); // rd_merges_or_rd_sec
1473 mwrites = str2ull(procfile_lineword(ff, l, 8), NULL); // wr_merges
1474
1475 // # of sectors read # of sectors written
1476 // This is the total number of sectors read or written successfully.
1477 readsectors = str2ull(procfile_lineword(ff, l, 5), NULL); // rd_sec_or_wr_ios
1478 writesectors = str2ull(procfile_lineword(ff, l, 9), NULL); // wr_sec
1479
1480 // # of milliseconds spent reading # of milliseconds spent writing
1481 // This is the total number of milliseconds spent by all reads or writes (as
1482 // measured from __make_request() to end_that_request_last()).
1483 readms = str2ull(procfile_lineword(ff, l, 6), NULL); // rd_ticks_or_wr_sec
1484 writems = str2ull(procfile_lineword(ff, l, 10), NULL); // wr_ticks
1485
1486 // # of I/Os currently in progress
1487 // The only field that should go to zero. Incremented as requests are
1488 // given to appropriate struct request_queue and decremented as they finish.
1489 queued_ios = str2ull(procfile_lineword(ff, l, 11), NULL); // ios_pgr
1490
1491 // # of milliseconds spent doing I/Os
1492 // This field increases so long as field queued_ios is nonzero.
1493 busy_ms = str2ull(procfile_lineword(ff, l, 12), NULL); // tot_ticks
1494
1495 // weighted # of milliseconds spent doing I/Os
1496 // This field is incremented at each I/O start, I/O completion, I/O
1497 // merge, or read of these stats by the number of I/Os in progress
1498 // (field queued_ios) times the number of milliseconds spent doing I/O since the
1499 // last update of this field. This can provide an easy measure of both
1500 // I/O completion time and the backlog that may be accumulating.
1501 backlog_ms = str2ull(procfile_lineword(ff, l, 13), NULL); // rq_ticks
1502
1503 if (unlikely(words > 13)) {
1504 do_dc_stats = 1;
1505
1506 // # of discards completed
1507 // This is the total number of discards completed successfully.
1508 discards = str2ull(procfile_lineword(ff, l, 14), NULL); // dc_ios
1509
1510 // # of discards merged
1511 // See the description of mreads/mwrites
1512 mdiscards = str2ull(procfile_lineword(ff, l, 15), NULL); // dc_merges
1513
1514 // # of sectors discarded
1515 // This is the total number of sectors discarded successfully.
1516 discardsectors = str2ull(procfile_lineword(ff, l, 16), NULL); // dc_sec
1517
1518 // # of milliseconds spent discarding
1519 // This is the total number of milliseconds spent by all discards (as
1520 // measured from __make_request() to end_that_request_last()).
1521 discardms = str2ull(procfile_lineword(ff, l, 17), NULL); // dc_ticks
1522 }
1523
1524 if (unlikely(words > 17)) {
1525 do_fl_stats = 1;
1526
1527 // number of flush I/Os processed
1528 // These values increment when an flush I/O request completes.
1529 // Block layer combines flush requests and executes at most one at a time.
1530 // This counts flush requests executed by disk. Not tracked for partitions.
1531 flushes = str2ull(procfile_lineword(ff, l, 18), NULL); // fl_ios
1532
1533 // total wait time for flush requests
1534 flushms = str2ull(procfile_lineword(ff, l, 19), NULL); // fl_ticks
1535 }
1536
1537 // --------------------------------------------------------------------------
1538 // get a disk structure for the disk
1539
1540 struct disk *d = get_disk(major, minor, disk);
1541 d->updated = 1;
1542
1543 // --------------------------------------------------------------------------
1544 // count the global system disk I/O of physical disks
1545
1546 if(unlikely(d->type == DISK_TYPE_PHYSICAL)) {
1547 system_read_kb += readsectors * SECTOR_SIZE / 1024;
1548 system_write_kb += writesectors * SECTOR_SIZE / 1024;
1549 }
1550
1551 // --------------------------------------------------------------------------
1552 // Set its family based on mount point
1553
1554 char *family = d->mount_point;
1555 if(!family) family = d->disk;
1556
1557
1558 // --------------------------------------------------------------------------
1559 // Do performance metrics
1560 if (d->do_io == CONFIG_BOOLEAN_YES || d->do_io == CONFIG_BOOLEAN_AUTO) {
1561 d->do_io = CONFIG_BOOLEAN_YES;
1562
1563 last_readsectors = d->disk_io.rd_io_reads ? rrddim_last_collected_raw_int(d->disk_io.rd_io_reads) / SECTOR_SIZE : 0;
1564 last_writesectors = d->disk_io.rd_io_writes ? rrddim_last_collected_raw_int(d->disk_io.rd_io_writes) / SECTOR_SIZE : 0;
1565
1566 common_disk_io(&d->disk_io,
1567 d->chart_id,
1568 d->disk,
1569 readsectors * SECTOR_SIZE,
1570 writesectors * SECTOR_SIZE,
1571 update_every,
1572 disk_labels_cb,
1573 d);
1574 }
1575
1576 if (do_dc_stats && d->do_io == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
1577 if (unlikely(!d->st_ext_io)) {
1578 d->st_ext_io = rrdset_create_localhost(
1579 "disk_ext"
1580 , d->chart_id
1581 , d->disk
1582 , family
1583 , "disk_ext.io"
1584 , "Amount of Discarded Data"
1585 , "KiB/s"
1586 , PLUGIN_PROC_NAME
1587 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1588 , NETDATA_CHART_PRIO_DISK_IO + 1
1589 , update_every
1590 , RRDSET_TYPE_AREA
1591 );
1592
1593 d->rd_io_discards = rrddim_add(d->st_ext_io, "discards", NULL, SECTOR_SIZE, 1024, RRD_ALGORITHM_INCREMENTAL);
1594
1595 add_labels_to_disk(d, d->st_ext_io);
1596 }
1597
1598 last_discardsectors = rrddim_set_by_pointer(d->st_ext_io, d->rd_io_discards, discardsectors);
1599 rrdset_done(d->st_ext_io);
1600 }
1601
1602 if (d->do_ops == CONFIG_BOOLEAN_YES || d->do_ops == CONFIG_BOOLEAN_AUTO) {
1603 d->do_ops = CONFIG_BOOLEAN_YES;
1604
1605 last_rd_ios = d->disk_ops.rd_ops_reads ? rrddim_last_collected_raw_int(d->disk_ops.rd_ops_reads) : 0;
1606 last_wr_ios = d->disk_ops.rd_ops_writes ? rrddim_last_collected_raw_int(d->disk_ops.rd_ops_writes) : 0;
1607
1608 common_disk_ops(&d->disk_ops,
1609 d->chart_id,
1610 d->disk, rd_ios, wr_ios,
1611 update_every,
1612 disk_labels_cb,
1613 d);
1614 }
1615
1616 if (do_dc_stats && d->do_ops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
1617 if (unlikely(!d->st_ext_ops)) {
1618 d->st_ext_ops = rrdset_create_localhost(
1619 "disk_ext_ops"
1620 , d->chart_id
1621 , d->disk
1622 , family
1623 , "disk_ext.ops"
1624 , "Disk Completed Extended I/O Operations"
1625 , "operations/s"
1626 , PLUGIN_PROC_NAME
1627 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1628 , NETDATA_CHART_PRIO_DISK_OPS + 1
1629 , update_every
1630 , RRDSET_TYPE_LINE
1631 );
1632
1633 d->rd_ops_discards = rrddim_add(d->st_ext_ops, "discards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1634 if (do_fl_stats)
1635 d->rd_ops_flushes = rrddim_add(d->st_ext_ops, "flushes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1636
1637 add_labels_to_disk(d, d->st_ext_ops);
1638 }
1639
1640 last_discards = rrddim_set_by_pointer(d->st_ext_ops, d->rd_ops_discards, discards);
1641 if (do_fl_stats)
1642 last_flushes = rrddim_set_by_pointer(d->st_ext_ops, d->rd_ops_flushes, flushes);
1643 rrdset_done(d->st_ext_ops);
1644 }
1645
1646 if (d->do_qops == CONFIG_BOOLEAN_YES || d->do_qops == CONFIG_BOOLEAN_AUTO) {
1647 d->do_qops = CONFIG_BOOLEAN_YES;
1648
1649 common_disk_qops(
1650 &d->disk_qops,
1651 d->chart_id,
1652 d->disk,
1653 queued_ios,
1654 update_every,
1655 disk_labels_cb,
1656 d);
1657 }
1658
1659 if (d->do_backlog == CONFIG_BOOLEAN_YES || d->do_backlog == CONFIG_BOOLEAN_AUTO) {
1660 d->do_backlog = CONFIG_BOOLEAN_YES;
1661
1662 if(unlikely(!d->st_backlog)) {
1663 d->st_backlog = rrdset_create_localhost(
1664 "disk_backlog"
1665 , d->chart_id
1666 , d->disk
1667 , family
1668 , "disk.backlog"
1669 , "Disk Backlog"
1670 , "milliseconds"
1671 , PLUGIN_PROC_NAME
1672 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1673 , NETDATA_CHART_PRIO_DISK_BACKLOG
1674 , update_every
1675 , RRDSET_TYPE_AREA
1676 );
1677
1678 d->rd_backlog_backlog = rrddim_add(d->st_backlog, "backlog", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1679
1680 add_labels_to_disk(d, d->st_backlog);
1681 }
1682
1683 rrddim_set_by_pointer(d->st_backlog, d->rd_backlog_backlog, backlog_ms);
1684 rrdset_done(d->st_backlog);
1685 }
1686
1687 if (d->do_util == CONFIG_BOOLEAN_YES || d->do_util == CONFIG_BOOLEAN_AUTO) {
1688 d->do_util = CONFIG_BOOLEAN_YES;
1689
1690 last_busy_ms = d->disk_busy.rd_busy ? rrddim_last_collected_raw_int(d->disk_busy.rd_busy) : 0;
1691
1692 common_disk_busy(&d->disk_busy,
1693 d->chart_id,
1694 d->disk,
1695 busy_ms,
1696 update_every,
1697 disk_labels_cb,
1698 d);
1699
1700 collected_number disk_utilization = (busy_ms - last_busy_ms) / (10 * update_every);
1701 if (disk_utilization > 100)
1702 disk_utilization = 100;
1703
1704 common_disk_util(&d->disk_util,
1705 d->chart_id,
1706 d->disk,
1707 disk_utilization,
1708 update_every,
1709 disk_labels_cb,
1710 d);
1711
1712 }
1713
1714 if (d->do_mops == CONFIG_BOOLEAN_YES || d->do_mops == CONFIG_BOOLEAN_AUTO) {
1715 d->do_mops = CONFIG_BOOLEAN_YES;
1716
1717 if(unlikely(!d->st_mops)) {
1718 d->st_mops = rrdset_create_localhost(
1719 "disk_mops"
1720 , d->chart_id
1721 , d->disk
1722 , family
1723 , "disk.mops"
1724 , "Disk Merged Operations"
1725 , "merged operations/s"
1726 , PLUGIN_PROC_NAME
1727 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1728 , NETDATA_CHART_PRIO_DISK_MOPS
1729 , update_every
1730 , RRDSET_TYPE_LINE
1731 );
1732
1733 d->rd_mops_reads = rrddim_add(d->st_mops, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1734 d->rd_mops_writes = rrddim_add(d->st_mops, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
1735
1736 add_labels_to_disk(d, d->st_mops);
1737 }
1738
1739 rrddim_set_by_pointer(d->st_mops, d->rd_mops_reads, mreads);
1740 rrddim_set_by_pointer(d->st_mops, d->rd_mops_writes, mwrites);
1741 rrdset_done(d->st_mops);
1742 }
1743
1744 if(do_dc_stats && d->do_mops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
1745 d->do_mops = CONFIG_BOOLEAN_YES;
1746
1747 if(unlikely(!d->st_ext_mops)) {
1748 d->st_ext_mops = rrdset_create_localhost(
1749 "disk_ext_mops"
1750 , d->chart_id
1751 , d->disk
1752 , family
1753 , "disk_ext.mops"
1754 , "Disk Merged Discard Operations"
1755 , "merged operations/s"
1756 , PLUGIN_PROC_NAME
1757 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1758 , NETDATA_CHART_PRIO_DISK_MOPS + 1
1759 , update_every
1760 , RRDSET_TYPE_LINE
1761 );
1762
1763 d->rd_mops_discards = rrddim_add(d->st_ext_mops, "discards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1764
1765 add_labels_to_disk(d, d->st_ext_mops);
1766 }
1767
1768 rrddim_set_by_pointer(d->st_ext_mops, d->rd_mops_discards, mdiscards);
1769 rrdset_done(d->st_ext_mops);
1770 }
1771
1772 if (d->do_iotime == CONFIG_BOOLEAN_YES || d->do_iotime == CONFIG_BOOLEAN_AUTO) {
1773 d->do_iotime = CONFIG_BOOLEAN_YES;
1774
1775 last_readms = d->disk_iotime.rd_reads_ms ? rrddim_last_collected_raw_int(d->disk_iotime.rd_reads_ms) : 0;
1776 last_writems = d->disk_iotime.rd_writes_ms ? rrddim_last_collected_raw_int(d->disk_iotime.rd_writes_ms) : 0;
1777
1778 common_disk_iotime(
1779 &d->disk_iotime,
1780 d->chart_id,
1781 d->disk,
1782 readms,
1783 writems,
1784 update_every,
1785 disk_labels_cb,
1786 d);
1787 }
1788
1789 if(do_dc_stats && d->do_iotime == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
1790 if(unlikely(!d->st_ext_iotime)) {
1791 d->st_ext_iotime = rrdset_create_localhost(
1792 "disk_ext_iotime"
1793 , d->chart_id
1794 , d->disk
1795 , family
1796 , "disk_ext.iotime"
1797 , "Disk Total I/O Time for Extended Operations"
1798 , "milliseconds/s"
1799 , PLUGIN_PROC_NAME
1800 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1801 , NETDATA_CHART_PRIO_DISK_IOTIME + 1
1802 , update_every
1803 , RRDSET_TYPE_LINE
1804 );
1805
1806 d->rd_iotime_discards = rrddim_add(d->st_ext_iotime, "discards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1807 if (do_fl_stats)
1808 d->rd_iotime_flushes = rrddim_add(d->st_ext_iotime, "flushes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1809
1810 add_labels_to_disk(d, d->st_ext_iotime);
1811 }
1812
1813 last_discardms = rrddim_set_by_pointer(d->st_ext_iotime, d->rd_iotime_discards, discardms);
1814 if (do_fl_stats)
1815 last_flushms = rrddim_set_by_pointer(d->st_ext_iotime, d->rd_iotime_flushes, flushms);
1816 rrdset_done(d->st_ext_iotime);
1817 }
1818
1819 // calculate differential charts
1820 // only if this is not the first time we run
1821
1822 if(likely(dt)) {
1823 if ((d->do_iotime == CONFIG_BOOLEAN_YES || d->do_iotime == CONFIG_BOOLEAN_AUTO) &&
1824 (d->do_ops == CONFIG_BOOLEAN_YES || d->do_ops == CONFIG_BOOLEAN_AUTO)) {
1825
1826 double read_ms_avg = (rd_ios - last_rd_ios) ? (double)(readms - last_readms) / (rd_ios - last_rd_ios) : 0;
1827 double write_ms_avg = (wr_ios - last_wr_ios) ? (double)(writems - last_writems) / (wr_ios - last_wr_ios) : 0;
1828
1829 common_disk_await(
1830 &d->disk_await,
1831 d->chart_id,
1832 d->disk,
1833 read_ms_avg,
1834 write_ms_avg,
1835 update_every,
1836 disk_labels_cb,
1837 d);
1838 }
1839
1840 if (do_dc_stats && d->do_iotime == CONFIG_BOOLEAN_YES && d->do_ops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
1841 if(unlikely(!d->st_ext_await)) {
1842 d->st_ext_await = rrdset_create_localhost(
1843 "disk_ext_await"
1844 , d->chart_id
1845 , d->disk
1846 , family
1847 , "disk_ext.await"
1848 , "Average Completed Extended I/O Operation Time"
1849 , "milliseconds/operation"
1850 , PLUGIN_PROC_NAME
1851 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1852 , NETDATA_CHART_PRIO_DISK_AWAIT + 1
1853 , update_every
1854 , RRDSET_TYPE_LINE
1855 );
1856
1857 d->rd_await_discards = rrddim_add(d->st_ext_await, "discards", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
1858 if (do_fl_stats)
1859 d->rd_await_flushes = rrddim_add(d->st_ext_await, "flushes", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
1860
1861 add_labels_to_disk(d, d->st_ext_await);
1862 }
1863
1864 double discard_avg =
1865 (discards - last_discards) ? (double)(discardms - last_discardms) / (discards - last_discards) : 0;
1866 double flushe_avg =
1867 (flushes - last_flushes) ? (double)(flushms - last_flushms) / (flushes - last_flushes) : 0;
1868
1869 rrddim_set_by_pointer(d->st_ext_await, d->rd_await_discards, (collected_number)(discard_avg * 1000));
1870
1871 if (do_fl_stats)
1872 rrddim_set_by_pointer(d->st_ext_await, d->rd_await_flushes, (collected_number)(flushe_avg * 1000));
1873
1874 rrdset_done(d->st_ext_await);
1875 }
1876
1877 if ((d->do_io == CONFIG_BOOLEAN_YES || d->do_io == CONFIG_BOOLEAN_AUTO) &&
1878 (d->do_ops == CONFIG_BOOLEAN_YES || d->do_ops == CONFIG_BOOLEAN_AUTO)) {
1879
1880 kernel_uint_t avg_read_bytes = SECTOR_SIZE * ((rd_ios - last_rd_ios) ? (readsectors - last_readsectors) / (rd_ios - last_rd_ios) : 0);
1881 kernel_uint_t avg_write_bytes = SECTOR_SIZE * ((wr_ios - last_wr_ios) ? (writesectors - last_writesectors) / (wr_ios - last_wr_ios) : 0);
1882
1883 common_disk_avgsz(
1884 &d->disk_avgsz,
1885 d->chart_id,
1886 d->disk,
1887 avg_read_bytes,
1888 avg_write_bytes,
1889 update_every,
1890 disk_labels_cb,
1891 d);
1892 }
1893
1894 if(do_dc_stats && d->do_io == CONFIG_BOOLEAN_YES && d->do_ops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
1895 if(unlikely(!d->st_ext_avgsz)) {
1896 d->st_ext_avgsz = rrdset_create_localhost(
1897 "disk_ext_avgsz"
1898 , d->chart_id
1899 , d->disk
1900 , family
1901 , "disk_ext.avgsz"
1902 , "Average Amount of Discarded Data"
1903 , "KiB/operation"
1904 , PLUGIN_PROC_NAME
1905 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1906 , NETDATA_CHART_PRIO_DISK_AVGSZ
1907 , update_every
1908 , RRDSET_TYPE_AREA
1909 );
1910
1911 d->rd_avgsz_discards = rrddim_add(d->st_ext_avgsz, "discards", NULL, SECTOR_SIZE, 1024, RRD_ALGORITHM_ABSOLUTE);
1912
1913 add_labels_to_disk(d, d->st_ext_avgsz);
1914 }
1915
1916 rrddim_set_by_pointer(
1917 d->st_ext_avgsz, d->rd_avgsz_discards,
1918 (discards - last_discards) ? (discardsectors - last_discardsectors) / (discards - last_discards) :
1919 0);
1920 rrdset_done(d->st_ext_avgsz);
1921 }
1922
1923 if ((d->do_util == CONFIG_BOOLEAN_YES || d->do_util == CONFIG_BOOLEAN_AUTO) &&
1924 (d->do_ops == CONFIG_BOOLEAN_YES || d->do_ops == CONFIG_BOOLEAN_AUTO)) {
1925
1926 double svctm_avg =
1927 ((rd_ios - last_rd_ios) + (wr_ios - last_wr_ios)) ?
1928 (double) (busy_ms - last_busy_ms) / ((rd_ios - last_rd_ios) + (wr_ios - last_wr_ios)) :
1929 0;
1930
1931 common_disk_svctm(
1932 &d->disk_svctm,
1933 d->chart_id,
1934 d->disk,
1935 svctm_avg,
1936 update_every,
1937 disk_labels_cb,
1938 d);
1939 }
1940 }
1941
1942 // read bcache metrics and generate the bcache charts
1943
1944 if(d->device_is_bcache && d->do_bcache != CONFIG_BOOLEAN_NO) {
1945 unsigned long long int
1946 stats_total_cache_bypass_hits = 0,
1947 stats_total_cache_bypass_misses = 0,
1948 stats_total_cache_hits = 0,
1949 stats_total_cache_miss_collisions = 0,
1950 stats_total_cache_misses = 0,
1951 stats_five_minute_cache_hit_ratio = 0,
1952 stats_hour_cache_hit_ratio = 0,
1953 stats_day_cache_hit_ratio = 0,
1954 stats_total_cache_hit_ratio = 0,
1955 cache_available_percent = 0,
1956 cache_readaheads = 0,
1957 cache_read_races = 0,
1958 cache_io_errors = 0,
1959 cache_congested = 0,
1960 dirty_data = 0,
1961 writeback_rate = 0;
1962
1963 // read the bcache values
1964
1965 if(d->bcache_filename_dirty_data)
1966 dirty_data = bcache_read_number_with_units(d->bcache_filename_dirty_data);
1967
1968 if(d->bcache_filename_writeback_rate)
1969 writeback_rate = bcache_read_number_with_units(d->bcache_filename_writeback_rate);
1970
1971 if(d->bcache_filename_cache_congested)
1972 cache_congested = bcache_read_number_with_units(d->bcache_filename_cache_congested);
1973
1974 if(d->bcache_filename_cache_available_percent)
1975 read_single_number_file(d->bcache_filename_cache_available_percent, &cache_available_percent);
1976
1977 if(d->bcache_filename_stats_five_minute_cache_hit_ratio)
1978 read_single_number_file(d->bcache_filename_stats_five_minute_cache_hit_ratio, &stats_five_minute_cache_hit_ratio);
1979
1980 if(d->bcache_filename_stats_hour_cache_hit_ratio)
1981 read_single_number_file(d->bcache_filename_stats_hour_cache_hit_ratio, &stats_hour_cache_hit_ratio);
1982
1983 if(d->bcache_filename_stats_day_cache_hit_ratio)
1984 read_single_number_file(d->bcache_filename_stats_day_cache_hit_ratio, &stats_day_cache_hit_ratio);
1985
1986 if(d->bcache_filename_stats_total_cache_hit_ratio)
1987 read_single_number_file(d->bcache_filename_stats_total_cache_hit_ratio, &stats_total_cache_hit_ratio);
1988
1989 if(d->bcache_filename_stats_total_cache_hits)
1990 read_single_number_file(d->bcache_filename_stats_total_cache_hits, &stats_total_cache_hits);
1991
1992 if(d->bcache_filename_stats_total_cache_misses)
1993 read_single_number_file(d->bcache_filename_stats_total_cache_misses, &stats_total_cache_misses);
1994
1995 if(d->bcache_filename_stats_total_cache_miss_collisions)
1996 read_single_number_file(d->bcache_filename_stats_total_cache_miss_collisions, &stats_total_cache_miss_collisions);
1997
1998 if(d->bcache_filename_stats_total_cache_bypass_hits)
1999 read_single_number_file(d->bcache_filename_stats_total_cache_bypass_hits, &stats_total_cache_bypass_hits);
2000
2001 if(d->bcache_filename_stats_total_cache_bypass_misses)
2002 read_single_number_file(d->bcache_filename_stats_total_cache_bypass_misses, &stats_total_cache_bypass_misses);
2003
2004 if(d->bcache_filename_stats_total_cache_readaheads)
2005 cache_readaheads = bcache_read_number_with_units(d->bcache_filename_stats_total_cache_readaheads);
2006
2007 if(d->bcache_filename_cache_read_races)
2008 read_single_number_file(d->bcache_filename_cache_read_races, &cache_read_races);
2009
2010 if(d->bcache_filename_cache_io_errors)
2011 read_single_number_file(d->bcache_filename_cache_io_errors, &cache_io_errors);
2012
2013 if(d->bcache_filename_priority_stats && global_bcache_priority_stats_update_every >= 1)
2014 bcache_read_priority_stats(d, family, global_bcache_priority_stats_update_every, dt);
2015
2016 // update the charts
2017
2018 {
2019 if(unlikely(!d->st_bcache_hit_ratio)) {
2020 d->st_bcache_hit_ratio = rrdset_create_localhost(
2021 "disk_bcache_hit_ratio"
2022 , d->chart_id
2023 , d->disk
2024 , family
2025 , "disk.bcache_hit_ratio"
2026 , "BCache Cache Hit Ratio"
2027 , "percentage"
2028 , PLUGIN_PROC_NAME
2029 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
2030 , NETDATA_CHART_PRIO_BCACHE_HIT_RATIO
2031 , update_every
2032 , RRDSET_TYPE_LINE
2033 );
2034
2035 d->rd_bcache_hit_ratio_5min = rrddim_add(d->st_bcache_hit_ratio, "5min", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
2036 d->rd_bcache_hit_ratio_1hour = rrddim_add(d->st_bcache_hit_ratio, "1hour", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
2037 d->rd_bcache_hit_ratio_1day = rrddim_add(d->st_bcache_hit_ratio, "1day", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
2038 d->rd_bcache_hit_ratio_total = rrddim_add(d->st_bcache_hit_ratio, "ever", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
2039
2040 add_labels_to_disk(d, d->st_bcache_hit_ratio);
2041 }
2042
2043 rrddim_set_by_pointer(d->st_bcache_hit_ratio, d->rd_bcache_hit_ratio_5min, stats_five_minute_cache_hit_ratio);
2044 rrddim_set_by_pointer(d->st_bcache_hit_ratio, d->rd_bcache_hit_ratio_1hour, stats_hour_cache_hit_ratio);
2045 rrddim_set_by_pointer(d->st_bcache_hit_ratio, d->rd_bcache_hit_ratio_1day, stats_day_cache_hit_ratio);
2046 rrddim_set_by_pointer(d->st_bcache_hit_ratio, d->rd_bcache_hit_ratio_total, stats_total_cache_hit_ratio);
2047 rrdset_done(d->st_bcache_hit_ratio);
2048 }
2049
2050 {
2051
2052 if(unlikely(!d->st_bcache_rates)) {
2053 d->st_bcache_rates = rrdset_create_localhost(
2054 "disk_bcache_rates"
2055 , d->chart_id
2056 , d->disk
2057 , family
2058 , "disk.bcache_rates"
2059 , "BCache Rates"
2060 , "KiB/s"
2061 , PLUGIN_PROC_NAME
2062 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
2063 , NETDATA_CHART_PRIO_BCACHE_RATES
2064 , update_every
2065 , RRDSET_TYPE_AREA
2066 );
2067
2068 d->rd_bcache_rate_congested = rrddim_add(d->st_bcache_rates, "congested", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
2069 d->rd_bcache_rate_writeback = rrddim_add(d->st_bcache_rates, "writeback", NULL, -1, 1024, RRD_ALGORITHM_ABSOLUTE);
2070
2071 add_labels_to_disk(d, d->st_bcache_rates);
2072 }
2073
2074 rrddim_set_by_pointer(d->st_bcache_rates, d->rd_bcache_rate_writeback, writeback_rate);
2075 rrddim_set_by_pointer(d->st_bcache_rates, d->rd_bcache_rate_congested, cache_congested);
2076 rrdset_done(d->st_bcache_rates);
2077 }
2078
2079 {
2080 if(unlikely(!d->st_bcache_size)) {
2081 d->st_bcache_size = rrdset_create_localhost(
2082 "disk_bcache_size"
2083 , d->chart_id
2084 , d->disk
2085 , family
2086 , "disk.bcache_size"
2087 , "BCache Cache Sizes"
2088 , "MiB"
2089 , PLUGIN_PROC_NAME
2090 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
2091 , NETDATA_CHART_PRIO_BCACHE_SIZE
2092 , update_every
2093 , RRDSET_TYPE_AREA
2094 );
2095
2096 d->rd_bcache_dirty_size = rrddim_add(d->st_bcache_size, "dirty", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2097
2098 add_labels_to_disk(d, d->st_bcache_size);
2099 }
2100
2101 rrddim_set_by_pointer(d->st_bcache_size, d->rd_bcache_dirty_size, dirty_data);
2102 rrdset_done(d->st_bcache_size);
2103 }
2104
2105 {
2106 if(unlikely(!d->st_bcache_usage)) {
2107 d->st_bcache_usage = rrdset_create_localhost(
2108 "disk_bcache_usage"
2109 , d->chart_id
2110 , d->disk
2111 , family
2112 , "disk.bcache_usage"
2113 , "BCache Cache Usage"
2114 , "percentage"
2115 , PLUGIN_PROC_NAME
2116 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
2117 , NETDATA_CHART_PRIO_BCACHE_USAGE
2118 , update_every
2119 , RRDSET_TYPE_AREA
2120 );
2121
2122 d->rd_bcache_available_percent = rrddim_add(d->st_bcache_usage, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
2123
2124 add_labels_to_disk(d, d->st_bcache_usage);
2125 }
2126
2127 rrddim_set_by_pointer(d->st_bcache_usage, d->rd_bcache_available_percent, cache_available_percent);
2128 rrdset_done(d->st_bcache_usage);
2129 }
2130
2131 {
2132
2133 if(unlikely(!d->st_bcache_cache_read_races)) {
2134 d->st_bcache_cache_read_races = rrdset_create_localhost(
2135 "disk_bcache_cache_read_races"
2136 , d->chart_id
2137 , d->disk
2138 , family
2139 , "disk.bcache_cache_read_races"
2140 , "BCache Cache Read Races"
2141 , "operations/s"
2142 , PLUGIN_PROC_NAME
2143 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
2144 , NETDATA_CHART_PRIO_BCACHE_CACHE_READ_RACES
2145 , update_every
2146 , RRDSET_TYPE_LINE
2147 );
2148
2149 d->rd_bcache_cache_read_races = rrddim_add(d->st_bcache_cache_read_races, "races", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2150 d->rd_bcache_cache_io_errors = rrddim_add(d->st_bcache_cache_read_races, "errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2151
2152 add_labels_to_disk(d, d->st_bcache_cache_read_races);
2153 }
2154
2155 rrddim_set_by_pointer(d->st_bcache_cache_read_races, d->rd_bcache_cache_read_races, cache_read_races);
2156 rrddim_set_by_pointer(d->st_bcache_cache_read_races, d->rd_bcache_cache_io_errors, cache_io_errors);
2157 rrdset_done(d->st_bcache_cache_read_races);
2158 }
2159
2160 if (d->do_bcache == CONFIG_BOOLEAN_YES || d->do_bcache == CONFIG_BOOLEAN_AUTO) {
2161 if(unlikely(!d->st_bcache)) {
2162 d->st_bcache = rrdset_create_localhost(
2163 "disk_bcache"
2164 , d->chart_id
2165 , d->disk
2166 , family
2167 , "disk.bcache"
2168 , "BCache Cache I/O Operations"
2169 , "operations/s"
2170 , PLUGIN_PROC_NAME
2171 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
2172 , NETDATA_CHART_PRIO_BCACHE_OPS
2173 , update_every
2174 , RRDSET_TYPE_LINE
2175 );
2176
2177 d->rd_bcache_hits = rrddim_add(d->st_bcache, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2178 d->rd_bcache_misses = rrddim_add(d->st_bcache, "misses", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
2179 d->rd_bcache_miss_collisions = rrddim_add(d->st_bcache, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
2180 d->rd_bcache_readaheads = rrddim_add(d->st_bcache, "readaheads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2181
2182 add_labels_to_disk(d, d->st_bcache);
2183 }
2184
2185 rrddim_set_by_pointer(d->st_bcache, d->rd_bcache_hits, stats_total_cache_hits);
2186 rrddim_set_by_pointer(d->st_bcache, d->rd_bcache_misses, stats_total_cache_misses);
2187 rrddim_set_by_pointer(d->st_bcache, d->rd_bcache_miss_collisions, stats_total_cache_miss_collisions);
2188 rrddim_set_by_pointer(d->st_bcache, d->rd_bcache_readaheads, cache_readaheads);
2189 rrdset_done(d->st_bcache);
2190 }
2191
2192 if (d->do_bcache == CONFIG_BOOLEAN_YES || d->do_bcache == CONFIG_BOOLEAN_AUTO) {
2193 if(unlikely(!d->st_bcache_bypass)) {
2194 d->st_bcache_bypass = rrdset_create_localhost(
2195 "disk_bcache_bypass"
2196 , d->chart_id
2197 , d->disk
2198 , family
2199 , "disk.bcache_bypass"
2200 , "BCache Cache Bypass I/O Operations"
2201 , "operations/s"
2202 , PLUGIN_PROC_NAME
2203 , PLUGIN_PROC_MODULE_DISKSTATS_NAME
2204 , NETDATA_CHART_PRIO_BCACHE_BYPASS
2205 , update_every
2206 , RRDSET_TYPE_LINE
2207 );
2208
2209 d->rd_bcache_bypass_hits = rrddim_add(d->st_bcache_bypass, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2210 d->rd_bcache_bypass_misses = rrddim_add(d->st_bcache_bypass, "misses", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
2211
2212 add_labels_to_disk(d, d->st_bcache_bypass);
2213 }
2214
2215 rrddim_set_by_pointer(d->st_bcache_bypass, d->rd_bcache_bypass_hits, stats_total_cache_bypass_hits);
2216 rrddim_set_by_pointer(d->st_bcache_bypass, d->rd_bcache_bypass_misses, stats_total_cache_bypass_misses);
2217 rrdset_done(d->st_bcache_bypass);
2218 }
2219 }
2220
2221 d->function_ready = !d->excluded;
2222 }
2223
2224 diskstats_cleanup_disks();
2225
2226 netdata_mutex_unlock(&diskstats_dev_mutex);
2227 // update the system total I/O
2228
2229 if (global_do_io == CONFIG_BOOLEAN_YES || global_do_io == CONFIG_BOOLEAN_AUTO) {
2230 common_system_io(system_read_kb * 1024, system_write_kb * 1024, update_every);
2231 }
2232
2233 return 0;
2234 }