summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcth <none@none>2006-10-12 10:18:45 -0700
committercth <none@none>2006-10-12 10:18:45 -0700
commit37fbbce5257519d600faa3d23d464b42b71c1605 (patch)
treec0f2a50b50b97964740ab5a7586f74bd87560678
parent2bc4236aac4fe3a3ba80139bc39ee9f1e356ab55 (diff)
downloadillumos-gate-37fbbce5257519d600faa3d23d464b42b71c1605.tar.gz
PSARC 2005/574 MPxIO iostat improvements
4261677 iostat -x shows extra output 6316660 device name gets truncated after 9 chars with iostat -xX option. 6318308 extend support in mpxio and iostat to show I/T/L based path stats
-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
-rw-r--r--usr/src/cmd/stat/iostat/iostat.c239
-rw-r--r--usr/src/uts/common/os/sunmdi.c105
-rw-r--r--usr/src/uts/common/sys/mdi_impldefs.h7
-rw-r--r--usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h2
10 files changed, 838 insertions, 437 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);
diff --git a/usr/src/cmd/stat/iostat/iostat.c b/usr/src/cmd/stat/iostat/iostat.c
index 1199870792..c26080eed9 100644
--- a/usr/src/cmd/stat/iostat/iostat.c
+++ b/usr/src/cmd/stat/iostat/iostat.c
@@ -54,12 +54,13 @@
#define DISK_EXTENDED 0x0004
#define DISK_ERRORS 0x0008
#define DISK_EXTENDED_ERRORS 0x0010
-#define DISK_IOPATH 0x0020
-#define DISK_NORMAL (DISK_OLD | DISK_NEW)
+#define DISK_IOPATH_LI 0x0020 /* LunInitiator */
+#define DISK_IOPATH_LTI 0x0040 /* LunTargetInitiator */
+#define DISK_NORMAL (DISK_OLD | DISK_NEW)
#define DISK_IO_MASK (DISK_OLD | DISK_NEW | DISK_EXTENDED)
#define DISK_ERROR_MASK (DISK_ERRORS | DISK_EXTENDED_ERRORS)
-#define PRINT_VERTICAL (DISK_ERROR_MASK | DISK_EXTENDED | DISK_IOPATH)
+#define PRINT_VERTICAL (DISK_ERROR_MASK | DISK_EXTENDED)
#define REPRINT 19
@@ -114,7 +115,7 @@ static uint_t hdr_out;
*/
static uint_t do_tty; /* show tty info (-t) */
static uint_t do_disk; /* show disk info per selected */
- /* format (-d, -D, -e, -E, -x -X) */
+ /* format (-d, -D, -e, -E, -x -X -Y) */
static uint_t do_cpu; /* show cpu info (-c) */
static uint_t do_interval; /* do intervals (-I) */
static int do_partitions; /* per-partition stats (-p) */
@@ -146,8 +147,11 @@ static uint_t show_mountpts; /* show mount points */
static int interval; /* interval (seconds) to output */
static int iter; /* iterations from command line */
-#define SMALL_SCRATCH_BUFLEN 64
-#define DISPLAYED_NAME_FORMAT "%-9.9s"
+#define SMALL_SCRATCH_BUFLEN MAXNAMELEN
+
+static int iodevs_nl; /* name field width */
+#define IODEVS_NL_MIN 6 /* not too thin for "device" */
+#define IODEVS_NL_MAX 24 /* but keep full width under 80 */
static char disk_header[132];
static uint_t dh_len; /* disk header length for centering */
@@ -155,8 +159,8 @@ static int lineout; /* data waiting to be printed? */
static struct snapshot *newss;
static struct snapshot *oldss;
-static double getime; /* elapsed time */
-static double percent; /* 100 / etime */
+static double getime; /* elapsed time */
+static double percent; /* 100 / etime */
/*
* List of functions to be called which will construct the desired output
@@ -164,7 +168,6 @@ static double percent; /* 100 / etime */
static format_t *formatter_list;
static format_t *formatter_end;
-static uint64_t hrtime_delta(hrtime_t, hrtime_t);
static u_longlong_t ull_delta(u_longlong_t, u_longlong_t);
static uint_t u32_delta(uint_t, uint_t);
static void setup(void (*nfunc)(void));
@@ -205,7 +208,6 @@ main(int argc, char **argv)
int iiter;
do_args(argc, argv);
- do_format();
/*
* iostat historically showed CPU changes, even though
@@ -218,9 +220,13 @@ main(int argc, char **argv)
if (do_disk && !do_partitions_only)
df.if_allowed_types |= IODEV_DISK;
- if (do_disk & DISK_IOPATH) {
- df.if_allowed_types |= IODEV_IOPATH;
- types |= SNAP_IOPATHS;
+ if (do_disk & DISK_IOPATH_LI) {
+ df.if_allowed_types |= IODEV_IOPATH_LTI;
+ types |= SNAP_IOPATHS_LI;
+ }
+ if (do_disk & DISK_IOPATH_LTI) {
+ df.if_allowed_types |= IODEV_IOPATH_LTI;
+ types |= SNAP_IOPATHS_LTI;
}
if (do_disk & DISK_ERROR_MASK)
types |= SNAP_IODEV_ERRORS;
@@ -232,7 +238,7 @@ main(int argc, char **argv)
types |= SNAP_IODEV_DEVID;
if (do_controller) {
if (!(do_disk & PRINT_VERTICAL) ||
- (do_disk & DISK_EXTENDED_ERRORS))
+ (do_disk & DISK_EXTENDED_ERRORS))
fail(0, "-C can only be used with -e or -x.");
types |= SNAP_CONTROLLERS;
df.if_allowed_types |= IODEV_CONTROLLER;
@@ -255,6 +261,15 @@ main(int argc, char **argv)
kc = open_kstat();
newss = acquire_snapshot(kc, types, &df);
+ /* compute width of "device" field */
+ iodevs_nl = newss->s_iodevs_is_name_maxlen;
+ iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ?
+ IODEVS_NL_MIN : iodevs_nl;
+ iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ?
+ IODEVS_NL_MAX : iodevs_nl;
+
+ do_format();
+
iiter = iter;
do {
if (do_tty || do_cpu) {
@@ -290,6 +305,12 @@ main(int argc, char **argv)
free_snapshot(oldss);
oldss = newss;
newss = acquire_snapshot(kc, types, &df);
+ iodevs_nl = (newss->s_iodevs_is_name_maxlen > iodevs_nl) ?
+ newss->s_iodevs_is_name_maxlen : iodevs_nl;
+ iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ?
+ IODEVS_NL_MIN : iodevs_nl;
+ iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ?
+ IODEVS_NL_MAX : iodevs_nl;
if (!suppress_state)
snapshot_report_changes(oldss, newss);
@@ -305,7 +326,7 @@ main(int argc, char **argv)
free_snapshot(oldss);
free_snapshot(newss);
(void) kstat_close(kc);
-
+ free(df.if_names);
return (0);
}
@@ -337,7 +358,9 @@ show_disk_name(void *v1, void *v2, void *data)
if (dev == NULL)
return;
- name = dev->is_pretty ? dev->is_pretty : dev->is_name;
+ name = do_conversions ? dev->is_pretty : dev->is_name;
+ name = name ? name : dev->is_name;
+
if (!do_raw) {
uint_t width;
@@ -561,7 +584,8 @@ show_disk(void *v1, void *v2, void *data)
break;
}
- disk_name = new->is_pretty ? new->is_pretty : new->is_name;
+ disk_name = do_conversions ? new->is_pretty : new->is_name;
+ disk_name = disk_name ? disk_name : new->is_name;
/*
* Only do if we want IO stats - Avoids errors traveling this
@@ -576,8 +600,18 @@ show_disk(void *v1, void *v2, void *data)
new->is_snaptime);
}
- if (new->is_type == IODEV_CONTROLLER && new->is_nr_children)
- t_delta /= new->is_nr_children;
+ if (new->is_nr_children) {
+ if (new->is_type == IODEV_CONTROLLER) {
+ t_delta /= new->is_nr_children;
+ } else if ((new->is_type == IODEV_IOPATH_LT) ||
+ (new->is_type == IODEV_IOPATH_LI)) {
+ /* synthetic path */
+ if (!old) {
+ t_delta = new->is_crtime;
+ }
+ t_delta /= new->is_nr_children;
+ }
+ }
hr_etime = (double)t_delta;
if (hr_etime == 0.0)
@@ -724,11 +758,12 @@ show_disk(void *v1, void *v2, void *data)
if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) {
if ((!do_conversions) && ((suppress_zero == 0) ||
((do_disk & DISK_EXTENDED) == 0))) {
- if (do_raw == 0)
- push_out(DISPLAYED_NAME_FORMAT,
- disk_name);
- else
+ if (do_raw == 0) {
+ push_out("%-*.*s",
+ iodevs_nl, iodevs_nl, disk_name);
+ } else {
push_out(disk_name);
+ }
}
}
@@ -751,14 +786,15 @@ show_disk(void *v1, void *v2, void *data)
if (suppress_zero) {
if (fzero(rps) && fzero(wps) && fzero(krps) &&
fzero(kwps) && fzero(avw) && fzero(avr) &&
- fzero(serv) && fzero(w_pct) && fzero(r_pct))
+ fzero(serv) && fzero(w_pct) && fzero(r_pct)) {
doit = 0;
- else if (do_conversions == 0) {
- if (do_raw == 0)
- push_out(DISPLAYED_NAME_FORMAT,
- disk_name);
- else
+ } else if (do_conversions == 0) {
+ if (do_raw == 0) {
+ push_out("%-*.*s",
+ iodevs_nl, iodevs_nl, disk_name);
+ } else {
push_out(disk_name);
+ }
}
}
if (doit) {
@@ -886,7 +922,7 @@ static void
usage(void)
{
(void) fprintf(stderr,
- "Usage: iostat [-cCdDeEiImMnpPrstxXz] "
+ "Usage: iostat [-cCdDeEiImMnpPrstxXYz] "
" [-l n] [-T d|u] [disk ...] [interval [count]]\n"
"\t\t-c: report percentage of time system has spent\n"
"\t\t\tin user/system/wait/idle mode\n"
@@ -915,6 +951,7 @@ usage(void)
"\t\t-t: display chars read/written to terminals\n"
"\t\t-x: display extended disk statistics\n"
"\t\t-X: display I/O path statistics\n"
+ "\t\t-Y: display I/O path (I/T/L) statistics\n"
"\t\t-z: Suppress entries with all zero values\n");
exit(1);
}
@@ -927,15 +964,15 @@ show_disk_errors(void *v1, void *v2, void *d)
kstat_named_t *knp;
size_t col;
int i, len;
- char *dev_name = disk->is_name;
+ char *dev_name;
if (disk->is_errors.ks_ndata == 0)
return;
if (disk->is_type == IODEV_CONTROLLER)
return;
- if (disk->is_pretty)
- dev_name = disk->is_pretty;
+ dev_name = do_conversions ? disk->is_pretty : disk->is_name;
+ dev_name = dev_name ? dev_name : disk->is_name;
len = strlen(dev_name);
if (len > 20)
@@ -1013,7 +1050,7 @@ do_args(int argc, char **argv)
extern char *optarg;
extern int optind;
- while ((c = getopt(argc, argv, "tdDxXCciIpPnmMeEszrT:l:")) != EOF)
+ while ((c = getopt(argc, argv, "tdDxXYCciIpPnmMeEszrT:l:")) != EOF)
switch (c) {
case 't':
do_tty++;
@@ -1028,7 +1065,16 @@ do_args(int argc, char **argv)
do_disk |= DISK_EXTENDED;
break;
case 'X':
- do_disk |= DISK_IOPATH;
+ if (do_disk & DISK_IOPATH_LTI)
+ errflg++; /* -Y already used */
+ else
+ do_disk |= DISK_IOPATH_LI;
+ break;
+ case 'Y':
+ if (do_disk & DISK_IOPATH_LI)
+ errflg++; /* -X already used */
+ else
+ do_disk |= DISK_IOPATH_LTI;
break;
case 'C':
do_controller++;
@@ -1100,11 +1146,20 @@ do_args(int argc, char **argv)
if (errflg) {
usage();
}
+
/* if no output classes explicity specified, use defaults */
if (do_tty == 0 && do_disk == 0 && do_cpu == 0)
do_tty = do_cpu = 1, do_disk = DISK_OLD;
/*
+ * multi-path options (-X, -Y) without a specific vertical
+ * output format (-x, -e, -E) imply extended -x format
+ */
+ if ((do_disk & (DISK_IOPATH_LI | DISK_IOPATH_LTI)) &&
+ !(do_disk & PRINT_VERTICAL))
+ do_disk |= DISK_EXTENDED;
+
+ /*
* If conflicting options take the preferred
* -D and -x result in -x
* -d or -D and -e or -E gives only whatever -d or -D was specified
@@ -1114,12 +1169,6 @@ do_args(int argc, char **argv)
if ((do_disk & DISK_NORMAL) && (do_disk & DISK_ERROR_MASK))
do_disk &= ~DISK_ERROR_MASK;
- /*
- * I/O path stats are only available with extended (-x) stats
- */
- if ((do_disk & DISK_IOPATH) && !(do_disk & DISK_EXTENDED))
- do_disk &= ~DISK_IOPATH;
-
/* nfs, tape, always shown */
df.if_allowed_types = IODEV_NFS | IODEV_TAPE;
@@ -1150,11 +1199,14 @@ do_args(int argc, char **argv)
* "Note: disks explicitly requested
* are not subject to this disk limit"
*/
- if (count > df.if_max_iodevs)
+ if ((count > df.if_max_iodevs) ||
+ (count && (df.if_max_iodevs == UNLIMITED_IODEVS)))
df.if_max_iodevs = count;
+
df.if_names = safe_alloc(count * sizeof (char *));
(void) memset(df.if_names, 0, count * sizeof (char *));
+ df.if_nr_names = 0;
while (optind < argc && !isdigit(argv[optind][0]))
df.if_names[df.if_nr_names++] = argv[optind++];
}
@@ -1219,20 +1271,29 @@ do_format(void)
fstr, ch, ch);
break;
case DISK_EXTENDED:
+ /* This is -x option */
if (!do_conversions) {
- if (do_raw == 0)
- fstr = "device r/%c w/%c "
+ /* without -n option */
+ if (do_raw == 0) {
+ /* without -r option */
+ (void) snprintf(disk_header,
+ sizeof (disk_header),
+ "%-*.*s r/%c w/%c "
"%cr/%c %cw/%c wait actv "
- "svc_t %%%%w %%%%b %s";
- else
- fstr = "device,r/%c,w/%c,%cr/%c,%cw/%c,"
- "wait,actv,svc_t,%%%%w,"
- "%%%%b,%s";
- (void) snprintf(disk_header,
- sizeof (disk_header),
- fstr, ch, ch, iosz, ch, iosz,
- ch, header);
+ "svc_t %%%%w %%%%b %s",
+ iodevs_nl, iodevs_nl, "device",
+ ch, ch, iosz, ch, iosz, ch, header);
+ } else {
+ /* with -r option */
+ (void) snprintf(disk_header,
+ sizeof (disk_header),
+ "device,r/%c,w/%c,%cr/%c,%cw/%c,"
+ "wait,actv,svc_t,%%%%w,"
+ "%%%%b,%s",
+ ch, ch, iosz, ch, iosz, ch, header);
+ }
} else {
+ /* with -n option */
if (do_raw == 0) {
fstr = " r/%c w/%c %cr/%c "
"%cw/%c wait actv wsvc_t asvc_t "
@@ -1255,15 +1316,14 @@ do_format(void)
/* do DISK_ERRORS header (already added above for DISK_EXTENDED) */
if ((do_disk & DISK_ERRORS) &&
((do_disk & DISK_IO_MASK) != DISK_EXTENDED)) {
- char *sep;
-
if (!do_conversions) {
- if (do_raw == 0) {
- sep = " ";
- } else
- sep = ",";
- (void) snprintf(disk_header, sizeof (disk_header),
- "%s%s%s", "device", sep, header);
+ if (do_raw == 0)
+ (void) snprintf(disk_header,
+ sizeof (disk_header), "%-*.*s %s",
+ iodevs_nl, iodevs_nl, "device", header);
+ else
+ (void) snprintf(disk_header,
+ sizeof (disk_header), "device,%s", header);
} else {
if (do_raw == 0) {
(void) snprintf(disk_header,
@@ -1647,63 +1707,6 @@ ull_delta(u_longlong_t old, u_longlong_t new)
}
/*
- * 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);
- }
- }
-}
-
-/*
* Take the difference of an unsigned 32
* bit int attempting to cater for
* overflow.
diff --git a/usr/src/uts/common/os/sunmdi.c b/usr/src/uts/common/os/sunmdi.c
index 093a6ae90c..ad173edf7c 100644
--- a/usr/src/uts/common/os/sunmdi.c
+++ b/usr/src/uts/common/os/sunmdi.c
@@ -212,7 +212,6 @@ static void i_mdi_log_sysevent(dev_info_t *, char *, char *);
/*
* Internal mdi_pathinfo node functions
*/
-static int i_mdi_pi_kstat_create(mdi_pathinfo_t *);
static void i_mdi_pi_kstat_destroy(mdi_pathinfo_t *);
static mdi_vhci_t *i_mdi_vhci_class2vhci(char *);
@@ -3486,10 +3485,9 @@ state_change_exit:
int
mdi_pi_online(mdi_pathinfo_t *pip, int flags)
{
- mdi_client_t *ct = MDI_PI(pip)->pi_client;
- dev_info_t *cdip;
+ mdi_client_t *ct = MDI_PI(pip)->pi_client;
int client_held = 0;
- int rv;
+ int rv;
ASSERT(ct != NULL);
rv = i_mdi_pi_state_change(pip, MDI_PATHINFO_STATE_ONLINE, flags);
@@ -3517,27 +3515,6 @@ mdi_pi_online(mdi_pathinfo_t *pip, int flags)
MDI_CLIENT_UNLOCK(ct);
}
- /*
- * Create the per-path (pathinfo) IO and error kstats which
- * are reported via iostat(1m).
- *
- * Defer creating the per-path kstats if device is not yet
- * attached; the names of the kstats are constructed in part
- * using the devices instance number which is assigned during
- * process of attaching the client device.
- *
- * The framework post_attach handler, mdi_post_attach(), is
- * is responsible for initializing the client's pathinfo list
- * once successfully attached.
- */
- cdip = ct->ct_dip;
- ASSERT(cdip);
- if (cdip == NULL || !i_ddi_devi_attached(cdip))
- return (rv);
-
- MDI_CLIENT_LOCK(ct);
- rv = i_mdi_pi_kstat_create(pip);
- MDI_CLIENT_UNLOCK(ct);
return (rv);
}
@@ -4882,7 +4859,7 @@ mdi_post_attach(dev_info_t *dip, ddi_attach_cmd_t cmd, int error)
{
mdi_phci_t *ph;
mdi_client_t *ct;
- mdi_pathinfo_t *pip;
+ mdi_vhci_t *vh;
if (MDI_PHCI(dip)) {
ph = i_devi_get_phci(dip);
@@ -4940,18 +4917,13 @@ mdi_post_attach(dev_info_t *dip, ddi_attach_cmd_t cmd, int error)
}
/*
- * Client device has successfully attached.
- * Create kstats for any pathinfo structures
- * initially associated with this client.
+ * Client device has successfully attached, inform
+ * the vhci.
*/
- for (pip = ct->ct_path_head; pip != NULL;
- pip = (mdi_pathinfo_t *)
- MDI_PI(pip)->pi_client_link) {
- if (!MDI_PI_IS_OFFLINE(pip)) {
- (void) i_mdi_pi_kstat_create(pip);
- i_mdi_report_path_state(ct, pip);
- }
- }
+ vh = ct->ct_vhci;
+ if (vh->vh_ops->vo_client_attached)
+ (*vh->vh_ops->vo_client_attached)(dip);
+
MDI_CLIENT_SET_ATTACH(ct);
break;
@@ -5247,67 +5219,36 @@ i_mdi_client_post_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, int error)
MDI_CLIENT_UNLOCK(ct);
}
+int
+mdi_pi_kstat_exists(mdi_pathinfo_t *pip)
+{
+ return (MDI_PI(pip)->pi_kstats ? 1 : 0);
+}
+
/*
* create and install per-path (client - pHCI) statistics
* I/O stats supported: nread, nwritten, reads, and writes
* Error stats - hard errors, soft errors, & transport errors
*/
-static int
-i_mdi_pi_kstat_create(mdi_pathinfo_t *pip)
+int
+mdi_pi_kstat_create(mdi_pathinfo_t *pip, char *ksname)
{
-
- dev_info_t *client = MDI_PI(pip)->pi_client->ct_dip;
- dev_info_t *ppath = MDI_PI(pip)->pi_phci->ph_dip;
- char ksname[KSTAT_STRLEN];
- mdi_pathinfo_t *cpip;
- const char *err_postfix = ",err";
- kstat_t *kiosp, *kerrsp;
- struct pi_errs *nsp;
- struct mdi_pi_kstats *mdi_statp;
-
- ASSERT(client != NULL && ppath != NULL);
-
- ASSERT(MDI_CLIENT_LOCKED(MDI_PI(pip)->pi_client));
+ kstat_t *kiosp, *kerrsp;
+ struct pi_errs *nsp;
+ struct mdi_pi_kstats *mdi_statp;
if (MDI_PI(pip)->pi_kstats != NULL)
return (MDI_SUCCESS);
- for (cpip = MDI_PI(pip)->pi_client->ct_path_head; cpip != NULL;
- cpip = (mdi_pathinfo_t *)(MDI_PI(cpip)->pi_client_link)) {
- if ((cpip == pip) || MDI_PI_IS_OFFLINE(pip))
- continue;
- /*
- * We have found a different path with same parent
- * kstats for a given client-pHCI are common
- */
- if ((MDI_PI(cpip)->pi_phci->ph_dip == ppath) &&
- (MDI_PI(cpip)->pi_kstats != NULL)) {
- MDI_PI(cpip)->pi_kstats->pi_kstat_ref++;
- MDI_PI(pip)->pi_kstats = MDI_PI(cpip)->pi_kstats;
- return (MDI_SUCCESS);
- }
- }
-
- /*
- * stats are named as follows: TGTx.HBAy, e.g. "ssd0.fp0"
- * clamp length of name against max length of error kstat name
- */
- if (snprintf(ksname, KSTAT_STRLEN, "%s%d.%s%d",
- ddi_driver_name(client), ddi_get_instance(client),
- ddi_driver_name(ppath), ddi_get_instance(ppath)) >
- (KSTAT_STRLEN - strlen(err_postfix))) {
- return (MDI_FAILURE);
- }
if ((kiosp = kstat_create("mdi", 0, ksname, "iopath",
- KSTAT_TYPE_IO, 1, 0)) == NULL) {
+ KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT)) == NULL) {
return (MDI_FAILURE);
}
- (void) strcat(ksname, err_postfix);
+ (void) strcat(ksname, ",err");
kerrsp = kstat_create("mdi", 0, ksname, "iopath_errors",
KSTAT_TYPE_NAMED,
sizeof (struct pi_errs) / sizeof (kstat_named_t), 0);
-
if (kerrsp == NULL) {
kstat_delete(kiosp);
return (MDI_FAILURE);
@@ -5351,6 +5292,8 @@ i_mdi_pi_kstat_destroy(mdi_pathinfo_t *pip)
struct mdi_pi_kstats *mdi_statp;
+ if (MDI_PI(pip)->pi_kstats == NULL)
+ return;
if ((mdi_statp = MDI_PI(pip)->pi_kstats) == NULL)
return;
diff --git a/usr/src/uts/common/sys/mdi_impldefs.h b/usr/src/uts/common/sys/mdi_impldefs.h
index 0c3a035339..4838c1ca7b 100644
--- a/usr/src/uts/common/sys/mdi_impldefs.h
+++ b/usr/src/uts/common/sys/mdi_impldefs.h
@@ -238,6 +238,9 @@ typedef struct mdi_vhci_ops {
/* Client path failover callback */
int (*vo_failover)(dev_info_t *vdip, dev_info_t *cdip, int flags);
+
+ /* Client attached callback */
+ void (*vo_client_attached)(dev_info_t *cdip);
} mdi_vhci_ops_t;
/*
@@ -1178,8 +1181,10 @@ int mdi_failover(dev_info_t *, dev_info_t *, int);
#define MDI_FAILOVER_ASYNC 2 /* Asyncronous Failover */
/*
- * mdi_pathinfo node state change functions.
+ * mdi_pathinfo node kstat functions.
*/
+int mdi_pi_kstat_exists(mdi_pathinfo_t *);
+int mdi_pi_kstat_create(mdi_pathinfo_t *pip, char *ks_name);
void mdi_pi_kstat_iosupdate(mdi_pathinfo_t *, struct buf *);
/*
diff --git a/usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h b/usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h
index ed45f2f421..5be9bc402b 100644
--- a/usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h
+++ b/usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h
@@ -742,6 +742,8 @@ typedef struct sv_switch_to_cntlr_iocdata {
#define SCSI_VHCI_PATH_ENABLE (SCSI_VHCI_CTL_SUB_CMD + 0x0D)
#define SCSI_VHCI_MPAPI (SCSI_VHCI_CTL_SUB_CMD + 0x0E)
+#define SCSI_VHCI_GET_TARGET_LONGNAME (SCSI_VHCI_CTL_SUB_CMD + 0x0F)
+
#ifdef __cplusplus
}
#endif