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