1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include "statcommon.h"
29
30#include <string.h>
31#include <errno.h>
32
33/* max size of report change annotations */
34#define	LIST_SIZE 512
35
36static char cpus_added[LIST_SIZE];
37static char cpus_removed[LIST_SIZE];
38
39static int
40cpu_walk(struct snapshot *old, struct snapshot *new,
41    snapshot_cb cb, void *data)
42{
43	int changed = 0;
44	int i;
45
46	/* CPUs can change state but not re-order */
47	for (i = 0; i < new->s_nr_cpus; i++) {
48		struct cpu_snapshot *cpu = NULL;
49		struct cpu_snapshot *newcpu = &new->s_cpus[i];
50		if (old)
51			cpu = &old->s_cpus[i];
52		cb(cpu, newcpu, data);
53		if (cpu == NULL)
54			changed = 1;
55		else {
56			/*
57			 * We only care about off/on line transitions
58			 */
59			if ((CPU_ACTIVE(cpu) && !CPU_ACTIVE(newcpu)) ||
60			    (!CPU_ACTIVE(cpu) && CPU_ACTIVE(newcpu)))
61				changed = 1;
62			if ((new->s_types & SNAP_PSETS) &&
63				cpu->cs_pset_id != newcpu->cs_pset_id)
64				changed = 1;
65		}
66
67	}
68
69	return (changed);
70}
71
72static int
73pset_walk(struct snapshot *old, struct snapshot *new,
74    snapshot_cb cb, void *data)
75{
76	int i = 0;
77	int j = 0;
78	int changed = 0;
79
80	while (old && i < old->s_nr_psets && j < new->s_nr_psets) {
81		if (old->s_psets[i].ps_id < new->s_psets[j].ps_id) {
82			cb(&old->s_psets[i], NULL, data);
83			i++;
84			changed = 1;
85		} else if (old->s_psets[i].ps_id > new->s_psets[j].ps_id) {
86			cb(NULL, &new->s_psets[j], data);
87			j++;
88			changed = 1;
89		} else {
90			cb(&old->s_psets[i], &new->s_psets[j], data);
91			i++;
92			j++;
93		}
94	}
95
96	while (old && i < old->s_nr_psets) {
97		cb(&old->s_psets[i], NULL, data);
98		i++;
99		changed = 1;
100	}
101
102	while (j < new->s_nr_psets) {
103		cb(NULL, &new->s_psets[j], data);
104		j++;
105		changed = 1;
106	}
107
108	return (changed);
109}
110
111static int
112iodev_walk(struct iodev_snapshot *d1, struct iodev_snapshot *d2,
113    snapshot_cb cb, void *data)
114{
115	int changed = 0;
116
117	while (d1 && d2) {
118		if (strcmp(d1->is_name, d2->is_name) < 0) {
119			changed = 1;
120			cb(d1, NULL, data);
121			(void) iodev_walk(d1->is_children, NULL, cb, data);
122			d1 = d1->is_next;
123		} else if (strcmp(d1->is_name, d2->is_name) > 0) {
124			changed = 1;
125			cb(NULL, d2, data);
126			(void) iodev_walk(NULL, d2->is_children, cb, data);
127			d2 = d2->is_next;
128		} else {
129			cb(d1, d2, data);
130			changed |= iodev_walk(d1->is_children,
131					d2->is_children, cb, data);
132			d1 = d1->is_next;
133			d2 = d2->is_next;
134		}
135	}
136
137	while (d1) {
138		changed = 1;
139		cb(d1, NULL, data);
140		(void) iodev_walk(d1->is_children, NULL, cb, data);
141		d1 = d1->is_next;
142	}
143
144	while (d2) {
145		changed = 1;
146		cb(NULL, d2, data);
147		(void) iodev_walk(NULL, d2->is_children, cb, data);
148		d2 = d2->is_next;
149	}
150
151	return (changed);
152}
153
154int
155snapshot_walk(enum snapshot_types type, struct snapshot *old,
156    struct snapshot *new, snapshot_cb cb, void *data)
157{
158	int changed = 0;
159
160	switch (type) {
161	case SNAP_CPUS:
162		changed = cpu_walk(old, new, cb, data);
163		break;
164
165	case SNAP_PSETS:
166		changed = pset_walk(old, new, cb, data);
167		break;
168
169	case SNAP_CONTROLLERS:
170	case SNAP_IODEVS:
171	case SNAP_IOPATHS_LI:
172	case SNAP_IOPATHS_LTI:
173		changed = iodev_walk(old ? old->s_iodevs : NULL,
174		    new->s_iodevs, cb, data);
175		break;
176
177	default:
178		break;
179	}
180
181	return (changed);
182}
183
184static void
185add_nr_to_list(char *buf, unsigned long nr)
186{
187	char tmp[LIST_SIZE];
188
189	(void) snprintf(tmp, LIST_SIZE, "%lu", nr);
190
191	if (strlen(buf))
192		(void) strlcat(buf, ", ", LIST_SIZE);
193
194	(void) strlcat(buf, tmp, LIST_SIZE);
195}
196
197static void
198cpu_report(void *v1, void *v2, void *data)
199{
200	int *pset = (int *)data;
201	struct cpu_snapshot *c1 = (struct cpu_snapshot *)v1;
202	struct cpu_snapshot *c2 = (struct cpu_snapshot *)v2;
203
204	if (*pset && c1->cs_pset_id != c2->cs_pset_id) {
205		(void) printf("<<processor %d moved from pset: %d to: %d>>\n",
206		    c1->cs_id, c1->cs_pset_id, c2->cs_pset_id);
207	}
208
209	if (c1->cs_state == c2->cs_state)
210		return;
211
212	if (CPU_ONLINE(c1->cs_state) && !CPU_ONLINE(c2->cs_state))
213		add_nr_to_list(cpus_removed, c1->cs_id);
214
215	if (!CPU_ONLINE(c1->cs_state) && CPU_ONLINE(c2->cs_state))
216		add_nr_to_list(cpus_added, c2->cs_id);
217}
218
219/*ARGSUSED*/
220static void
221pset_report(void *v1, void *v2, void *data)
222{
223	struct pset_snapshot *p1 = (struct pset_snapshot *)v1;
224	struct pset_snapshot *p2 = (struct pset_snapshot *)v2;
225
226	if (p2 == NULL) {
227		(void) printf("<<pset destroyed: %u>>\n", p1->ps_id);
228		return;
229	}
230
231	if (p1 == NULL)
232		(void) printf("<<pset created: %u>>\n", p2->ps_id);
233}
234
235static void
236get_child_list(struct iodev_snapshot *iodev, char *buf)
237{
238	char tmp[LIST_SIZE];
239	struct iodev_snapshot *pos = iodev->is_children;
240
241	while (pos) {
242		if (pos->is_type == IODEV_PARTITION) {
243			add_nr_to_list(buf, pos->is_id.id);
244		} else if (pos->is_type == IODEV_DISK) {
245			if (strlen(buf))
246				(void) strlcat(buf, ", ", LIST_SIZE);
247			(void) strlcat(buf, "t", LIST_SIZE);
248			(void) strlcat(buf, pos->is_id.tid, LIST_SIZE);
249			(void) strlcat(buf, "d", LIST_SIZE);
250			*tmp = '\0';
251			add_nr_to_list(tmp, pos->is_id.id);
252			(void) strlcat(buf, tmp, LIST_SIZE);
253		}
254		pos = pos->is_next;
255	}
256}
257
258static void
259iodev_changed(struct iodev_snapshot *iodev, int added)
260{
261	char tmp[LIST_SIZE];
262	int is_disk = iodev->is_type == IODEV_DISK;
263	char *name = iodev->is_name;
264
265	if (iodev->is_pretty)
266		name = iodev->is_pretty;
267
268	switch (iodev->is_type) {
269	case IODEV_IOPATH_LT:
270	case IODEV_IOPATH_LI:
271	case IODEV_IOPATH_LTI:
272		(void) printf("<<multi-path %s: %s>>\n",
273		    added ? "added" : "removed", name);
274		break;
275	case IODEV_PARTITION:
276		(void) printf("<<partition %s: %s>>\n",
277		    added ? "added" : "removed", name);
278		break;
279	case IODEV_NFS:
280		(void) printf("<<NFS %s: %s>>\n",
281		    added ? "mounted" : "unmounted", name);
282		break;
283	case IODEV_TAPE:
284		(void) printf("<<device %s: %s>>\n",
285		    added ? "added" : "removed", name);
286		break;
287	case IODEV_CONTROLLER:
288	case IODEV_DISK:
289		*tmp = '\0';
290		get_child_list(iodev, tmp);
291		(void) printf("<<%s %s: %s", is_disk ? "disk" : "controller",
292		    added ? "added" : "removed", name);
293		if (!*tmp) {
294			(void) printf(">>\n");
295			return;
296		}
297		(void) printf(" (%s %s)>>\n", is_disk ? "slices" : "disks",
298		    tmp);
299		break;
300	};
301}
302
303static void
304iodev_report(struct iodev_snapshot *d1, struct iodev_snapshot *d2)
305{
306	while (d1 && d2) {
307		if (iodev_cmp(d1, d2) < 0) {
308			iodev_changed(d1, 0);
309			d1 = d1->is_next;
310		} else if (iodev_cmp(d1, d2) > 0) {
311			iodev_changed(d2, 1);
312			d2 = d2->is_next;
313		} else {
314			iodev_report(d1->is_children, d2->is_children);
315			d1 = d1->is_next;
316			d2 = d2->is_next;
317		}
318	}
319
320	while (d1) {
321		iodev_changed(d1, 0);
322		d1 = d1->is_next;
323	}
324
325	while (d2) {
326		iodev_changed(d2, 1);
327		d2 = d2->is_next;
328	}
329}
330
331void
332snapshot_report_changes(struct snapshot *old, struct snapshot *new)
333{
334	int pset;
335
336	if (old == NULL || new == NULL)
337		return;
338
339	if (old->s_types != new->s_types)
340		return;
341
342	pset = old->s_types & SNAP_PSETS;
343
344	cpus_removed[0] = '\0';
345	cpus_added[0] = '\0';
346
347	if (old->s_types & SNAP_CPUS)
348		(void) snapshot_walk(SNAP_CPUS, old, new, cpu_report, &pset);
349
350	if (cpus_added[0]) {
351		(void) printf("<<processors added: %s>>\n",
352		    cpus_added);
353	}
354	if (cpus_removed[0]) {
355		(void) printf("<<processors removed: %s>>\n",
356		    cpus_removed);
357	}
358	if (pset) {
359		(void) snapshot_walk(SNAP_PSETS, old, new,
360		    pset_report, NULL);
361	}
362
363	iodev_report(old->s_iodevs, new->s_iodevs);
364}
365
366/*ARGSUSED*/
367static void
368dummy_cb(void *v1, void *v2, void *data)
369{
370}
371
372int
373snapshot_has_changed(struct snapshot *old, struct snapshot *new)
374{
375	int ret = 0;
376	int cpu_mask = SNAP_CPUS | SNAP_PSETS | SNAP_SYSTEM;
377	int iodev_mask = SNAP_CONTROLLERS | SNAP_IODEVS |
378			SNAP_IOPATHS_LI | SNAP_IOPATHS_LTI;
379
380	if (old == NULL)
381		return (1);
382
383	if (new == NULL)
384		return (EINVAL);
385
386	if (old->s_types != new->s_types)
387		return (EINVAL);
388
389	if (!ret && (old->s_types & cpu_mask))
390		ret = snapshot_walk(SNAP_CPUS, old, new, dummy_cb, NULL);
391	if (!ret && (old->s_types & SNAP_PSETS))
392		ret = snapshot_walk(SNAP_PSETS, old, new, dummy_cb, NULL);
393	if (!ret && (old->s_types & iodev_mask))
394		ret = snapshot_walk(SNAP_IODEVS, old, new, dummy_cb, NULL);
395
396	return (ret);
397}
398