diff options
Diffstat (limited to 'src/pmdas/solaris/disk.c')
-rw-r--r-- | src/pmdas/solaris/disk.c | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/src/pmdas/solaris/disk.c b/src/pmdas/solaris/disk.c new file mode 100644 index 0000000..70261c2 --- /dev/null +++ b/src/pmdas/solaris/disk.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2010 Max Matveev. 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 "common.h" +#include <libdevinfo.h> + +#define SOLARIS_PMDA_TRACE (DBG_TRACE_APPL0|DBG_TRACE_APPL2) + +typedef struct { + int fetched; + int err; + kstat_t *ksp; + kstat_io_t iostat; + kstat_t *sderr; + int sderr_fresh; +} ctl_t; + +static di_devlink_handle_t devlink_hndl = DI_LINK_NIL; +static di_node_t di_root = DI_NODE_NIL; + +static ctl_t * +getDiskCtl(pmInDom dindom, const char *name) +{ + ctl_t *ctl = NULL; + int inst; + int rv = pmdaCacheLookupName(dindom,name, &inst, (void **)&ctl); + + if (rv == PMDA_CACHE_ACTIVE) + return ctl; + + if ((rv == PMDA_CACHE_INACTIVE) && ctl) { + rv = pmdaCacheStore(dindom, PMDA_CACHE_ADD, name, ctl); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot reactivate cached data for disk '%s': %s\n", + name, pmErrStr(rv)); + return NULL; + } + } else { + if ((ctl = (ctl_t *)calloc(1, sizeof(ctl_t))) == NULL) { + __pmNotifyErr(LOG_WARNING, + "Out of memory to keep state for disk '%s'\n", + name); + return NULL; + } + + rv = pmdaCacheStore(dindom, PMDA_CACHE_ADD, name, ctl); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot cache data for disk '%s': %s\n", + name, pmErrStr(rv)); + free(ctl); + return NULL; + } + } + return ctl; +} + +static void +disk_walk_chains(pmInDom dindom) +{ + kstat_t *ksp; + kstat_ctl_t *kc; + + if ((kc = kstat_ctl_update()) == NULL) + return; + + for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + ctl_t *ctl; + + if ((strcmp(ksp->ks_class, "disk") == 0) && + (ksp->ks_type == KSTAT_TYPE_IO)) { + if ((ctl = getDiskCtl(dindom, ksp->ks_name)) == NULL) + continue; + + ctl->ksp = ksp; + ctl->fetched = 0; + } else if (strcmp(ksp->ks_class, "device_error") == 0) { + char *comma; + char modname[KSTAT_STRLEN]; + + strcpy(modname, ksp->ks_name); + if ((comma = strchr(modname, ',')) == NULL) + continue; + + *comma = '\0'; + if ((ctl = getDiskCtl(dindom, modname)) == NULL) + continue; + ctl->sderr = ksp; + ctl->sderr_fresh = 0; + } + } +} + +void +disk_init(int first) +{ + pmInDom dindom = indomtab[DISK_INDOM].it_indom; + + if (!first) + /* TODO ... not sure if/when we'll use this re-init hook */ + return; + + pmdaCacheOp(dindom, PMDA_CACHE_LOAD); + disk_walk_chains(dindom); + pmdaCacheOp(dindom, PMDA_CACHE_SAVE); +} + +void +disk_prefetch(void) +{ + if (di_root != DI_NODE_NIL) { + di_fini(di_root); + di_root = DI_NODE_NIL; + } + + if (devlink_hndl != DI_LINK_NIL) { + di_devlink_fini(&devlink_hndl); + devlink_hndl = DI_LINK_NIL; + } + pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_INACTIVE); + disk_walk_chains(indomtab[DISK_INDOM].it_indom); + pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_SAVE); +} + +static __uint64_t +disk_derived(pmdaMetric *mdesc, int inst, const kstat_io_t *iostat) +{ + pmID pmid; + __pmID_int *ip = (__pmID_int *)&pmid; + __uint64_t val; + + pmid = mdesc->m_desc.pmid; + ip->domain = 0; + +// from kstat_io_t ... +// +// u_longlong_t nread; /* number of bytes read */ +// u_longlong_t nwritten; /* number of bytes written */ +// uint_t reads; /* number of read operations */ +// uint_t writes; /* number of write operations */ +// + + switch (pmid) { + case PMDA_PMID(SCLR_DISK,2): /* disk.all.total */ + case PMDA_PMID(SCLR_DISK,12): /* disk.dev.total */ + val = iostat->reads + iostat->writes; + break; + + case PMDA_PMID(SCLR_DISK,5): /* disk.all.total_bytes */ + case PMDA_PMID(SCLR_DISK,15): /* disk.dev.total_bytes */ + val = iostat->nread + iostat->nwritten; + break; + + /* iostat->wcnt and iostat->rcnt are 32 bit intergers, + * these two metrics must be derived because the metrics + * are using 64 bit integers to avoid overflows during + * accumultion */ + case PMDA_PMID(SCLR_DISK,7): /* disk.all.wait.count */ + val = iostat->wcnt; + break; + case PMDA_PMID(SCLR_DISK,9): /* disk.all.run.time */ + val = iostat->rcnt; + break; + + default: + fprintf(stderr, "disk_derived: Botch: no method for pmid %s\n", + pmIDStr(mdesc->m_desc.pmid)); + val = 0; + break; + } + +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + /* desperate */ + fprintf(stderr, "disk_derived: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, (unsigned long long)val); + } +#endif + + return val; +} + +static int +fetch_disk_data(kstat_ctl_t *kc, const pmdaMetric *mdesc, ctl_t *ctl, + const char *diskname) +{ + if (ctl->fetched == 1) + return 1; + + if (ctl->ksp == NULL) + return 0; + + if ((kstat_read(kc, ctl->ksp, &ctl->iostat) == -1)) { + if (ctl->err == 0) { + __pmNotifyErr(LOG_WARNING, + "Error: disk_fetch(pmid=%s disk=%s ...) - " + "kstat_read(kc=%p, ksp=%p, ...) failed: %s\n", + pmIDStr(mdesc->m_desc.pmid), diskname, + kc, ctl->ksp, osstrerror()); + } + ctl->err++; + ctl->fetched = -1; + return 0; + } + + ctl->fetched = 1; + if (ctl->err != 0) { + __pmNotifyErr(LOG_INFO, + "Success: disk_fetch(pmid=%s disk=%s ...) " + "after %d errors as previously reported\n", + pmIDStr(mdesc->m_desc.pmid), diskname, ctl->err); + ctl->err = 0; + } + + return 1; +} + +static int +get_devlink_path(di_devlink_t devlink, void *arg) +{ + const char **p = arg; + *p = di_devlink_path(devlink); + return DI_WALK_TERMINATE; +} + +static int +fetch_disk_devlink(const kstat_t *ksp, pmAtomValue *atom) +{ + di_node_t n; + + if (di_root == DI_NODE_NIL) { + if ((di_root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) + return 0; + } + + if (devlink_hndl == DI_LINK_NIL) { + if ((devlink_hndl = di_devlink_init(NULL, DI_MAKE_LINK)) == DI_LINK_NIL) + return 0; + } + + if ((n = di_drv_first_node(ksp->ks_module, di_root)) == DI_NODE_NIL) { +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + fprintf(stderr,"No nodes for %s: %s\n", + ksp->ks_name, osstrerror()); + } +#endif + return 0; + } + + do { + if (di_instance(n) == ksp->ks_instance) { + di_minor_t minor = di_minor_next(n, DI_MINOR_NIL); + char *path; + char *devlink = NULL; + + if (minor == DI_MINOR_NIL) { +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + fprintf (stderr, "No minors of %s: %s\n", + ksp->ks_name, osstrerror()); + } +#endif + return 0; + } + path = di_devfs_minor_path(minor); + di_devlink_walk(devlink_hndl, NULL, path, 0, &devlink, + get_devlink_path); + di_devfs_path_free(path); + + if (devlink) { + atom->cp = devlink; + return 1; + } + return 0; + } + n = di_drv_next_node(n); + } while (n != DI_NODE_NIL); + return 0; +} + +static int +get_instance_value(pmdaMetric *mdesc, pmInDom dindom, int inst, + pmAtomValue *atom) +{ + ctl_t *ctl; + char *diskname; + uint64_t ull; + ptrdiff_t offset = ((metricdesc_t *)mdesc->m_user)->md_offset; + kstat_ctl_t *kc; + + if ((kc = kstat_ctl_update()) == NULL) + return 0; + + if (pmdaCacheLookup(dindom, inst, &diskname, + (void **)&ctl) != PMDA_CACHE_ACTIVE) { +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + fprintf(stderr, + "Unexpected cache result - instance %d " + "is not active in disk indom cache\n", + inst); + } +#endif + return 0; + } + + if (offset == -1) { + if (pmid_item(mdesc->m_desc.pmid) == 35) { /* hinv.disk.devlink */ + return fetch_disk_devlink(ctl->ksp, atom); + } + if (!fetch_disk_data(kc, mdesc, ctl, diskname)) + return 0; + ull = disk_derived(mdesc, inst, &ctl->iostat); + } else if (offset > sizeof(ctl->iostat)) { /* device_error */ + if (ctl->sderr) { + kstat_named_t *kn; + char * m = (char *)offset; + + if (!ctl->sderr_fresh) { + ctl->sderr_fresh = (kstat_read(kc, ctl->sderr, NULL) != -1); + + if (!ctl->sderr_fresh) + return 0; + } + + if ((kn = kstat_data_lookup(ctl->sderr, m)) == NULL) { +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) + fprintf(stderr, "No %s in %s\n", m, diskname); +#endif + return 0; + } + + return kstat_named_to_pmAtom(kn, atom); + } + return 0; + } else { + char *iop = ((char *)&ctl->iostat) + offset; + if (!fetch_disk_data(kc, mdesc, ctl, diskname)) + return 0; + if (mdesc->m_desc.type == PM_TYPE_U64) { + __uint64_t *ullp = (__uint64_t *)iop; + ull = *ullp; +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + /* desperate */ + fprintf(stderr, "disk_fetch: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, + (unsigned long long)*ullp); + } +#endif + } + else { + __uint32_t *ulp = (__uint32_t *)iop; + ull = *ulp; +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + /* desperate */ + fprintf(stderr, "disk_fetch: pmid %s inst %d val %u\n", + pmIDStr(mdesc->m_desc.pmid), inst, *ulp); + } +#endif + } + } + + if (mdesc->m_desc.type == PM_TYPE_U64) { + /* export as 64-bit value */ + atom->ull += ull; + } + else { + /* else export as a 32-bit */ + atom->ul += (__uint32_t)ull; + } + + return 1; +} + +int +disk_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) +{ + int i; + pmInDom dindom = indomtab[DISK_INDOM].it_indom; + + if (pmid_item(mdesc->m_desc.pmid) == 20) { /* hinv.ndisk */ + i = pmdaCacheOp(dindom, PMDA_CACHE_SIZE_ACTIVE); + if (i < 0) { + return 0; + } else { + atom->ul = i; + return 1; + } + } + + memset(atom, 0, sizeof(*atom)); + + if (inst == PM_IN_NULL) { + pmdaCacheOp(dindom,PMDA_CACHE_WALK_REWIND); + while ((i = pmdaCacheOp(dindom, PMDA_CACHE_WALK_NEXT)) != -1) { + if (get_instance_value(mdesc, dindom, i, atom) == 0) + return 0; + } + return 1; + } + + return get_instance_value(mdesc, dindom, inst, atom); +} |