OpenSolaris_b135/cmd/stat/common/walkers.c

/*
 * 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 <string.h>
#include <errno.h>

/* 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("<<processor %d moved from pset: %d to: %d>>\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("<<pset destroyed: %u>>\n", p1->ps_id);
		return;
	}

	if (p1 == NULL)
		(void) printf("<<pset created: %u>>\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("<<multi-path %s: %s>>\n",
		    added ? "added" : "removed", name);
		break;
	case IODEV_PARTITION:
		(void) printf("<<partition %s: %s>>\n",
		    added ? "added" : "removed", name);
		break;
	case IODEV_NFS:
		(void) printf("<<NFS %s: %s>>\n",
		    added ? "mounted" : "unmounted", name);
		break;
	case IODEV_TAPE:
		(void) printf("<<device %s: %s>>\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("<<processors added: %s>>\n",
		    cpus_added);
	}
	if (cpus_removed[0]) {
		(void) printf("<<processors removed: %s>>\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);
}