/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * 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. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "statcommon.h" #include #include /* max size of report change annotations */ #define LIST_SIZE 512 static char cpus_added[LIST_SIZE]; static char cpus_removed[LIST_SIZE]; static int cpu_walk(struct snapshot *old, struct snapshot *new, snapshot_cb cb, void *data) { int changed = 0; int i; /* CPUs can change state but not re-order */ for (i = 0; i < new->s_nr_cpus; i++) { struct cpu_snapshot *cpu = NULL; struct cpu_snapshot *newcpu = &new->s_cpus[i]; if (old) cpu = &old->s_cpus[i]; cb(cpu, newcpu, data); if (cpu == NULL) changed = 1; else { /* * We only care about off/on line transitions */ if ((CPU_ACTIVE(cpu) && !CPU_ACTIVE(newcpu)) || (!CPU_ACTIVE(cpu) && CPU_ACTIVE(newcpu))) changed = 1; if ((new->s_types & SNAP_PSETS) && cpu->cs_pset_id != newcpu->cs_pset_id) changed = 1; } } return (changed); } static int pset_walk(struct snapshot *old, struct snapshot *new, snapshot_cb cb, void *data) { int i = 0; int j = 0; int changed = 0; while (old && i < old->s_nr_psets && j < new->s_nr_psets) { if (old->s_psets[i].ps_id < new->s_psets[j].ps_id) { cb(&old->s_psets[i], NULL, data); i++; changed = 1; } else if (old->s_psets[i].ps_id > new->s_psets[j].ps_id) { cb(NULL, &new->s_psets[j], data); j++; changed = 1; } else { cb(&old->s_psets[i], &new->s_psets[j], data); i++; j++; } } while (old && i < old->s_nr_psets) { cb(&old->s_psets[i], NULL, data); i++; changed = 1; } while (j < new->s_nr_psets) { cb(NULL, &new->s_psets[j], data); j++; changed = 1; } return (changed); } static int iodev_walk(struct iodev_snapshot *d1, struct iodev_snapshot *d2, snapshot_cb cb, void *data) { int changed = 0; while (d1 && d2) { if (strcmp(d1->is_name, d2->is_name) < 0) { changed = 1; cb(d1, NULL, data); (void) iodev_walk(d1->is_children, NULL, cb, data); d1 = d1->is_next; } else if (strcmp(d1->is_name, d2->is_name) > 0) { changed = 1; cb(NULL, d2, data); (void) iodev_walk(NULL, d2->is_children, cb, data); d2 = d2->is_next; } else { cb(d1, d2, data); changed |= iodev_walk(d1->is_children, d2->is_children, cb, data); d1 = d1->is_next; d2 = d2->is_next; } } while (d1) { changed = 1; cb(d1, NULL, data); (void) iodev_walk(d1->is_children, NULL, cb, data); d1 = d1->is_next; } while (d2) { changed = 1; cb(NULL, d2, data); (void) iodev_walk(NULL, d2->is_children, cb, data); d2 = d2->is_next; } return (changed); } int snapshot_walk(enum snapshot_types type, struct snapshot *old, struct snapshot *new, snapshot_cb cb, void *data) { int changed = 0; switch (type) { case SNAP_CPUS: changed = cpu_walk(old, new, cb, data); break; case SNAP_PSETS: changed = pset_walk(old, new, cb, data); break; case SNAP_CONTROLLERS: case SNAP_IODEVS: case SNAP_IOPATHS_LI: case SNAP_IOPATHS_LTI: changed = iodev_walk(old ? old->s_iodevs : NULL, new->s_iodevs, cb, data); break; default: break; } return (changed); } static void add_nr_to_list(char *buf, unsigned long nr) { char tmp[LIST_SIZE]; (void) snprintf(tmp, LIST_SIZE, "%lu", nr); if (strlen(buf)) (void) strlcat(buf, ", ", LIST_SIZE); (void) strlcat(buf, tmp, LIST_SIZE); } static void cpu_report(void *v1, void *v2, void *data) { int *pset = (int *)data; struct cpu_snapshot *c1 = (struct cpu_snapshot *)v1; struct cpu_snapshot *c2 = (struct cpu_snapshot *)v2; if (*pset && c1->cs_pset_id != c2->cs_pset_id) { (void) printf("<>\n", c1->cs_id, c1->cs_pset_id, c2->cs_pset_id); } if (c1->cs_state == c2->cs_state) return; if (CPU_ONLINE(c1->cs_state) && !CPU_ONLINE(c2->cs_state)) add_nr_to_list(cpus_removed, c1->cs_id); if (!CPU_ONLINE(c1->cs_state) && CPU_ONLINE(c2->cs_state)) add_nr_to_list(cpus_added, c2->cs_id); } /*ARGSUSED*/ static void pset_report(void *v1, void *v2, void *data) { struct pset_snapshot *p1 = (struct pset_snapshot *)v1; struct pset_snapshot *p2 = (struct pset_snapshot *)v2; if (p2 == NULL) { (void) printf("<>\n", p1->ps_id); return; } if (p1 == NULL) (void) printf("<>\n", p2->ps_id); } static void get_child_list(struct iodev_snapshot *iodev, char *buf) { char tmp[LIST_SIZE]; struct iodev_snapshot *pos = iodev->is_children; while (pos) { if (pos->is_type == IODEV_PARTITION) { add_nr_to_list(buf, pos->is_id.id); } else if (pos->is_type == IODEV_DISK) { if (strlen(buf)) (void) strlcat(buf, ", ", LIST_SIZE); (void) strlcat(buf, "t", LIST_SIZE); (void) strlcat(buf, pos->is_id.tid, LIST_SIZE); (void) strlcat(buf, "d", LIST_SIZE); *tmp = '\0'; add_nr_to_list(tmp, pos->is_id.id); (void) strlcat(buf, tmp, LIST_SIZE); } pos = pos->is_next; } } static void iodev_changed(struct iodev_snapshot *iodev, int added) { char tmp[LIST_SIZE]; int is_disk = iodev->is_type == IODEV_DISK; char *name = iodev->is_name; if (iodev->is_pretty) name = iodev->is_pretty; switch (iodev->is_type) { case IODEV_IOPATH_LT: case IODEV_IOPATH_LI: case IODEV_IOPATH_LTI: (void) printf("<>\n", added ? "added" : "removed", name); break; case IODEV_PARTITION: (void) printf("<>\n", added ? "added" : "removed", name); break; case IODEV_NFS: (void) printf("<>\n", added ? "mounted" : "unmounted", name); break; case IODEV_TAPE: (void) printf("<>\n", added ? "added" : "removed", name); break; case IODEV_CONTROLLER: case IODEV_DISK: *tmp = '\0'; get_child_list(iodev, tmp); (void) printf("<<%s %s: %s", is_disk ? "disk" : "controller", added ? "added" : "removed", name); if (!*tmp) { (void) printf(">>\n"); return; } (void) printf(" (%s %s)>>\n", is_disk ? "slices" : "disks", tmp); break; }; } static void iodev_report(struct iodev_snapshot *d1, struct iodev_snapshot *d2) { while (d1 && d2) { if (iodev_cmp(d1, d2) < 0) { iodev_changed(d1, 0); d1 = d1->is_next; } else if (iodev_cmp(d1, d2) > 0) { iodev_changed(d2, 1); d2 = d2->is_next; } else { iodev_report(d1->is_children, d2->is_children); d1 = d1->is_next; d2 = d2->is_next; } } while (d1) { iodev_changed(d1, 0); d1 = d1->is_next; } while (d2) { iodev_changed(d2, 1); d2 = d2->is_next; } } void snapshot_report_changes(struct snapshot *old, struct snapshot *new) { int pset; if (old == NULL || new == NULL) return; if (old->s_types != new->s_types) return; pset = old->s_types & SNAP_PSETS; cpus_removed[0] = '\0'; cpus_added[0] = '\0'; if (old->s_types & SNAP_CPUS) (void) snapshot_walk(SNAP_CPUS, old, new, cpu_report, &pset); if (cpus_added[0]) { (void) printf("<>\n", cpus_added); } if (cpus_removed[0]) { (void) printf("<>\n", cpus_removed); } if (pset) { (void) snapshot_walk(SNAP_PSETS, old, new, pset_report, NULL); } iodev_report(old->s_iodevs, new->s_iodevs); } /*ARGSUSED*/ static void dummy_cb(void *v1, void *v2, void *data) { } int 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_LI | SNAP_IOPATHS_LTI; if (old == NULL) return (1); if (new == NULL) return (EINVAL); if (old->s_types != new->s_types) return (EINVAL); if (!ret && (old->s_types & cpu_mask)) ret = snapshot_walk(SNAP_CPUS, old, new, dummy_cb, NULL); if (!ret && (old->s_types & SNAP_PSETS)) ret = snapshot_walk(SNAP_PSETS, old, new, dummy_cb, NULL); if (!ret && (old->s_types & iodev_mask)) ret = snapshot_walk(SNAP_IODEVS, old, new, dummy_cb, NULL); return (ret); }