summaryrefslogtreecommitdiff
path: root/src/pmdas/darwin/disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/darwin/disk.c')
-rw-r--r--src/pmdas/darwin/disk.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/src/pmdas/darwin/disk.c b/src/pmdas/darwin/disk.c
new file mode 100644
index 0000000..5d175b3
--- /dev/null
+++ b/src/pmdas/darwin/disk.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <mach/mach.h>
+#define IOKIT 1
+#include <device/device_types.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOBlockStorageDriver.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/IOBSD.h>
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+
+#include "disk.h"
+
+/*
+ * Ensure we have space for the next device in our pre-allocated
+ * device pool. If not, make some or pass on the error.
+ */
+static int
+check_stats_size(struct diskstats *stats, int count)
+{
+ if (count > stats->highwater) {
+ stats->highwater++;
+ stats->disks = realloc(stats->disks,
+ stats->highwater * sizeof(struct diskstat));
+ if (!stats->disks) {
+ stats->highwater = 0;
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Insert all disks into the global disk instance domain.
+ */
+static int
+update_disk_indom(struct diskstats *all, int count, pmdaIndom *indom)
+{
+ int i;
+
+ if (count > 0 && count != indom->it_numinst) {
+ i = sizeof(pmdaInstid) * count;
+ if ((indom->it_set = realloc(indom->it_set, i)) == NULL) {
+ indom->it_numinst = 0;
+ return -ENOMEM;
+ }
+ }
+ for (i = 0; i < count; i++) {
+ indom->it_set[i].i_name = all->disks[i].name;
+ indom->it_set[i].i_inst = i;
+ }
+ indom->it_numinst = count;
+ return 0;
+}
+
+/*
+ * Update the global counters with values from one disk.
+ */
+static void
+update_disk_totals(struct diskstats *all, struct diskstat *disk)
+{
+ all->read += disk->read;
+ all->write += disk->write;
+ all->read_bytes += disk->read_bytes;
+ all->write_bytes += disk->write_bytes;
+ all->blkread += disk->read_bytes / disk->blocksize;
+ all->blkwrite += disk->write_bytes / disk->blocksize;
+ all->read_time += disk->read_time;
+ all->write_time += disk->write_time;
+}
+
+static void
+clear_disk_totals(struct diskstats *all)
+{
+ all->read = 0;
+ all->write = 0;
+ all->read_bytes = 0;
+ all->write_bytes = 0;
+ all->blkread = 0;
+ all->blkwrite = 0;
+ all->read_time = 0;
+ all->write_time = 0;
+}
+
+/*
+ * Update the counters associated with a single disk.
+ */
+static void
+update_disk_stats(struct diskstat *disk,
+ CFDictionaryRef pproperties, CFDictionaryRef properties)
+{
+ CFDictionaryRef statistics;
+ CFStringRef name;
+ CFNumberRef number;
+
+ memset(disk, 0, sizeof(struct diskstat));
+
+ /* Get name from the drive properties */
+ name = (CFStringRef) CFDictionaryGetValue(pproperties,
+ CFSTR(kIOBSDNameKey));
+ if(name == NULL)
+ return; /* Not much we can do with no name */
+
+ CFStringGetCString(name, disk->name, DEVNAMEMAX,
+ CFStringGetSystemEncoding());
+
+ /* Get the blocksize from the drive properties */
+ number = (CFNumberRef) CFDictionaryGetValue(pproperties,
+ CFSTR(kIOMediaPreferredBlockSizeKey));
+ if(number == NULL)
+ return; /* Not much we can do with no number */
+ CFNumberGetValue(number, kCFNumberSInt64Type, &disk->blocksize);
+
+ /* Get the statistics from the device properties. */
+ statistics = (CFDictionaryRef) CFDictionaryGetValue(properties,
+ CFSTR(kIOBlockStorageDriverStatisticsKey));
+ if (statistics) {
+ number = (CFNumberRef) CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsReadsKey));
+ if (number)
+ CFNumberGetValue(number, kCFNumberSInt64Type,
+ &disk->read);
+ number = (CFNumberRef) CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsWritesKey));
+ if (number)
+ CFNumberGetValue(number, kCFNumberSInt64Type,
+ &disk->write);
+ number = (CFNumberRef) CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey));
+ if (number)
+ CFNumberGetValue(number, kCFNumberSInt64Type,
+ &disk->read_bytes);
+ number = (CFNumberRef) CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey));
+ if (number)
+ CFNumberGetValue(number, kCFNumberSInt64Type,
+ &disk->write_bytes);
+ number = (CFNumberRef) CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey));
+ if (number)
+ CFNumberGetValue(number, kCFNumberSInt64Type,
+ &disk->read_time);
+ number = (CFNumberRef) CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey));
+ if (number)
+ CFNumberGetValue(number, kCFNumberSInt64Type,
+ &disk->write_time);
+ }
+}
+
+static int
+update_disk(diskstats_t *stats, io_registry_entry_t drive, int index)
+{
+ io_registry_entry_t device;
+ CFDictionaryRef pproperties, properties;
+ int status;
+
+ /* Get the drives parent, from which we get statistics. */
+ status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &device);
+ if (status != KERN_SUCCESS)
+ return -oserror();
+
+ if (!IOObjectConformsTo(device, "IOBlockStorageDriver")) {
+ IOObjectRelease(device);
+ return 0;
+ }
+
+ /* Obtain the drive properties. */
+ pproperties = 0;
+ status = IORegistryEntryCreateCFProperties(drive,
+ (CFMutableDictionaryRef *)&pproperties,
+ kCFAllocatorDefault, kNilOptions);
+ if (status != KERN_SUCCESS) {
+ IOObjectRelease(device);
+ return -oserror();
+ }
+
+ /* Obtain the device properties. */
+ properties = 0;
+ status = IORegistryEntryCreateCFProperties(device,
+ (CFMutableDictionaryRef *)&properties,
+ kCFAllocatorDefault, kNilOptions);
+ if (status != KERN_SUCCESS) {
+ IOObjectRelease(device);
+ return -oserror();
+ }
+
+ /* Make space to store the actual values, then go get them. */
+ status = check_stats_size(stats, index + 1);
+ if (status < 0) {
+ IOObjectRelease(device);
+ } else {
+ update_disk_stats(&stats->disks[index], pproperties, properties);
+ update_disk_totals(stats, &stats->disks[index]);
+ }
+ CFRelease(pproperties);
+ CFRelease(properties);
+ return status;
+}
+
+int
+refresh_disks(struct diskstats *stats, pmdaIndom *indom)
+{
+ io_registry_entry_t drive;
+ CFMutableDictionaryRef match;
+ int i, status;
+ static int inited = 0;
+ static mach_port_t mach_master_port;
+ static io_iterator_t mach_device_list;
+
+ if (!inited) {
+ /* Get ports and services for device statistics. */
+ if (IOMasterPort(bootstrap_port, &mach_master_port)) {
+ fprintf(stderr, "%s: IOMasterPort error\n", __FUNCTION__);
+ return -oserror();
+ }
+ memset(stats, 0, sizeof(struct diskstats));
+ inited = 1;
+ }
+
+ /* Get an interator for IOMedia objects (drives). */
+ match = IOServiceMatching("IOMedia");
+ CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
+ status = IOServiceGetMatchingServices(mach_master_port,
+ match, &mach_device_list);
+ if (status != KERN_SUCCESS) {
+ fprintf(stderr, "%s: IOServiceGetMatchingServices error\n",
+ __FUNCTION__);
+ return -oserror();
+ }
+
+ indom->it_numinst = 0;
+ clear_disk_totals(stats);
+ for (i = 0; (drive = IOIteratorNext(mach_device_list)) != 0; i++) {
+ status = update_disk(stats, drive, i);
+ if (status)
+ break;
+ IOObjectRelease(drive);
+ }
+ IOIteratorReset(mach_device_list);
+
+ if (!status)
+ status = update_disk_indom(stats, i, indom);
+ return status;
+}