summaryrefslogtreecommitdiff
path: root/usr/src/cmd/stat/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/stat/common')
-rw-r--r--usr/src/cmd/stat/common/acquire.c68
-rw-r--r--usr/src/cmd/stat/common/acquire_iodevs.c590
-rw-r--r--usr/src/cmd/stat/common/dsr.c210
-rw-r--r--usr/src/cmd/stat/common/dsr.h14
-rw-r--r--usr/src/cmd/stat/common/statcommon.h23
-rw-r--r--usr/src/cmd/stat/common/walkers.c17
6 files changed, 685 insertions, 237 deletions
diff --git a/usr/src/cmd/stat/common/acquire.c b/usr/src/cmd/stat/common/acquire.c
index e3c911283c..cf17de7c3f 100644
--- a/usr/src/cmd/stat/common/acquire.c
+++ b/usr/src/cmd/stat/common/acquire.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -34,6 +33,7 @@
#include <unistd.h>
#include <strings.h>
#include <errno.h>
+#include <limits.h>
#include <poll.h>
#define ARRAY_SIZE(a) (sizeof (a) / sizeof (*a))
@@ -345,7 +345,8 @@ retry:
if (!err && (types & SNAP_PSETS))
err = acquire_psets(ss);
- if (!err && (types & (SNAP_IODEVS | SNAP_CONTROLLERS | SNAP_IOPATHS)))
+ if (!err && (types & (SNAP_IODEVS | SNAP_CONTROLLERS |
+ SNAP_IOPATHS_LI | SNAP_IOPATHS_LTI)))
err = acquire_iodevs(ss, kc, iodev_filter);
if (!err && (types & SNAP_SYSTEM))
@@ -538,3 +539,60 @@ nr_active_cpus(struct snapshot *ss)
return (count);
}
+
+/*
+ * Return the number of ticks delta between two hrtime_t
+ * values. Attempt to cater for various kinds of overflow
+ * in hrtime_t - no matter how improbable.
+ */
+uint64_t
+hrtime_delta(hrtime_t old, hrtime_t new)
+{
+ uint64_t del;
+
+ if ((new >= old) && (old >= 0L))
+ return (new - old);
+ else {
+ /*
+ * We've overflowed the positive portion of an
+ * hrtime_t.
+ */
+ if (new < 0L) {
+ /*
+ * The new value is negative. Handle the
+ * case where the old value is positive or
+ * negative.
+ */
+ uint64_t n1;
+ uint64_t o1;
+
+ n1 = -new;
+ if (old > 0L)
+ return (n1 - old);
+ else {
+ o1 = -old;
+ del = n1 - o1;
+ return (del);
+ }
+ } else {
+ /*
+ * Either we've just gone from being negative
+ * to positive *or* the last entry was positive
+ * and the new entry is also positive but *less*
+ * than the old entry. This implies we waited
+ * quite a few days on a very fast system between
+ * iostat displays.
+ */
+ if (old < 0L) {
+ uint64_t o2;
+
+ o2 = -old;
+ del = UINT64_MAX - o2;
+ } else {
+ del = UINT64_MAX - old;
+ }
+ del += new;
+ return (del);
+ }
+ }
+}
diff --git a/usr/src/cmd/stat/common/acquire_iodevs.c b/usr/src/cmd/stat/common/acquire_iodevs.c
index be29ff88ef..e569f3fe6a 100644
--- a/usr/src/cmd/stat/common/acquire_iodevs.c
+++ b/usr/src/cmd/stat/common/acquire_iodevs.c
@@ -78,9 +78,11 @@ parent_iodev_type(enum iodev_type type)
{
switch (type) {
case IODEV_CONTROLLER: return (0);
+ case IODEV_IOPATH_LT: return (0);
+ case IODEV_IOPATH_LI: return (0);
case IODEV_NFS: return (0);
case IODEV_TAPE: return (0);
- case IODEV_IOPATH: return (IODEV_DISK);
+ case IODEV_IOPATH_LTI: return (IODEV_DISK);
case IODEV_DISK: return (IODEV_CONTROLLER);
case IODEV_PARTITION: return (IODEV_DISK);
}
@@ -221,6 +223,13 @@ disk_or_partition(enum iodev_type type)
return (type == IODEV_DISK || type == IODEV_PARTITION);
}
+static int
+disk_or_partition_or_iopath(enum iodev_type type)
+{
+ return (type == IODEV_DISK || type == IODEV_PARTITION ||
+ type == IODEV_IOPATH_LTI);
+}
+
static void
insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev)
{
@@ -238,30 +247,60 @@ insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev)
insert_into(list, iodev);
}
+/* return 1 if dev passes filter */
static int
iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df)
{
- size_t i;
- int is_floppy = (strncmp(dev->is_name, "fd", 2) == 0);
+ int is_floppy = (strncmp(dev->is_name, "fd", 2) == 0);
+ char *isn, *ispn, *ifn;
+ char *path;
+ int ifnl;
+ size_t i;
/* no filter, pass */
if (df == NULL)
- return (1);
+ return (1); /* pass */
/* no filtered names, pass if not floppy and skipped */
if (df->if_nr_names == NULL)
return (!(df->if_skip_floppy && is_floppy));
+ isn = dev->is_name;
+ ispn = dev->is_pretty;
for (i = 0; i < df->if_nr_names; i++) {
- if (strcmp(dev->is_name, df->if_names[i]) == 0)
- return (1);
- if (dev->is_pretty != NULL &&
- strcmp(dev->is_pretty, df->if_names[i]) == 0)
- return (1);
+ ifn = df->if_names[i];
+ ifnl = strlen(ifn);
+ path = strchr(ifn, '.');
+
+ if ((strcmp(isn, ifn) == 0) ||
+ (ispn && (strcmp(ispn, ifn) == 0)))
+ return (1); /* pass */
+
+ /* if filter is a path allow partial match */
+ if (path &&
+ ((strncmp(isn, ifn, ifnl) == 0) ||
+ (ispn && (strncmp(ispn, ifn, ifnl) == 0))))
+ return (1); /* pass */
}
- /* not found in specified names, fail match */
- return (0);
+ return (0); /* fail */
+}
+
+/* return 1 if path is an mpxio path associated with dev */
+static int
+iodev_path_match(struct iodev_snapshot *dev, struct iodev_snapshot *path)
+{
+ char *dn, *pn;
+ int dnl;
+
+ dn = dev->is_name;
+ pn = path->is_name;
+ dnl = strlen(dn);
+
+ if ((strncmp(pn, dn, dnl) == 0) && (pn[dnl] == '.'))
+ return (1); /* yes */
+
+ return (0); /* no */
}
/* select which I/O devices to collect stats for */
@@ -269,40 +308,72 @@ static void
choose_iodevs(struct snapshot *ss, struct iodev_snapshot *iodevs,
struct iodev_filter *df)
{
- struct iodev_snapshot *pos = iodevs;
- int nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS;
+ struct iodev_snapshot *pos, *ppos, *tmp, *ptmp;
+ int nr_iodevs;
+ int nr_iodevs_orig;
+
+ nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS;
+ nr_iodevs_orig = nr_iodevs;
if (nr_iodevs == UNLIMITED_IODEVS)
nr_iodevs = INT_MAX;
+ /* add the full matches */
+ pos = iodevs;
while (pos && nr_iodevs) {
- struct iodev_snapshot *tmp = pos;
+ tmp = pos;
pos = pos->is_next;
if (!iodev_match(tmp, df))
- continue;
+ continue; /* failed full match */
list_del(&iodevs, tmp);
insert_iodev(ss, tmp);
- --nr_iodevs;
- }
-
- pos = iodevs;
+ /*
+ * Add all mpxio paths associated with match above. Added
+ * paths don't count against nr_iodevs.
+ */
+ if (strchr(tmp->is_name, '.') == NULL) {
+ ppos = iodevs;
+ while (ppos) {
+ ptmp = ppos;
+ ppos = ppos->is_next;
+
+ if (!iodev_path_match(tmp, ptmp))
+ continue; /* not an mpxio path */
+
+ list_del(&iodevs, ptmp);
+ insert_iodev(ss, ptmp);
+ if (pos == ptmp)
+ pos = ppos;
+ }
+ }
- /* now insert any iodevs into the remaining slots */
- while (pos && nr_iodevs) {
- struct iodev_snapshot *tmp = pos;
- pos = pos->is_next;
+ nr_iodevs--;
+ }
- if (df && df->if_skip_floppy &&
- strncmp(tmp->is_name, "fd", 2) == 0)
- continue;
+ /*
+ * If we had a filter, and *nothing* passed the filter then we
+ * don't want to fill the remaining slots - it is just confusing
+ * if we don that, it makes it look like the filter code is broken.
+ */
+ if ((df->if_nr_names == NULL) || (nr_iodevs != nr_iodevs_orig)) {
+ /* now insert any iodevs into the remaining slots */
+ pos = iodevs;
+ while (pos && nr_iodevs) {
+ tmp = pos;
+ pos = pos->is_next;
+
+ if (df && df->if_skip_floppy &&
+ strncmp(tmp->is_name, "fd", 2) == 0)
+ continue;
- list_del(&iodevs, tmp);
- insert_iodev(ss, tmp);
+ list_del(&iodevs, tmp);
+ insert_iodev(ss, tmp);
- --nr_iodevs;
+ --nr_iodevs;
+ }
}
/* clear the unwanted ones */
@@ -454,7 +525,7 @@ get_ids(struct iodev_snapshot *iodev, const char *pretty)
iodev->is_id.id = disk;
(void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN);
iodev->is_parent_id.id = ctr;
- } else if (iodev->is_type == IODEV_IOPATH) {
+ } else if (iodev->is_type == IODEV_IOPATH_LTI) {
iodev->is_parent_id.id = disk;
(void) strlcpy(iodev->is_parent_id.tid,
target, KSTAT_STRLEN);
@@ -464,65 +535,12 @@ get_ids(struct iodev_snapshot *iodev, const char *pretty)
free(target);
}
-static char *
-get_slice(int partition, disk_list_t *dl)
-{
- char *tmpbuf;
- size_t tmplen;
-
- if (!(dl->flags & SLICES_OK))
- return (NULL);
- if (partition < 0 || partition >= NDKMAP)
- return (NULL);
-
- /* space for 's', and integer < NDKMAP (16) */
- tmplen = strlen(dl->dsk) + strlen("sXX") + 1;
- tmpbuf = safe_alloc(tmplen);
-
- /*
- * This is a regular slice. Create the name and
- * copy it for use by the calling routine.
- */
- (void) snprintf(tmpbuf, tmplen, "%ss%d", dl->dsk, partition);
- return (tmpbuf);
-}
-
-static char *
-get_intel_partition(int partition, disk_list_t *dl)
-{
- char *tmpbuf;
- size_t tmplen;
-
- if (partition <= 0 || !(dl->flags & PARTITIONS_OK))
- return (NULL);
-
- /*
- * See if it falls in the range of allowable partitions. The
- * fdisk partitions show up after the traditional slices so we
- * determine which partition we're in and return that.
- * The NUMPART + 1 is not a mistake. There are currently
- * FD_NUMPART + 1 partitions that show up in the device directory.
- */
- partition -= NDKMAP;
- if (partition < 0 || partition >= (FD_NUMPART + 1))
- return (NULL);
-
- /* space for 'p', and integer < NDKMAP (16) */
- tmplen = strlen(dl->dsk) + strlen("pXX") + 1;
- tmpbuf = safe_alloc(tmplen);
-
- (void) snprintf(tmpbuf, tmplen, "%sp%d", dl->dsk, partition);
- return (tmpbuf);
-}
-
static void
get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev,
kstat_ctl_t *kc)
{
- disk_list_t *dl;
- char *pretty = NULL;
- char *tmp;
- int partition;
+ disk_list_t *dl;
+ char *pretty = NULL;
if (iodev->is_type == IODEV_NFS) {
if (!(types & SNAP_IODEV_PRETTY))
@@ -532,26 +550,7 @@ get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev,
return;
}
- if (iodev->is_type == IODEV_IOPATH) {
- char buf[KSTAT_STRLEN];
- size_t len;
-
- tmp = iodev->is_name;
- while (*tmp && *tmp != '.')
- tmp++;
- if (!*tmp)
- return;
- (void) strlcpy(buf, iodev->is_name, 1 + tmp - iodev->is_name);
- dl = lookup_ks_name(buf, (types & SNAP_IODEV_DEVID) ? 1 : 0);
- if (dl == NULL || dl->dsk == NULL)
- return;
- len = strlen(dl->dsk) + strlen(tmp) + 1;
- pretty = safe_alloc(len);
- (void) strlcpy(pretty, dl->dsk, len);
- (void) strlcat(pretty, tmp, len);
- goto out;
- }
-
+ /* lookup/translate the kstat name */
dl = lookup_ks_name(iodev->is_name, (types & SNAP_IODEV_DEVID) ? 1 : 0);
if (dl == NULL)
return;
@@ -567,35 +566,13 @@ get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev,
if (dl->devidstr)
iodev->is_devid = safe_strdup(dl->devidstr);
- /* look for a possible partition number */
- tmp = iodev->is_name;
- while (*tmp && *tmp != ',')
- tmp++;
- if (*tmp != ',')
- goto out;
-
- tmp++;
- partition = (int)(*tmp - 'a');
-
- if (iodev->is_type == IODEV_PARTITION) {
- char *part;
- if ((part = get_slice(partition, dl)) == NULL)
- part = get_intel_partition(partition, dl);
- if (part != NULL) {
- free(pretty);
- pretty = part;
- }
- }
-
-out:
get_ids(iodev, pretty);
- /* only fill in the pretty name if specifically asked for */
- if (types & SNAP_IODEV_PRETTY) {
- iodev->is_pretty = pretty;
- } else {
- free(pretty);
- }
+ /*
+ * we fill in pretty name wether it is asked for or not because
+ * it could be used in a filter by match_iodevs.
+ */
+ iodev->is_pretty = pretty;
}
static enum iodev_type
@@ -608,51 +585,344 @@ get_iodev_type(kstat_t *ksp)
if (strcmp(ksp->ks_class, "nfs") == 0)
return (IODEV_NFS);
if (strcmp(ksp->ks_class, "iopath") == 0)
- return (IODEV_IOPATH);
+ return (IODEV_IOPATH_LTI);
if (strcmp(ksp->ks_class, "tape") == 0)
return (IODEV_TAPE);
return (IODEV_UNKNOWN);
}
+/* get the lun/target/initiator from the name, return 1 on success */
+static int
+get_lti(char *s,
+ char *lname, int *l, char *tname, int *t, char *iname, int *i)
+{
+ int num = 0;
+
+ num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z]%d", lname, l,
+ tname, t, iname, i);
+ return ((num == 6) ? 1 : 0);
+}
+
+
+/* get the lun, target, and initiator name and instance */
+static void
+get_path_info(struct iodev_snapshot *io, char *mod, int *type, int *inst,
+ char *name, size_t size)
+{
+
+ /*
+ * If it is iopath or ssd then pad the name with i/t/l so we can sort
+ * by alpha order and set type for IOPATH to DISK since we want to
+ * have it grouped with its ssd parent. The lun can be 5 digits,
+ * the target can be 4 digits, and the initiator can be 3 digits and
+ * the padding is done appropriately for string comparisons.
+ */
+ if (disk_or_partition_or_iopath(io->is_type)) {
+ int i1, t1, l1;
+ char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN];
+ char *ptr, lname[KSTAT_STRLEN];
+
+ i1 = t1 = l1 = 0;
+ (void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1);
+ *type = io->is_type;
+ if (io->is_type == IODEV_DISK) {
+ (void) snprintf(name, size, "%s%05d", lname, l1);
+ } else if (io->is_type == IODEV_PARTITION) {
+ ptr = strchr(io->is_name, ',');
+ (void) snprintf(name, size, "%s%05d%s", lname, l1, ptr);
+ } else {
+ (void) snprintf(name, size, "%s%05d.%s%04d.%s%03d",
+ lname, l1, tname, t1, iname, i1);
+ /* set to disk so we sort with disks */
+ *type = IODEV_DISK;
+ }
+ (void) strcpy(mod, lname);
+ *inst = l1;
+ } else {
+ (void) strcpy(mod, io->is_module);
+ (void) strcpy(name, io->is_name);
+ *type = io->is_type;
+ *inst = io->is_instance;
+ }
+}
+
int
iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2)
{
- /* neutral sort order between disk and part */
- if (!disk_or_partition(io1->is_type) ||
- !disk_or_partition(io2->is_type)) {
- if (io1->is_type < io2->is_type)
+ int type1, type2;
+ int inst1, inst2;
+ char name1[KSTAT_STRLEN], name2[KSTAT_STRLEN];
+ char mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN];
+
+ get_path_info(io1, mod1, &type1, &inst1, name1, sizeof (name1));
+ get_path_info(io2, mod2, &type2, &inst2, name2, sizeof (name2));
+ if ((!disk_or_partition(type1)) ||
+ (!disk_or_partition(type2))) {
+ /* neutral sort order between disk and part */
+ if (type1 < type2) {
return (-1);
- if (io1->is_type > io2->is_type)
+ }
+ if (type1 > type2) {
return (1);
+ }
}
/* controller doesn't have ksp */
if (io1->is_ksp && io2->is_ksp) {
- if (strcmp(io1->is_module, io2->is_module) != 0)
- return (strcmp(io1->is_module, io2->is_module));
- if (io1->is_instance < io2->is_instance)
+ if (strcmp(mod1, mod2) != 0) {
+ return (strcmp(mod1, mod2));
+ }
+ if (inst1 < inst2) {
return (-1);
- if (io1->is_instance > io2->is_instance)
+ }
+ if (inst1 > inst2) {
return (1);
+ }
} else {
- if (io1->is_id.id < io2->is_id.id)
+ if (io1->is_id.id < io2->is_id.id) {
return (-1);
- if (io1->is_id.id > io2->is_id.id)
+ }
+ if (io1->is_id.id > io2->is_id.id) {
return (1);
+ }
}
- return (strcmp(io1->is_name, io2->is_name));
+ return (strcmp(name1, name2));
+}
+
+/* update the target reads and writes */
+static void
+update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path)
+{
+ tgt->is_stats.reads += path->is_stats.reads;
+ tgt->is_stats.writes += path->is_stats.writes;
+ tgt->is_stats.nread += path->is_stats.nread;
+ tgt->is_stats.nwritten += path->is_stats.nwritten;
+ tgt->is_stats.wcnt += path->is_stats.wcnt;
+ tgt->is_stats.rcnt += path->is_stats.rcnt;
+
+ /*
+ * Stash the t_delta in the crtime for use in show_disk
+ * NOTE: this can't be done in show_disk because the
+ * itl entry is removed for the old format
+ */
+ tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime);
+ tgt->is_snaptime += path->is_snaptime;
+ tgt->is_nr_children += 1;
+}
+
+/*
+ * Create a new synthetic device entry of the specified type. The supported
+ * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI.
+ */
+static struct iodev_snapshot *
+make_extended_device(int type, struct iodev_snapshot *old)
+{
+ struct iodev_snapshot *tptr = NULL;
+ char *ptr;
+ int lun, tgt, initiator;
+ char lun_name[KSTAT_STRLEN];
+ char tgt_name[KSTAT_STRLEN];
+ char initiator_name[KSTAT_STRLEN];
+
+ if (old == NULL)
+ return (NULL);
+ if (get_lti(old->is_name,
+ lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) {
+ return (NULL);
+ }
+ tptr = safe_alloc(sizeof (*old));
+ bzero(tptr, sizeof (*old));
+ if (old->is_pretty != NULL) {
+ tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1);
+ (void) strcpy(tptr->is_pretty, old->is_pretty);
+ }
+ bcopy(&old->is_parent_id, &tptr->is_parent_id,
+ sizeof (old->is_parent_id));
+
+ tptr->is_type = type;
+
+ if (type == IODEV_IOPATH_LT) {
+ /* make new synthetic entry that is the LT */
+ /* set the id to the target id */
+ tptr->is_id.id = tgt;
+ (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
+ "%s%d", tgt_name, tgt);
+ (void) snprintf(tptr->is_name, sizeof (tptr->is_name),
+ "%s%d.%s%d", lun_name, lun, tgt_name, tgt);
+
+ if (old->is_pretty) {
+ ptr = strrchr(tptr->is_pretty, '.');
+ if (ptr)
+ *ptr = '\0';
+ }
+ } else if (type == IODEV_IOPATH_LI) {
+ /* make new synthetic entry that is the LI */
+ /* set the id to the initiator number */
+ tptr->is_id.id = initiator;
+ (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
+ "%s%d", initiator_name, initiator);
+ (void) snprintf(tptr->is_name, sizeof (tptr->is_name),
+ "%s%d.%s%d", lun_name, lun, initiator_name, initiator);
+
+ if (old->is_pretty) {
+ ptr = strchr(tptr->is_pretty, '.');
+ if (ptr)
+ (void) snprintf(ptr + 1,
+ strlen(tptr->is_pretty) + 1,
+ "%s%d", initiator_name, initiator);
+ }
+ }
+ return (tptr);
+}
+
+/*
+ * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat
+ * is found - traverse the children looking for the same initiator and sum
+ * them up. Add an LI entry and delete all of the LTI entries with the same
+ * initiator.
+ */
+static int
+create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list)
+{
+ struct iodev_snapshot *pos, *entry, *parent;
+ int lun, tgt, initiator;
+ char lun_name[KSTAT_STRLEN];
+ char tgt_name[KSTAT_STRLEN];
+ char initiator_name[KSTAT_STRLEN];
+ int err;
+
+ for (entry = list; entry; entry = entry->is_next) {
+ if ((err = create_li_delete_lti(ss, entry->is_children)) != 0)
+ return (err);
+
+ if (entry->is_type == IODEV_IOPATH_LTI) {
+ parent = find_parent(ss, entry);
+ if (get_lti(entry->is_name, lun_name, &lun,
+ tgt_name, &tgt, initiator_name, &initiator) != 1) {
+ return (1);
+ }
+
+ pos = (parent == NULL) ? NULL : parent->is_children;
+ for (; pos; pos = pos->is_next) {
+ if (pos->is_id.id != -1 &&
+ pos->is_id.id == initiator &&
+ pos->is_type == IODEV_IOPATH_LI) {
+ /* found the same initiator */
+ update_target(pos, entry);
+ list_del(&parent->is_children, entry);
+ free_iodev(entry);
+ parent->is_nr_children--;
+ entry = pos;
+ break;
+ }
+ }
+
+ if (!pos) {
+ /* make the first LI entry */
+ pos = make_extended_device(
+ IODEV_IOPATH_LI, entry);
+ update_target(pos, entry);
+
+ if (parent) {
+ insert_before(&parent->is_children,
+ entry, pos);
+ list_del(&parent->is_children, entry);
+ free_iodev(entry);
+ } else {
+ insert_before(&ss->s_iodevs, entry,
+ pos);
+ list_del(&ss->s_iodevs, entry);
+ free_iodev(entry);
+ }
+ entry = pos;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * We have the LTI kstat, now add an entry for the LT that sums up all of
+ * the LTI's with the same target(t).
+ */
+static int
+create_lt(struct snapshot *ss, struct iodev_snapshot *list)
+{
+ struct iodev_snapshot *entry, *parent, *pos;
+ int lun, tgt, initiator;
+ char lun_name[KSTAT_STRLEN];
+ char tgt_name[KSTAT_STRLEN];
+ char initiator_name[KSTAT_STRLEN];
+ int err;
+
+ for (entry = list; entry; entry = entry->is_next) {
+ if ((err = create_lt(ss, entry->is_children)) != 0)
+ return (err);
+
+ if (entry->is_type == IODEV_IOPATH_LTI) {
+ parent = find_parent(ss, entry);
+ if (get_lti(entry->is_name, lun_name, &lun,
+ tgt_name, &tgt, initiator_name, &initiator) != 1) {
+ return (1);
+ }
+
+ pos = (parent == NULL) ? NULL : parent->is_children;
+ for (; pos; pos = pos->is_next) {
+ if (pos->is_id.id != -1 &&
+ pos->is_id.id == tgt &&
+ pos->is_type == IODEV_IOPATH_LT) {
+ /* found the same target */
+ update_target(pos, entry);
+ break;
+ }
+ }
+
+ if (!pos) {
+ pos = make_extended_device(
+ IODEV_IOPATH_LT, entry);
+ update_target(pos, entry);
+
+ if (parent) {
+ insert_before(&parent->is_children,
+ entry, pos);
+ parent->is_nr_children++;
+ } else {
+ insert_before(&ss->s_iodevs,
+ entry, pos);
+ }
+ }
+ }
+ }
+ return (0);
+}
+
+/* Find the longest is_name field to aid formatting of output */
+static int
+iodevs_is_name_maxlen(struct iodev_snapshot *list)
+{
+ struct iodev_snapshot *entry;
+ int max = 0, cmax, len;
+
+ for (entry = list; entry; entry = entry->is_next) {
+ cmax = iodevs_is_name_maxlen(entry->is_children);
+ max = (cmax > max) ? cmax : max;
+ len = strlen(entry->is_name);
+ max = (len > max) ? len : max;
+ }
+ return (max);
}
int
acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df)
{
- kstat_t *ksp;
- int err = 0;
- struct iodev_snapshot *pos;
- struct iodev_snapshot *list = NULL;
+ kstat_t *ksp;
+ struct iodev_snapshot *pos;
+ struct iodev_snapshot *list = NULL;
+ int err = 0;
ss->s_nr_iodevs = 0;
+ ss->s_iodevs_is_name_maxlen = 0;
/*
* Call cleanup_iodevs_snapshot() so that a cache miss in
@@ -710,6 +980,28 @@ acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df)
if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0)
goto out;
+ if (ss->s_types & SNAP_IOPATHS_LTI) {
+ /*
+ * -Y: kstats are LTI, need to create a synthetic LT
+ * for -Y output.
+ */
+ if ((err = create_lt(ss, ss->s_iodevs)) != 0) {
+ return (err);
+ }
+ }
+ if (ss->s_types & SNAP_IOPATHS_LI) {
+ /*
+ * -X: kstats are LTI, need to create a synthetic LI and
+ * delete the LTI for -X output
+ */
+ if ((err = create_li_delete_lti(ss, ss->s_iodevs)) != 0) {
+ return (err);
+ }
+ }
+
+ /* determine width of longest is_name */
+ ss->s_iodevs_is_name_maxlen = iodevs_is_name_maxlen(ss->s_iodevs);
+
err = 0;
out:
return (err);
diff --git a/usr/src/cmd/stat/common/dsr.c b/usr/src/cmd/stat/common/dsr.c
index b25637a73b..5d6f51f835 100644
--- a/usr/src/cmd/stat/common/dsr.c
+++ b/usr/src/cmd/stat/common/dsr.c
@@ -49,30 +49,38 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <devid.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
#include "dsr.h"
#include "statcommon.h"
-/* disk/tape info */
-static di_node_t di_root; /* for devid */
-static di_dim_t di_dim; /* for /dev names */
+/* where we get kstat name translation information from */
+static di_node_t di_root; /* from di_init: for devid */
+static di_dim_t di_dim; /* from di_dim_init: for /dev names */
+static int scsi_vhci_fd = -1; /* from scsi_vhci: for mpxio path */
+
+/* disk/tape/misc info */
typedef struct {
char *minor_name;
int minor_isdisk;
} minor_match_t;
static minor_match_t mm_disk = {"a", 1};
static minor_match_t mm_tape = {"", 0};
+static minor_match_t mm_misc = {"0", 0};
static char md_minor_name[MAXPATHLEN];
static minor_match_t mm_md = {md_minor_name, 0};
-static minor_match_t *mma_disk_tape[] = {&mm_disk, &mm_tape, NULL};
+static minor_match_t *mma_disk_tape_misc[] =
+ {&mm_disk, &mm_tape, &mm_misc, NULL};
static minor_match_t *mma_md[] = {&mm_md, NULL};
static char *mdsetno2name(int setno);
#define DISKLIST_MOD 256 /* ^2 instunit mod hash */
static disk_list_t *disklist[DISKLIST_MOD];
+
/* nfs info */
extern kstat_ctl_t *kc;
extern mnt_t *nfs;
@@ -108,8 +116,8 @@ cleanup_iodevs_snapshot()
* name is the same as public name: the caller will just use kstat name.
*/
static int
-drvinstunit2dev(char *driver, int instunit,
- char **devpathp, char **adevpathp, char **devidp, int *isdiskp)
+drvinstunitpart2dev(char *driver, int instunit, char *part,
+ char **devpathp, char **adevpathp, char **devidp)
{
int instance;
minor_match_t **mma;
@@ -123,7 +131,6 @@ drvinstunit2dev(char *driver, int instunit,
char *devicespath;
di_node_t node;
-
/* setup "no result" return values */
if (devpathp)
*devpathp = NULL;
@@ -131,8 +138,6 @@ drvinstunit2dev(char *driver, int instunit,
*adevpathp = NULL;
if (devidp)
*devidp = NULL;
- if (isdiskp)
- *isdiskp = 0;
/* take <driver><instance><minor_name> snapshot if not established */
if (di_dim == NULL) {
@@ -173,14 +178,18 @@ drvinstunit2dev(char *driver, int instunit,
"%d,%d,blk", mdsetno, instunit);
} else {
instance = instunit;
- mma = mma_disk_tape; /* disk/tape minors */
+ mma = mma_disk_tape_misc; /* disk/tape/misc minors */
}
- /* Try to find a minor_match that works */
- for (mm = *mma++; mm; mm = *mma++) {
- if ((devpath = di_dim_path_dev(di_dim,
- driver, instance, mm->minor_name)) != NULL)
- break;
+ if (part) {
+ devpath = di_dim_path_dev(di_dim, driver, instance, part);
+ } else {
+ /* Try to find a minor_match that works */
+ for (mm = *mma++; mm; mm = *mma++) {
+ if ((devpath = di_dim_path_dev(di_dim,
+ driver, instance, mm->minor_name)) != NULL)
+ break;
+ }
}
if (devpath == NULL)
return (0);
@@ -193,10 +202,11 @@ drvinstunit2dev(char *driver, int instunit,
*devpathp = safe_strdup(devpath);
if (adevpathp) { /* abbreviated devpath */
- if (mm->minor_isdisk) {
+ if ((part == NULL) && mm->minor_isdisk) {
/*
- * For disks we return the last component (with
- * trailing "s#" or "p#" stripped off for disks).
+ * For disk kstats without a partition we return the
+ * last component with trailing "s#" or "p#" stripped
+ * off (i.e. partition/slice information is removed).
* For example for devpath of "/dev/dsk/c0t0d0s0" the
* abbreviated devpath would be "c0t0d0".
*/
@@ -249,7 +259,7 @@ drvinstunit2dev(char *driver, int instunit,
}
if (devidp) { /* lookup the devid */
- /* take snapshots if not established */
+ /* take snapshot if not established */
if (di_root == DI_NODE_NIL) {
di_root = di_init("/", DINFOCACHE);
}
@@ -270,17 +280,58 @@ drvinstunit2dev(char *driver, int instunit,
}
}
- if (isdiskp)
- *isdiskp = mm->minor_isdisk;
-
free(devpath);
return (1); /* success */
}
/*
- * Find/create a disk_list entry for "<driver><instunit>" given a kstat name.
- * The basic format of a kstat name is "<driver><instunit>,<partition>". The
- * <instunit> is a base10 number, and the ",<partition>" part is optional.
+ * Do <pid> to 'target-port' translation
+ */
+static int
+drvpid2port(uint_t pid, char **target_portp)
+{
+ sv_iocdata_t ioc;
+ char target_port[MAXNAMELEN];
+
+ /* setup "no result" return values */
+ *target_portp = NULL;
+
+ /* open scsi_vhci if not already done */
+ if (scsi_vhci_fd == -1) {
+ scsi_vhci_fd = open("/devices/scsi_vhci:devctl", O_RDONLY);
+ if (scsi_vhci_fd == -1)
+ return (0); /* failure */
+ }
+
+ /*
+ * Perform ioctl for <pid> -> 'target-port' translation.
+ *
+ * NOTE: it is legimite for this ioctl to fail for transports
+ * that use mpxio, but don't set a 'target-port' pathinfo property.
+ * On failure we return the the "<pid>" as the target port string.
+ */
+ bzero(&ioc, sizeof (sv_iocdata_t));
+ ioc.buf_elem = pid;
+ ioc.addr = target_port;
+ if (ioctl(scsi_vhci_fd, SCSI_VHCI_GET_TARGET_LONGNAME, &ioc) < 0) {
+ (void) snprintf(target_port, sizeof (target_port), "%d", pid);
+ }
+
+ *target_portp = safe_strdup(target_port);
+ return (1); /* success */
+}
+
+/*
+ * Find/create a disk_list entry for given a kstat name.
+ * The basic format of a kstat name is
+ *
+ * "<driver><instunit>.<pid>.<phci-driver><instance>,<partition>".
+ *
+ * The <instunit> is a decimal number. The ".<pid>.<phci-driver><instance>",
+ * which describes mpxio path stat information, and ",<partition>" parts are
+ * optional. The <pid> consists of the letter 't' followed by a decimal number.
+ * When available, we use the <pid> to find the 'target-port' via ioctls to
+ * the scsi_vhci driver.
*
* NOTE: In the case of non-local metadevices, the format of "<driver>" in
* a kstat name is acutally "<setno>/md".
@@ -288,75 +339,122 @@ drvinstunit2dev(char *driver, int instunit,
disk_list_t *
lookup_ks_name(char *ks_name, int want_devid)
{
+ char *pidp; /* ".<pid>... */
+ char *part; /* ",partition... */
+ char *initiator; /* ".<phci-driver>... */
char *p;
int len;
- char driver[MAXNAMELEN];
+ char driver[KSTAT_STRLEN];
int instunit;
disk_list_t **dlhp; /* disklist head */
disk_list_t *entry;
- char *devpath;
+ char *devpath = NULL;
char *adevpath = NULL;
char *devid = NULL;
- int isdisk;
+ int pid;
+ char *target_port = NULL;
+ char portform[MAXPATHLEN];
- /*
- * Extract <driver> and <instunit> from kstat name.
- * Filter out illegal forms (like all digits).
- */
+ /* Filter out illegal forms (like all digits). */
if ((ks_name == NULL) || (*ks_name == 0) ||
(strspn(ks_name, "0123456789") == strlen(ks_name)))
- return (NULL);
- p = strrchr(ks_name, ','); /* start of ",partition" */
+ goto fail;
+
+ /* parse ks_name to create new entry */
+ pidp = strchr(ks_name, '.'); /* start of ".<pid>" */
+ initiator = strrchr(ks_name, '.'); /* start of ".<pHCI-driver>" */
+ if (pidp && (pidp == initiator)) /* can't have same start */
+ goto fail;
+
+ part = strchr(ks_name, ','); /* start of ",<partition>" */
+ p = strchr(ks_name, ':'); /* start of ":<partition>" */
+ if (part && p)
+ goto fail; /* can't have both */
+ if (p)
+ part = p;
+ if (part && pidp)
+ goto fail; /* <pid> and partition: bad */
+
+ p = part ? part : pidp;
if (p == NULL)
p = &ks_name[strlen(ks_name) - 1]; /* last char */
else
- p--; /* before ",partition" */
+ p--; /* before ',' or '.' */
while ((p >= ks_name) && isdigit(*p))
p--; /* backwards over digits */
p++; /* start of instunit */
- if ((*p == '\0') || (*p == ','))
- return (NULL); /* no <instunit> */
+ if ((*p == '\0') || (*p == ',') || (*p == '.') || (*p == ':'))
+ goto fail; /* no <instunit> */
len = p - ks_name;
(void) strncpy(driver, ks_name, len);
driver[len] = '\0';
instunit = atoi(p);
+ if (part)
+ part++; /* skip ',' */
- /* hash and search for existing disklist entry */
+ /* hash by instunit and search for existing entry */
dlhp = &disklist[instunit & (DISKLIST_MOD - 1)];
for (entry = *dlhp; entry; entry = entry->next) {
- if ((strcmp(entry->dtype, driver) == 0) &&
- (entry->dnum == instunit)) {
+ if (strcmp(entry->ks_name, ks_name) == 0) {
return (entry);
}
}
- /* not found, try to get dev information */
- if (drvinstunit2dev(driver, instunit, &devpath, &adevpath,
- want_devid ? &devid : NULL, &isdisk) == 0) {
- return (NULL);
+ /* not found, translate kstat_name components and create new entry */
+
+ /* translate kstat_name dev information */
+ if (drvinstunitpart2dev(driver, instunit, part,
+ &devpath, &adevpath, want_devid ? &devid : NULL) == 0) {
+ goto fail;
}
- /* and make a new disklist entry ... */
+ /* parse and translate path information */
+ if (pidp) {
+ /* parse path information: ".t#.<phci-driver><instance>" */
+ pidp++; /* skip '.' */
+ initiator++; /* skip '.' */
+ if ((*pidp != 't') || !isdigit(pidp[1]))
+ goto fail; /* not ".t#" */
+ pid = atoi(&pidp[1]);
+
+ /* translate <pid> to 'target-port' */
+ if (drvpid2port(pid, &target_port) == 0)
+ goto fail;
+
+ /* Establish 'target-port' form. */
+ (void) snprintf(portform, sizeof (portform),
+ "%s.t%s.%s", adevpath, target_port, initiator);
+ free(target_port);
+ free(adevpath);
+ adevpath = strdup(portform);
+ }
+
+ /* make a new entry ... */
entry = safe_alloc(sizeof (disk_list_t));
- entry->dtype = safe_strdup(driver);
- entry->dnum = instunit;
+ entry->ks_name = safe_strdup(ks_name);
entry->dname = devpath;
entry->dsk = adevpath;
entry->devidstr = devid;
- entry->flags = 0;
- if (isdisk) {
- entry->flags |= SLICES_OK;
-#if defined(__i386)
- entry->flags |= PARTITIONS_OK;
-#endif
- }
- entry->seen = 0;
- /* add new entry to head of instunit hashed list */
+#ifdef DEBUG
+ (void) printf("lookup_ks_name: new: %s %s\n",
+ ks_name, entry->dsk ? entry->dsk : "NULL");
+#endif /* DEBUG */
+
+ /* add new entry to head of hashed list */
entry->next = *dlhp;
*dlhp = entry;
return (entry);
+
+fail:
+ free(devpath);
+ free(adevpath);
+ free(devid);
+#ifdef DEBUG
+ (void) printf("lookup_ks_name: failed: %s\n", ks_name);
+#endif /* DEBUG */
+ return (NULL);
}
/*
diff --git a/usr/src/cmd/stat/common/dsr.h b/usr/src/cmd/stat/common/dsr.h
index 96757e68a8..d91244dfd5 100644
--- a/usr/src/cmd/stat/common/dsr.h
+++ b/usr/src/cmd/stat/common/dsr.h
@@ -36,13 +36,10 @@ extern "C" {
* Description of each device identified
*/
typedef struct list_of_disks {
- char *dtype; /* device type: sd, ssd, md, st, etc. */
- int dnum; /* device number */
+ char *ks_name; /* untranslated kstat name */
char *dsk; /* in form of cNtNdN */
char *dname; /* in form of /dev/dsk/cNtNdN */
char *devidstr; /* in form of "id1,sd@XXXX" */
- uint_t flags; /* see SLICES_OK and PARTITIONS_OK above */
- uint_t seen; /* Used for diffing disk lists */
struct list_of_disks *next; /* link to next one */
} disk_list_t;
@@ -57,15 +54,6 @@ typedef struct mnt_info {
struct mnt_info *next;
} mnt_t;
-/*
- * The following are used to control treatment of kstat names
- * which fall beyond the number of disk partitions allowed on
- * the particular ISA. PARTITIONS_OK is set only on an Intel
- * system.
- */
-#define SLICES_OK 1
-#define PARTITIONS_OK 2
-
void do_mnttab(void);
mnt_t *lookup_mntent_byname(char *);
disk_list_t *lookup_ks_name(char *, int);
diff --git a/usr/src/cmd/stat/common/statcommon.h b/usr/src/cmd/stat/common/statcommon.h
index 674148da6b..848f10ebb8 100644
--- a/usr/src/cmd/stat/common/statcommon.h
+++ b/usr/src/cmd/stat/common/statcommon.h
@@ -74,14 +74,16 @@ enum snapshot_types {
SNAP_IODEVS = 1 << 5,
/* disk controller aggregates */
SNAP_CONTROLLERS = 1 << 6,
- /* mpxio (multipath) paths */
- SNAP_IOPATHS = 1 << 7,
+ /* mpxio L I (multipath) paths: -X: Lun,LunInitiator */
+ SNAP_IOPATHS_LI = 1 << 7,
+ /* mpxio LTI (multipath) paths: -Y: Lun,LunTarget,LunTargetInitiator */
+ SNAP_IOPATHS_LTI = 1 << 8,
/* disk error stats */
- SNAP_IODEV_ERRORS = 1 << 8,
+ SNAP_IODEV_ERRORS = 1 << 9,
/* pretty names for iodevs */
- SNAP_IODEV_PRETTY = 1 << 9,
+ SNAP_IODEV_PRETTY = 1 << 10,
/* devid for iodevs */
- SNAP_IODEV_DEVID = 1 << 10
+ SNAP_IODEV_DEVID = 1 << 11
};
struct cpu_snapshot {
@@ -131,8 +133,10 @@ enum iodev_type {
IODEV_PARTITION = 1 << 2,
IODEV_TAPE = 1 << 3,
IODEV_NFS = 1 << 4,
- IODEV_IOPATH = 1 << 5,
- IODEV_UNKNOWN = 1 << 6
+ IODEV_IOPATH_LT = 1 << 5, /* synthetic LunTarget */
+ IODEV_IOPATH_LI = 1 << 6, /* synthetic LunInitiator */
+ IODEV_IOPATH_LTI = 1 << 7, /* LunTgtInitiator (pathinfo) */
+ IODEV_UNKNOWN = 1 << 8
};
/* identify a disk, partition, etc. */
@@ -152,6 +156,7 @@ struct iodev_id {
struct iodev_snapshot {
/* original kstat name */
char is_name[KSTAT_STRLEN];
+ /* type of kstat */
enum iodev_type is_type;
/* ID if meaningful */
struct iodev_id is_id;
@@ -210,6 +215,7 @@ struct snapshot {
struct intr_snapshot *s_intrs;
size_t s_nr_iodevs;
struct iodev_snapshot *s_iodevs;
+ size_t s_iodevs_is_name_maxlen;
struct sys_snapshot s_sys;
struct biostats s_biostats;
struct flushmeter s_flushes;
@@ -237,6 +243,9 @@ int kstat_copy(const kstat_t *src, kstat_t *dst);
*/
uint64_t kstat_delta(kstat_t *old, kstat_t *new, char *name);
+/* Return the number of ticks delta between two hrtime_t values. */
+uint64_t hrtime_delta(hrtime_t old, hrtime_t new);
+
/*
* Add the integer-valued stats from "src" to the
* existing ones in "dst". If "dst" does not contain
diff --git a/usr/src/cmd/stat/common/walkers.c b/usr/src/cmd/stat/common/walkers.c
index c01a52be6e..adf6d6eb6f 100644
--- a/usr/src/cmd/stat/common/walkers.c
+++ b/usr/src/cmd/stat/common/walkers.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -169,7 +168,8 @@ snapshot_walk(enum snapshot_types type, struct snapshot *old,
case SNAP_CONTROLLERS:
case SNAP_IODEVS:
- case SNAP_IOPATHS:
+ case SNAP_IOPATHS_LI:
+ case SNAP_IOPATHS_LTI:
changed = iodev_walk(old ? old->s_iodevs : NULL,
new->s_iodevs, cb, data);
break;
@@ -266,7 +266,9 @@ iodev_changed(struct iodev_snapshot *iodev, int added)
name = iodev->is_pretty;
switch (iodev->is_type) {
- case IODEV_IOPATH:
+ case IODEV_IOPATH_LT:
+ case IODEV_IOPATH_LI:
+ case IODEV_IOPATH_LTI:
(void) printf("<<multi-path %s: %s>>\n",
added ? "added" : "removed", name);
break;
@@ -372,7 +374,8 @@ snapshot_has_changed(struct snapshot *old, struct snapshot *new)
{
int ret = 0;
int cpu_mask = SNAP_CPUS | SNAP_PSETS | SNAP_SYSTEM;
- int iodev_mask = SNAP_CONTROLLERS | SNAP_IODEVS | SNAP_IOPATHS;
+ int iodev_mask = SNAP_CONTROLLERS | SNAP_IODEVS |
+ SNAP_IOPATHS_LI | SNAP_IOPATHS_LTI;
if (old == NULL)
return (1);