summaryrefslogtreecommitdiff
path: root/src/pmdas/solaris/disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/solaris/disk.c')
-rw-r--r--src/pmdas/solaris/disk.c420
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);
+}