diff options
Diffstat (limited to 'src/pmdas/darwin/disk.c')
-rw-r--r-- | src/pmdas/darwin/disk.c | 261 |
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; +} |