diff options
author | cth <none@none> | 2006-10-12 10:18:45 -0700 |
---|---|---|
committer | cth <none@none> | 2006-10-12 10:18:45 -0700 |
commit | 37fbbce5257519d600faa3d23d464b42b71c1605 (patch) | |
tree | c0f2a50b50b97964740ab5a7586f74bd87560678 | |
parent | 2bc4236aac4fe3a3ba80139bc39ee9f1e356ab55 (diff) | |
download | illumos-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.c | 68 | ||||
-rw-r--r-- | usr/src/cmd/stat/common/acquire_iodevs.c | 590 | ||||
-rw-r--r-- | usr/src/cmd/stat/common/dsr.c | 210 | ||||
-rw-r--r-- | usr/src/cmd/stat/common/dsr.h | 14 | ||||
-rw-r--r-- | usr/src/cmd/stat/common/statcommon.h | 23 | ||||
-rw-r--r-- | usr/src/cmd/stat/common/walkers.c | 17 | ||||
-rw-r--r-- | usr/src/cmd/stat/iostat/iostat.c | 239 | ||||
-rw-r--r-- | usr/src/uts/common/os/sunmdi.c | 105 | ||||
-rw-r--r-- | usr/src/uts/common/sys/mdi_impldefs.h | 7 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h | 2 |
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 |