2 * collectd - src/disk.c
3 * Copyright (C) 2005-2012 Florian octo Forster
4 * Copyright (C) 2009 Manuel Sanmartin
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; only version 2 of the License is applicable.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * Florian octo Forster <octo at collectd.org>
28 #include "utils_ignorelist.h"
30 #if HAVE_MACH_MACH_TYPES_H
31 #include <mach/mach_types.h>
33 #if HAVE_MACH_MACH_INIT_H
34 #include <mach/mach_init.h>
36 #if HAVE_MACH_MACH_ERROR_H
37 #include <mach/mach_error.h>
39 #if HAVE_MACH_MACH_PORT_H
40 #include <mach/mach_port.h>
42 #if HAVE_COREFOUNDATION_COREFOUNDATION_H
43 #include <CoreFoundation/CoreFoundation.h>
45 #if HAVE_IOKIT_IOKITLIB_H
46 #include <IOKit/IOKitLib.h>
48 #if HAVE_IOKIT_IOTYPES_H
49 #include <IOKit/IOTypes.h>
51 #if HAVE_IOKIT_STORAGE_IOBLOCKSTORAGEDRIVER_H
52 #include <IOKit/storage/IOBlockStorageDriver.h>
54 #if HAVE_IOKIT_IOBSD_H
55 #include <IOKit/IOBSD.h>
66 #define UINT_MAX 4294967295U
74 #ifndef _AIXVERSION_610
75 #include <sys/systemcfg.h>
77 #include <libperfstat.h>
78 #include <sys/protosw.h>
81 #if HAVE_IOKIT_IOKITLIB_H
82 static mach_port_t io_master_port = MACH_PORT_NULL;
83 /* This defaults to false for backwards compatibility. Please fix in the next
85 static bool use_bsd_name;
86 /* #endif HAVE_IOKIT_IOKITLIB_H */
89 typedef struct diskstats {
92 /* This overflows in roughly 1361 years */
93 unsigned int poll_count;
95 derive_t read_sectors;
96 derive_t write_sectors;
106 derive_t avg_read_time;
107 derive_t avg_write_time;
110 bool has_in_progress;
113 struct diskstats *next;
116 static diskstats_t *disklist;
117 /* #endif KERNEL_LINUX */
119 static struct gmesh geom_tree;
120 /* #endif KERNEL_FREEBSD */
126 #define MAX_NUMDISK 1024
127 extern kstat_ctl_t *kc;
128 static kstat_t *ksp[MAX_NUMDISK];
130 /* #endif HAVE_LIBKSTAT */
132 #elif defined(HAVE_LIBSTATGRAB)
133 /* #endif HAVE_LIBSTATGRAB */
136 static perfstat_disk_t *stat_disk;
139 /* #endif HAVE_PERFSTAT */
142 #error "No applicable input method."
148 static char *conf_udev_name_attr;
149 static struct udev *handle_udev;
152 static const char *config_keys[] = {"Disk", "UseBSDName", "IgnoreSelected",
154 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
156 static ignorelist_t *ignorelist;
158 static int disk_config(const char *key, const char *value) {
159 if (ignorelist == NULL)
160 ignorelist = ignorelist_create(/* invert = */ 1);
161 if (ignorelist == NULL)
164 if (strcasecmp("Disk", key) == 0) {
165 ignorelist_add(ignorelist, value);
166 } else if (strcasecmp("IgnoreSelected", key) == 0) {
170 ignorelist_set_invert(ignorelist, invert);
171 } else if (strcasecmp("UseBSDName", key) == 0) {
172 #if HAVE_IOKIT_IOKITLIB_H
173 use_bsd_name = IS_TRUE(value);
175 WARNING("disk plugin: The \"UseBSDName\" option is only supported "
176 "on Mach / Mac OS X and will be ignored.");
178 } else if (strcasecmp("UdevNameAttr", key) == 0) {
180 if (conf_udev_name_attr != NULL) {
181 free(conf_udev_name_attr);
182 conf_udev_name_attr = NULL;
184 if ((conf_udev_name_attr = strdup(value)) == NULL)
187 WARNING("disk plugin: The \"UdevNameAttr\" option is only supported "
188 "if collectd is built with libudev support");
195 } /* int disk_config */
197 static int disk_init(void) {
198 #if HAVE_IOKIT_IOKITLIB_H
199 kern_return_t status;
201 if (io_master_port != MACH_PORT_NULL) {
202 mach_port_deallocate(mach_task_self(), io_master_port);
203 io_master_port = MACH_PORT_NULL;
206 status = IOMasterPort(MACH_PORT_NULL, &io_master_port);
207 if (status != kIOReturnSuccess) {
208 ERROR("IOMasterPort failed: %s", mach_error_string(status));
209 io_master_port = MACH_PORT_NULL;
212 /* #endif HAVE_IOKIT_IOKITLIB_H */
216 if (conf_udev_name_attr != NULL) {
217 handle_udev = udev_new();
218 if (handle_udev == NULL) {
219 ERROR("disk plugin: udev_new() failed!");
223 #endif /* HAVE_LIBUDEV_H */
224 /* #endif KERNEL_LINUX */
229 rv = geom_gettree(&geom_tree);
231 ERROR("geom_gettree() failed, returned %d", rv);
234 rv = geom_stats_open();
236 ERROR("geom_stats_open() failed, returned %d", rv);
239 /* #endif KERNEL_FREEBSD */
249 for (numdisk = 0, ksp_chain = kc->kc_chain;
250 (numdisk < MAX_NUMDISK) && (ksp_chain != NULL);
251 ksp_chain = ksp_chain->ks_next) {
252 if (strncmp(ksp_chain->ks_class, "disk", 4) &&
253 strncmp(ksp_chain->ks_class, "partition", 9))
255 if (ksp_chain->ks_type != KSTAT_TYPE_IO)
257 ksp[numdisk++] = ksp_chain;
259 #endif /* HAVE_LIBKSTAT */
262 } /* int disk_init */
264 static int disk_shutdown(void) {
267 if (handle_udev != NULL)
268 udev_unref(handle_udev);
269 #endif /* HAVE_LIBUDEV_H */
270 #endif /* KERNEL_LINUX */
272 } /* int disk_shutdown */
274 static void disk_submit(const char *plugin_instance, const char *type,
275 derive_t read, derive_t write) {
276 value_list_t vl = VALUE_LIST_INIT;
278 {.derive = read}, {.derive = write},
282 vl.values_len = STATIC_ARRAY_SIZE(values);
283 sstrncpy(vl.plugin, "disk", sizeof(vl.plugin));
284 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
285 sstrncpy(vl.type, type, sizeof(vl.type));
287 plugin_dispatch_values(&vl);
288 } /* void disk_submit */
290 #if KERNEL_FREEBSD || KERNEL_LINUX
291 static void submit_io_time(char const *plugin_instance, derive_t io_time,
292 derive_t weighted_time) {
293 value_list_t vl = VALUE_LIST_INIT;
295 {.derive = io_time}, {.derive = weighted_time},
299 vl.values_len = STATIC_ARRAY_SIZE(values);
300 sstrncpy(vl.plugin, "disk", sizeof(vl.plugin));
301 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
302 sstrncpy(vl.type, "disk_io_time", sizeof(vl.type));
304 plugin_dispatch_values(&vl);
305 } /* void submit_io_time */
306 #endif /* KERNEL_FREEBSD || KERNEL_LINUX */
309 static void submit_in_progress(char const *disk_name, gauge_t in_progress) {
310 value_list_t vl = VALUE_LIST_INIT;
312 vl.values = &(value_t){.gauge = in_progress};
314 sstrncpy(vl.plugin, "disk", sizeof(vl.plugin));
315 sstrncpy(vl.plugin_instance, disk_name, sizeof(vl.plugin_instance));
316 sstrncpy(vl.type, "pending_operations", sizeof(vl.type));
318 plugin_dispatch_values(&vl);
321 static counter_t disk_calc_time_incr(counter_t delta_time,
322 counter_t delta_ops) {
323 double interval = CDTIME_T_TO_DOUBLE(plugin_get_interval());
324 double avg_time = ((double)delta_time) / ((double)delta_ops);
325 double avg_time_incr = interval * avg_time;
327 return (counter_t)(avg_time_incr + .5);
333 * Attempt to provide an rename disk instance from an assigned udev attribute.
335 * On success, it returns a strduped char* to the desired attribute value.
336 * Otherwise it returns NULL.
339 static char *disk_udev_attr_name(struct udev *udev, char *disk_name,
341 struct udev_device *dev;
345 dev = udev_device_new_from_subsystem_sysname(udev, "block", disk_name);
347 prop = udev_device_get_property_value(dev, attr);
349 output = strdup(prop);
350 DEBUG("disk plugin: renaming %s => %s", disk_name, output);
352 udev_device_unref(dev);
358 #if HAVE_IOKIT_IOKITLIB_H
359 static signed long long dict_get_value(CFDictionaryRef dict, const char *key) {
360 signed long long val_int;
364 /* `key_obj' needs to be released. */
365 key_obj = CFStringCreateWithCString(kCFAllocatorDefault, key,
366 kCFStringEncodingASCII);
367 if (key_obj == NULL) {
368 DEBUG("CFStringCreateWithCString (%s) failed.", key);
372 /* get => we don't need to release (== free) the object */
373 val_obj = (CFNumberRef)CFDictionaryGetValue(dict, key_obj);
377 if (val_obj == NULL) {
378 DEBUG("CFDictionaryGetValue (%s) failed.", key);
382 if (!CFNumberGetValue(val_obj, kCFNumberSInt64Type, &val_int)) {
383 DEBUG("CFNumberGetValue (%s) failed.", key);
389 #endif /* HAVE_IOKIT_IOKITLIB_H */
391 static int disk_read(void) {
392 #if HAVE_IOKIT_IOKITLIB_H
393 io_registry_entry_t disk;
394 io_registry_entry_t disk_child;
395 io_iterator_t disk_list;
396 CFMutableDictionaryRef props_dict, child_dict;
397 CFDictionaryRef stats_dict;
398 CFStringRef tmp_cf_string_ref;
399 kern_return_t status;
401 signed long long read_ops, read_byt, read_tme;
402 signed long long write_ops, write_byt, write_tme;
404 int disk_major, disk_minor;
405 char disk_name[DATA_MAX_NAME_LEN];
406 char child_disk_name_bsd[DATA_MAX_NAME_LEN],
407 props_disk_name_bsd[DATA_MAX_NAME_LEN];
409 /* Get the list of all disk objects. */
410 if (IOServiceGetMatchingServices(
411 io_master_port, IOServiceMatching(kIOBlockStorageDriverClass),
412 &disk_list) != kIOReturnSuccess) {
413 ERROR("disk plugin: IOServiceGetMatchingServices failed.");
417 while ((disk = IOIteratorNext(disk_list)) != 0) {
422 /* get child of disk entry and corresponding property dictionary */
423 if ((status = IORegistryEntryGetChildEntry(
424 disk, kIOServicePlane, &disk_child)) != kIOReturnSuccess) {
425 /* This fails for example for DVD/CD drives, which we want to ignore
427 DEBUG("IORegistryEntryGetChildEntry (disk) failed: 0x%08x", status);
428 IOObjectRelease(disk);
431 if (IORegistryEntryCreateCFProperties(
432 disk_child, (CFMutableDictionaryRef *)&child_dict,
433 kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess ||
434 child_dict == NULL) {
435 ERROR("disk plugin: IORegistryEntryCreateCFProperties (disk_child) "
437 IOObjectRelease(disk_child);
438 IOObjectRelease(disk);
442 /* extract name and major/minor numbers */
443 memset(child_disk_name_bsd, 0, sizeof(child_disk_name_bsd));
445 (CFStringRef)CFDictionaryGetValue(child_dict, CFSTR(kIOBSDNameKey));
446 if (tmp_cf_string_ref) {
447 assert(CFGetTypeID(tmp_cf_string_ref) == CFStringGetTypeID());
448 CFStringGetCString(tmp_cf_string_ref, child_disk_name_bsd,
449 sizeof(child_disk_name_bsd), kCFStringEncodingUTF8);
451 disk_major = (int)dict_get_value(child_dict, kIOBSDMajorKey);
452 disk_minor = (int)dict_get_value(child_dict, kIOBSDMinorKey);
453 DEBUG("disk plugin: child_disk_name_bsd=\"%s\" major=%d minor=%d",
454 child_disk_name_bsd, disk_major, disk_minor);
455 CFRelease(child_dict);
456 IOObjectRelease(disk_child);
458 /* get property dictionary of the disk entry itself */
459 if (IORegistryEntryCreateCFProperties(
460 disk, (CFMutableDictionaryRef *)&props_dict, kCFAllocatorDefault,
461 kNilOptions) != kIOReturnSuccess ||
462 props_dict == NULL) {
463 ERROR("disk-plugin: IORegistryEntryCreateCFProperties failed.");
464 IOObjectRelease(disk);
468 /* extract name and stats dictionary */
469 memset(props_disk_name_bsd, 0, sizeof(props_disk_name_bsd));
471 (CFStringRef)CFDictionaryGetValue(props_dict, CFSTR(kIOBSDNameKey));
472 if (tmp_cf_string_ref) {
473 assert(CFGetTypeID(tmp_cf_string_ref) == CFStringGetTypeID());
474 CFStringGetCString(tmp_cf_string_ref, props_disk_name_bsd,
475 sizeof(props_disk_name_bsd), kCFStringEncodingUTF8);
477 stats_dict = (CFDictionaryRef)CFDictionaryGetValue(
478 props_dict, CFSTR(kIOBlockStorageDriverStatisticsKey));
479 if (stats_dict == NULL) {
480 ERROR("disk plugin: CFDictionaryGetValue (%s) failed.",
481 kIOBlockStorageDriverStatisticsKey);
482 CFRelease(props_dict);
483 IOObjectRelease(disk);
486 DEBUG("disk plugin: props_disk_name_bsd=\"%s\"", props_disk_name_bsd);
490 if (child_disk_name_bsd[0] != 0)
491 sstrncpy(disk_name, child_disk_name_bsd, sizeof(disk_name));
492 else if (props_disk_name_bsd[0] != 0)
493 sstrncpy(disk_name, props_disk_name_bsd, sizeof(disk_name));
495 ERROR("disk plugin: can't find bsd disk name.");
496 snprintf(disk_name, sizeof(disk_name), "%i-%i", disk_major, disk_minor);
499 snprintf(disk_name, sizeof(disk_name), "%i-%i", disk_major, disk_minor);
501 DEBUG("disk plugin: disk_name = \"%s\"", disk_name);
503 /* check the name against ignore list */
504 if (ignorelist_match(ignorelist, disk_name) != 0) {
505 CFRelease(props_dict);
506 IOObjectRelease(disk);
510 /* extract the stats */
512 dict_get_value(stats_dict, kIOBlockStorageDriverStatisticsReadsKey);
514 dict_get_value(stats_dict, kIOBlockStorageDriverStatisticsBytesReadKey);
515 read_tme = dict_get_value(stats_dict,
516 kIOBlockStorageDriverStatisticsTotalReadTimeKey);
518 dict_get_value(stats_dict, kIOBlockStorageDriverStatisticsWritesKey);
519 write_byt = dict_get_value(stats_dict,
520 kIOBlockStorageDriverStatisticsBytesWrittenKey);
521 write_tme = dict_get_value(
522 stats_dict, kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
523 CFRelease(props_dict);
524 IOObjectRelease(disk);
527 if ((read_byt != -1LL) || (write_byt != -1LL))
528 disk_submit(disk_name, "disk_octets", read_byt, write_byt);
529 if ((read_ops != -1LL) || (write_ops != -1LL))
530 disk_submit(disk_name, "disk_ops", read_ops, write_ops);
531 if ((read_tme != -1LL) || (write_tme != -1LL))
532 disk_submit(disk_name, "disk_time", read_tme / 1000, write_tme / 1000);
534 IOObjectRelease(disk_list);
535 /* #endif HAVE_IOKIT_IOKITLIB_H */
541 struct devstat *snap_iter;
543 struct gident *geom_id;
545 const char *disk_name;
546 long double read_time, write_time, busy_time, total_duration;
548 for (retry = 0, dirty = 1; retry < 5 && dirty == 1; retry++) {
550 geom_stats_snapshot_free(snap);
552 /* Get a fresh copy of stats snapshot */
553 snap = geom_stats_snapshot_get();
555 ERROR("disk plugin: geom_stats_snapshot_get() failed.");
559 /* Check if we have dirty read from this snapshot */
561 geom_stats_snapshot_reset(snap);
562 while ((snap_iter = geom_stats_snapshot_next(snap)) != NULL) {
563 if (snap_iter->id == NULL)
565 geom_id = geom_lookupid(&geom_tree, snap_iter->id);
567 /* New device? refresh GEOM tree */
568 if (geom_id == NULL) {
569 geom_deletetree(&geom_tree);
570 if (geom_gettree(&geom_tree) != 0) {
571 ERROR("disk plugin: geom_gettree() failed");
572 geom_stats_snapshot_free(snap);
575 geom_id = geom_lookupid(&geom_tree, snap_iter->id);
578 * This should be rare: the device come right before we take the
579 * snapshot and went away right after it. We will handle this
580 * case later, so don't mark dirty but silently ignore it.
585 /* Only collect PROVIDER data */
586 if (geom_id->lg_what != ISPROVIDER)
589 /* Only collect data when rank is 1 (physical devices) */
590 if (((struct gprovider *)(geom_id->lg_ptr))->lg_geom->lg_rank != 1)
593 /* Check if this is a dirty read quit for another try */
594 if (snap_iter->sequence0 != snap_iter->sequence1) {
602 geom_stats_snapshot_reset(snap);
604 snap_iter = geom_stats_snapshot_next(snap);
605 if (snap_iter == NULL)
608 if (snap_iter->id == NULL)
610 geom_id = geom_lookupid(&geom_tree, snap_iter->id);
613 if (geom_id->lg_what != ISPROVIDER)
615 if (((struct gprovider *)(geom_id->lg_ptr))->lg_geom->lg_rank != 1)
617 /* Skip dirty reads, if present */
618 if (dirty && (snap_iter->sequence0 != snap_iter->sequence1))
621 disk_name = ((struct gprovider *)geom_id->lg_ptr)->lg_name;
623 if (ignorelist_match(ignorelist, disk_name) != 0)
626 if ((snap_iter->bytes[DEVSTAT_READ] != 0) ||
627 (snap_iter->bytes[DEVSTAT_WRITE] != 0)) {
628 disk_submit(disk_name, "disk_octets",
629 (derive_t)snap_iter->bytes[DEVSTAT_READ],
630 (derive_t)snap_iter->bytes[DEVSTAT_WRITE]);
633 if ((snap_iter->operations[DEVSTAT_READ] != 0) ||
634 (snap_iter->operations[DEVSTAT_WRITE] != 0)) {
635 disk_submit(disk_name, "disk_ops",
636 (derive_t)snap_iter->operations[DEVSTAT_READ],
637 (derive_t)snap_iter->operations[DEVSTAT_WRITE]);
640 read_time = devstat_compute_etime(&snap_iter->duration[DEVSTAT_READ], NULL);
642 devstat_compute_etime(&snap_iter->duration[DEVSTAT_WRITE], NULL);
643 if ((read_time != 0) || (write_time != 0)) {
644 disk_submit(disk_name, "disk_time", (derive_t)(read_time * 1000),
645 (derive_t)(write_time * 1000));
647 if (devstat_compute_statistics(snap_iter, NULL, 1.0, DSM_TOTAL_BUSY_TIME,
648 &busy_time, DSM_TOTAL_DURATION,
649 &total_duration, DSM_NONE) != 0) {
650 WARNING("%s", devstat_errbuf);
652 submit_io_time(disk_name, busy_time, total_duration);
655 geom_stats_snapshot_free(snap);
667 derive_t read_sectors = 0;
668 derive_t write_sectors = 0;
670 derive_t read_ops = 0;
671 derive_t read_merged = 0;
672 derive_t read_time = 0;
673 derive_t write_ops = 0;
674 derive_t write_merged = 0;
675 derive_t write_time = 0;
676 gauge_t in_progress = NAN;
677 derive_t io_time = 0;
678 derive_t weighted_time = 0;
681 diskstats_t *ds, *pre_ds;
683 if ((fh = fopen("/proc/diskstats", "r")) == NULL) {
684 fh = fopen("/proc/partitions", "r");
686 ERROR("disk plugin: fopen (/proc/{diskstats,partitions}) failed.");
690 /* Kernel is 2.4.* */
694 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
698 numfields = strsplit(buffer, fields, 32);
700 if ((numfields != (14 + fieldshift)) && (numfields != 7))
703 minor = atoll(fields[1]);
705 disk_name = fields[2 + fieldshift];
707 for (ds = disklist, pre_ds = disklist; ds != NULL;
708 pre_ds = ds, ds = ds->next)
709 if (strcmp(disk_name, ds->name) == 0)
713 if ((ds = (diskstats_t *)calloc(1, sizeof(diskstats_t))) == NULL)
716 if ((ds->name = strdup(disk_name)) == NULL) {
728 if (numfields == 7) {
729 /* Kernel 2.6, Partition */
730 read_ops = atoll(fields[3]);
731 read_sectors = atoll(fields[4]);
732 write_ops = atoll(fields[5]);
733 write_sectors = atoll(fields[6]);
734 } else if (numfields == (14 + fieldshift)) {
735 read_ops = atoll(fields[3 + fieldshift]);
736 write_ops = atoll(fields[7 + fieldshift]);
738 read_sectors = atoll(fields[5 + fieldshift]);
739 write_sectors = atoll(fields[9 + fieldshift]);
741 if ((fieldshift == 0) || (minor == 0)) {
743 read_merged = atoll(fields[4 + fieldshift]);
744 read_time = atoll(fields[6 + fieldshift]);
745 write_merged = atoll(fields[8 + fieldshift]);
746 write_time = atoll(fields[10 + fieldshift]);
748 in_progress = atof(fields[11 + fieldshift]);
750 io_time = atof(fields[12 + fieldshift]);
751 weighted_time = atof(fields[13 + fieldshift]);
754 DEBUG("numfields = %i; => unknown file format.", numfields);
759 derive_t diff_read_sectors;
760 derive_t diff_write_sectors;
762 /* If the counter wraps around, it's only 32 bits.. */
763 if (read_sectors < ds->read_sectors)
764 diff_read_sectors = 1 + read_sectors + (UINT_MAX - ds->read_sectors);
766 diff_read_sectors = read_sectors - ds->read_sectors;
767 if (write_sectors < ds->write_sectors)
768 diff_write_sectors = 1 + write_sectors + (UINT_MAX - ds->write_sectors);
770 diff_write_sectors = write_sectors - ds->write_sectors;
772 ds->read_bytes += 512 * diff_read_sectors;
773 ds->write_bytes += 512 * diff_write_sectors;
774 ds->read_sectors = read_sectors;
775 ds->write_sectors = write_sectors;
778 /* Calculate the average time an io-op needs to complete */
780 derive_t diff_read_ops;
781 derive_t diff_write_ops;
782 derive_t diff_read_time;
783 derive_t diff_write_time;
785 if (read_ops < ds->read_ops)
786 diff_read_ops = 1 + read_ops + (UINT_MAX - ds->read_ops);
788 diff_read_ops = read_ops - ds->read_ops;
789 DEBUG("disk plugin: disk_name = %s; read_ops = %" PRIi64 "; "
790 "ds->read_ops = %" PRIi64 "; diff_read_ops = %" PRIi64 ";",
791 disk_name, read_ops, ds->read_ops, diff_read_ops);
793 if (write_ops < ds->write_ops)
794 diff_write_ops = 1 + write_ops + (UINT_MAX - ds->write_ops);
796 diff_write_ops = write_ops - ds->write_ops;
798 if (read_time < ds->read_time)
799 diff_read_time = 1 + read_time + (UINT_MAX - ds->read_time);
801 diff_read_time = read_time - ds->read_time;
803 if (write_time < ds->write_time)
804 diff_write_time = 1 + write_time + (UINT_MAX - ds->write_time);
806 diff_write_time = write_time - ds->write_time;
808 if (diff_read_ops != 0)
809 ds->avg_read_time += disk_calc_time_incr(diff_read_time, diff_read_ops);
810 if (diff_write_ops != 0)
811 ds->avg_write_time +=
812 disk_calc_time_incr(diff_write_time, diff_write_ops);
814 ds->read_ops = read_ops;
815 ds->read_time = read_time;
816 ds->write_ops = write_ops;
817 ds->write_time = write_time;
819 if (read_merged || write_merged)
820 ds->has_merged = true;
823 ds->has_in_progress = true;
826 ds->has_io_time = true;
830 /* Don't write to the RRDs if we've just started.. */
832 if (ds->poll_count <= 2) {
833 DEBUG("disk plugin: (ds->poll_count = %i) <= "
834 "(min_poll_count = 2); => Not writing.",
839 if ((read_ops == 0) && (write_ops == 0)) {
840 DEBUG("disk plugin: ((read_ops == 0) && "
841 "(write_ops == 0)); => Not writing.");
845 output_name = disk_name;
848 char *alt_name = NULL;
849 if (conf_udev_name_attr != NULL) {
851 disk_udev_attr_name(handle_udev, disk_name, conf_udev_name_attr);
852 if (alt_name != NULL)
853 output_name = alt_name;
857 if (ignorelist_match(ignorelist, output_name) != 0) {
859 /* release udev-based alternate name, if allocated */
865 if ((ds->read_bytes != 0) || (ds->write_bytes != 0))
866 disk_submit(output_name, "disk_octets", ds->read_bytes, ds->write_bytes);
868 if ((ds->read_ops != 0) || (ds->write_ops != 0))
869 disk_submit(output_name, "disk_ops", read_ops, write_ops);
871 if ((ds->avg_read_time != 0) || (ds->avg_write_time != 0))
872 disk_submit(output_name, "disk_time", ds->avg_read_time,
877 disk_submit(output_name, "disk_merged", read_merged, write_merged);
878 if (ds->has_in_progress)
879 submit_in_progress(output_name, in_progress);
881 submit_io_time(output_name, io_time, weighted_time);
885 /* release udev-based alternate name, if allocated */
888 } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
891 /* #endif defined(KERNEL_LINUX) */
894 #if HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_NWRITES && HAVE_KSTAT_IO_T_WTIME
895 #define KIO_ROCTETS reads
896 #define KIO_WOCTETS writes
897 #define KIO_ROPS nreads
898 #define KIO_WOPS nwrites
899 #define KIO_RTIME rtime
900 #define KIO_WTIME wtime
901 #elif HAVE_KSTAT_IO_T_NWRITTEN && HAVE_KSTAT_IO_T_WRITES && \
902 HAVE_KSTAT_IO_T_WTIME
903 #define KIO_ROCTETS nread
904 #define KIO_WOCTETS nwritten
905 #define KIO_ROPS reads
906 #define KIO_WOPS writes
907 #define KIO_RTIME rtime
908 #define KIO_WTIME wtime
910 #error "kstat_io_t does not have the required members"
912 static kstat_io_t kio;
917 for (int i = 0; i < numdisk; i++) {
918 if (kstat_read(kc, ksp[i], &kio) == -1)
921 if (strncmp(ksp[i]->ks_class, "disk", 4) == 0) {
922 if (ignorelist_match(ignorelist, ksp[i]->ks_name) != 0)
925 disk_submit(ksp[i]->ks_name, "disk_octets", kio.KIO_ROCTETS,
927 disk_submit(ksp[i]->ks_name, "disk_ops", kio.KIO_ROPS, kio.KIO_WOPS);
928 /* FIXME: Convert this to microseconds if necessary */
929 disk_submit(ksp[i]->ks_name, "disk_time", kio.KIO_RTIME, kio.KIO_WTIME);
930 } else if (strncmp(ksp[i]->ks_class, "partition", 9) == 0) {
931 if (ignorelist_match(ignorelist, ksp[i]->ks_name) != 0)
934 disk_submit(ksp[i]->ks_name, "disk_octets", kio.KIO_ROCTETS,
936 disk_submit(ksp[i]->ks_name, "disk_ops", kio.KIO_ROPS, kio.KIO_WOPS);
939 /* #endif defined(HAVE_LIBKSTAT) */
941 #elif defined(HAVE_LIBSTATGRAB)
942 sg_disk_io_stats *ds;
943 #if HAVE_LIBSTATGRAB_0_90
948 char name[DATA_MAX_NAME_LEN];
950 if ((ds = sg_get_disk_io_stats(&disks)) == NULL)
953 for (int counter = 0; counter < disks; counter++) {
954 strncpy(name, ds->disk_name, sizeof(name));
955 name[sizeof(name) - 1] =
956 '\0'; /* strncpy doesn't terminate longer strings */
958 if (ignorelist_match(ignorelist, name) != 0) {
963 disk_submit(name, "disk_octets", ds->read_bytes, ds->write_bytes);
966 /* #endif defined(HAVE_LIBSTATGRAB) */
968 #elif defined(HAVE_PERFSTAT)
969 derive_t read_sectors;
970 derive_t write_sectors;
975 perfstat_id_t firstpath;
978 if ((numdisk = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0)) < 0) {
979 WARNING("disk plugin: perfstat_disk: %s", STRERRNO);
983 if (numdisk != pnumdisk || stat_disk == NULL) {
984 if (stat_disk != NULL)
986 stat_disk = (perfstat_disk_t *)calloc(numdisk, sizeof(perfstat_disk_t));
990 firstpath.name[0] = '\0';
991 if ((rnumdisk = perfstat_disk(&firstpath, stat_disk, sizeof(perfstat_disk_t),
993 WARNING("disk plugin: perfstat_disk : %s", STRERRNO);
997 for (int i = 0; i < rnumdisk; i++) {
998 if (ignorelist_match(ignorelist, stat_disk[i].name) != 0)
1001 read_sectors = stat_disk[i].rblks * stat_disk[i].bsize;
1002 write_sectors = stat_disk[i].wblks * stat_disk[i].bsize;
1003 disk_submit(stat_disk[i].name, "disk_octets", read_sectors, write_sectors);
1005 read_ops = stat_disk[i].xrate;
1006 write_ops = stat_disk[i].xfers - stat_disk[i].xrate;
1007 disk_submit(stat_disk[i].name, "disk_ops", read_ops, write_ops);
1009 read_time = stat_disk[i].rserv;
1010 read_time *= ((double)(_system_configuration.Xint) /
1011 (double)(_system_configuration.Xfrac)) /
1013 write_time = stat_disk[i].wserv;
1014 write_time *= ((double)(_system_configuration.Xint) /
1015 (double)(_system_configuration.Xfrac)) /
1017 disk_submit(stat_disk[i].name, "disk_time", read_time, write_time);
1019 #endif /* defined(HAVE_PERFSTAT) */
1022 } /* int disk_read */
1024 void module_register(void) {
1025 plugin_register_config("disk", disk_config, config_keys, config_keys_num);
1026 plugin_register_init("disk", disk_init);
1027 plugin_register_shutdown("disk", disk_shutdown);
1028 plugin_register_read("disk", disk_read);
1029 } /* void module_register */