xref: /illumos-gate/usr/src/cmd/power/sysstat.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1995-2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <unistd.h>				/* sleep() */
32 #include <string.h>
33 #include <errno.h>
34 #include <syslog.h>
35 #include <thread.h>
36 #include <time.h>
37 #include <kstat.h>
38 #include <sys/sysinfo.h>
39 #include <sys/sysmacros.h>
40 #include "powerd.h"
41 
42 /*
43  * External Variables
44  */
45 extern	pwr_info_t	*info;
46 
47 /*
48  * State Variables
49  */
50 static	kstat_ctl_t	*kc;			/* libkstat cookie */
51 static	int		ncpus;
52 static	kstat_t		**cpu_stats_list = NULL;
53 static	kstat_t		old_cpu_stats, new_cpu_stats;
54 static	hrtime_t	tty_snaptime;
55 static	kstat_t		*load_ave_ksp;
56 static	ulong_t		load_ave;
57 static	hrtime_t	last_load_ave_change;
58 static	kstat_t		*conskbd_ksp, *consms_ksp;
59 static	kstat_t		*nfs_client2_kstat, *nfs_client3_kstat;
60 static	kstat_t		*nfs_server2_kstat, *nfs_server3_kstat;
61 static	uint64_t	old_nfs_calls, new_nfs_calls;
62 
63 typedef	struct activity_data {
64 		struct	activity_data	*next;
65 		struct	activity_data	*prev;
66 		int			activity_delta;
67 		hrtime_t		snaptime;
68 } activity_data_t;
69 
70 #define	NULLACTIVITY (activity_data_t *)0
71 static	activity_data_t	*disk_act_start = NULLACTIVITY;
72 static	activity_data_t	*disk_act_end = NULLACTIVITY;
73 static	activity_data_t	*tty_act_start = NULLACTIVITY;
74 static	activity_data_t	*tty_act_end = NULLACTIVITY;
75 static	activity_data_t	*nfs_act_start = NULLACTIVITY;
76 static	activity_data_t	*nfs_act_end = NULLACTIVITY;
77 
78 struct diskinfo {
79 	struct diskinfo *next;
80 	kstat_t 	*ks;
81 	kstat_io_t 	new_kios, old_kios;
82 };
83 
84 #define	NULLDISK (struct diskinfo *)0
85 static	struct diskinfo zerodisk = { NULL, NULL };
86 static	struct diskinfo *firstdisk = NULLDISK;
87 static	struct diskinfo *lastdisk = NULLDISK;
88 static	struct diskinfo *snip = NULLDISK;
89 
90 #define	CPU_STAT(ksp, name)	(((kstat_named_t *)safe_kstat_data_lookup( \
91 				    (ksp), (name)))->value.ui64)
92 #define	DISK_DELTA(x)	(disk->new_kios.x - disk->old_kios.x)
93 #define	CPU_DELTA(x)	(CPU_STAT(&new_cpu_stats, (x)) - \
94 			    CPU_STAT(&old_cpu_stats, (x)))
95 #define	FSHIFT		8
96 #define	FSCALE		(1<<FSHIFT)
97 
98 /*
99  * Local Functions
100  */
101 static	void	init_all(void);
102 static	void	init_disks(void);
103 static	void	cpu_stats_init(void);
104 static	void	load_ave_init(void);
105 static	void	nfs_init(void);
106 static	void	conskbd_init(void);
107 static	void	consms_init(void);
108 static	int	diskinfo_load(void);
109 static	int	cpu_stats_load(void);
110 static	int	load_ave_load(void);
111 static	int	nfs_load(void);
112 static	void	fail(char *, ...);
113 static	void	safe_zalloc(void **, int, int);
114 static	void	*safe_kstat_data_lookup(kstat_t *, char *);
115 static	int	kscmp(kstat_t *, kstat_t *);
116 static	void	keep_activity_data(activity_data_t **, activity_data_t **,
117 					int *, int, hrtime_t);
118 static	int	check_activity(activity_data_t *, int, hrtime_t *, int);
119 static	void	kstat_copy(kstat_t *, kstat_t *, int);
120 
121 void
122 sysstat_init()
123 {
124 	info->pd_ttychars_sum = 0;
125 	info->pd_loadaverage = 0;
126 	info->pd_diskreads_sum = 0;
127 	info->pd_nfsreqs_sum = 0;
128 
129 	if ((kc = kstat_open()) == NULL) {
130 		fail("kstat_open(): can't open /dev/kstat");
131 	}
132 	init_all();
133 }
134 
135 static void
136 init_all(void)
137 {
138 	char *msg = "kstat_read(): can't read kstat";
139 
140 	init_disks();
141 	if (diskinfo_load() != 0) {
142 		fail(msg);
143 	}
144 
145 	cpu_stats_init();
146 	if (cpu_stats_load() != 0) {
147 		fail(msg);
148 	}
149 
150 	load_ave_init();
151 	last_load_ave_change = gethrtime();
152 	if (load_ave_load() != 0) {
153 		fail(msg);
154 	}
155 
156 	nfs_init();
157 	if (nfs_load() != 0) {
158 		fail(msg);
159 	}
160 	conskbd_init();
161 	consms_init();
162 }
163 
164 int
165 last_disk_activity(hrtime_t *hr_now, int threshold)
166 {
167 	return (check_activity(disk_act_start, info->pd_diskreads_sum, hr_now,
168 			threshold));
169 }
170 
171 int
172 last_tty_activity(hrtime_t *hr_now, int threshold)
173 {
174 	return (check_activity(tty_act_start, info->pd_ttychars_sum, hr_now,
175 			threshold));
176 }
177 
178 int
179 last_load_ave_activity(hrtime_t *hr_now)
180 {
181 	return ((*hr_now - last_load_ave_change) / NANOSEC);
182 }
183 
184 int
185 last_nfs_activity(hrtime_t *hr_now, int threshold)
186 {
187 	return (check_activity(nfs_act_start, info->pd_nfsreqs_sum, hr_now,
188 			threshold));
189 }
190 
191 static void
192 init_disks(void)
193 {
194 	struct diskinfo	*disk, *prevdisk, *comp;
195 	kstat_t		*ksp;
196 
197 	disk = &zerodisk;
198 
199 	/*
200 	 * Patch the snip in the diskinfo list (see below)
201 	 */
202 	if (snip) {
203 		lastdisk->next = snip;
204 	}
205 
206 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
207 		if (ksp->ks_type != KSTAT_TYPE_IO ||
208 				strcmp(ksp->ks_class, "disk") != 0) {
209 			continue;
210 		}
211 		prevdisk = disk;
212 		if (disk->next) {
213 			disk = disk->next;
214 		} else {
215 			safe_zalloc((void **)&disk->next,
216 				sizeof (struct diskinfo), 0);
217 			disk = disk->next;
218 			disk->next = NULLDISK;
219 		}
220 		disk->ks = ksp;
221 		(void *) memset((void *)&disk->new_kios, 0,
222 			sizeof (kstat_io_t));
223 		disk->new_kios.wlastupdate = disk->ks->ks_crtime;
224 		disk->new_kios.rlastupdate = disk->ks->ks_crtime;
225 
226 		/*
227 		 * Insertion sort on (ks_module, ks_instance, ks_name)
228 		 */
229 		comp = &zerodisk;
230 		while (kscmp(disk->ks, comp->next->ks) > 0) {
231 			comp = comp->next;
232 		}
233 		if (prevdisk != comp) {
234 			prevdisk->next = disk->next;
235 			disk->next = comp->next;
236 			comp->next = disk;
237 			disk = prevdisk;
238 		}
239 	}
240 	/*
241 	 * Put a snip in the linked list of diskinfos.  The idea:
242 	 * If there was a state change such that now there are fewer
243 	 * disks, we snip the list and retain the tail, rather than
244 	 * freeing it.  At the next state change, we clip the tail back on.
245 	 * This prevents a lot of malloc/free activity, and it's simpler.
246 	 */
247 	lastdisk = disk;
248 	snip = disk->next;
249 	disk->next = NULLDISK;
250 
251 	firstdisk = zerodisk.next;
252 }
253 
254 static int
255 diskinfo_load(void)
256 {
257 	struct diskinfo *disk;
258 
259 	for (disk = firstdisk; disk; disk = disk->next) {
260 		disk->old_kios = disk->new_kios;
261 		if (kstat_read(kc, disk->ks,
262 				(void *)&disk->new_kios) == -1) {
263 			return (1);
264 		}
265 	}
266 
267 	return (0);
268 }
269 
270 int
271 check_disks(hrtime_t *hr_now, int threshold)
272 {
273 	struct diskinfo *disk;
274 	int		delta = 0;
275 	hrtime_t	time = 0;
276 
277 	while (kstat_chain_update(kc) || diskinfo_load()) {
278 		init_all();
279 	}
280 	for (disk = firstdisk; disk; disk = disk->next) {
281 		if (time == 0) {
282 			time = disk->new_kios.wlastupdate;
283 		}
284 		delta += DISK_DELTA(reads);
285 		if (DISK_DELTA(reads) > 0) {
286 			time = MAX(time, disk->new_kios.wlastupdate);
287 		}
288 	}
289 	keep_activity_data(&disk_act_start, &disk_act_end,
290 			&info->pd_diskreads_sum, delta, time);
291 #ifdef DEBUG
292 	(void) printf("    Disk reads = %d\n", delta);
293 #endif
294 	return (check_activity(disk_act_start, info->pd_diskreads_sum, hr_now,
295 			threshold));
296 }
297 
298 static void
299 cpu_stats_init(void)
300 {
301 	kstat_t	*ksp;
302 
303 	ncpus = 0;
304 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
305 		if (strcmp(ksp->ks_module, "cpu") == 0 &&
306 		    strcmp(ksp->ks_name, "sys") == 0)
307 			ncpus++;
308 	}
309 
310 	safe_zalloc((void **)&cpu_stats_list, ncpus * sizeof (*cpu_stats_list),
311 	    1);
312 
313 	ncpus = 0;
314 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
315 		if (strcmp(ksp->ks_module, "cpu") == 0 &&
316 		    strcmp(ksp->ks_name, "sys") == 0 &&
317 		    kstat_read(kc, ksp, NULL) != -1)
318 			cpu_stats_list[ncpus++] = ksp;
319 	}
320 
321 	if (ncpus == 0)
322 		fail("can't find any cpu statistics");
323 }
324 
325 static int
326 cpu_stats_load(void)
327 {
328 	int		i, j;
329 	kstat_named_t	*nkp, *tkp;
330 
331 	tty_snaptime = 0;
332 	kstat_copy(&new_cpu_stats, &old_cpu_stats, 1);
333 
334 	/*
335 	 * Sum across all cpus
336 	 */
337 	for (i = 0; i < ncpus; i++) {
338 		if (kstat_read(kc, cpu_stats_list[i], NULL) == -1)
339 			return (1);
340 
341 		if (i == 0) {
342 			kstat_copy(cpu_stats_list[i], &new_cpu_stats, 1);
343 			continue;
344 		} else {
345 			/*
346 			 * Other CPUs' statistics are accumulated in
347 			 * new_cpu_stats, initialized at the first iteration of
348 			 * the loop.
349 			 */
350 			nkp = (kstat_named_t *)new_cpu_stats.ks_data;
351 			tkp = (kstat_named_t *)cpu_stats_list[i]->ks_data;
352 			for (j = 0; j < cpu_stats_list[i]->ks_ndata; j++)
353 				(nkp++)->value.ui64 += (tkp++)->value.ui64;
354 			tty_snaptime = MAX(tty_snaptime,
355 			    cpu_stats_list[i]->ks_snaptime);
356 		}
357 	}
358 
359 	return (0);
360 }
361 
362 int
363 check_tty(hrtime_t *hr_now, int threshold)
364 {
365 	int	delta;
366 
367 	while (kstat_chain_update(kc) || cpu_stats_load()) {
368 		init_all();
369 	}
370 	delta = CPU_DELTA("rawch") + CPU_DELTA("outch");
371 	keep_activity_data(&tty_act_start, &tty_act_end,
372 			&info->pd_ttychars_sum, delta, tty_snaptime);
373 #ifdef DEBUG
374 	(void) printf("    Tty chars = %d\n", delta);
375 #endif
376 	return (check_activity(tty_act_start, info->pd_ttychars_sum, hr_now,
377 			threshold));
378 }
379 
380 static void
381 load_ave_init(void)
382 {
383 	if ((load_ave_ksp = kstat_lookup(kc, "unix", 0, "system_misc")) ==
384 			NULL) {
385 		fail("kstat_lookup('unix', 0, 'system_misc') failed");
386 	}
387 }
388 
389 static int
390 load_ave_load(void)
391 {
392 	if (kstat_read(kc, load_ave_ksp, NULL) == -1) {
393 		return (1);
394 	}
395 	load_ave = ((kstat_named_t *)safe_kstat_data_lookup(
396 		load_ave_ksp, "avenrun_1min"))->value.l;
397 
398 	return (0);
399 }
400 
401 int
402 check_load_ave(hrtime_t *hr_now, float threshold)
403 {
404 	while (kstat_chain_update(kc) || load_ave_load()) {
405 		init_all();
406 	}
407 	info->pd_loadaverage = (double)load_ave / FSCALE;
408 	if (info->pd_loadaverage > threshold) {
409 		last_load_ave_change = load_ave_ksp->ks_snaptime;
410 	}
411 #ifdef DEBUG
412 	(void) printf("    Load average = %f\n", ((double)load_ave / FSCALE));
413 #endif
414 	return ((*hr_now - last_load_ave_change) / NANOSEC);
415 }
416 
417 static void
418 nfs_init(void)
419 {
420 	nfs_client2_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v2");
421 	nfs_client3_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v3");
422 	nfs_server2_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v2");
423 	nfs_server3_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v3");
424 }
425 
426 static int
427 nfs_load(void)
428 {
429 	kstat_named_t	*kstat_ptr;
430 	int		index;
431 	uint64_t	total_calls = 0;
432 	uint64_t	getattr_calls = 0;
433 	uint64_t	null_calls = 0;
434 	uint64_t	access_calls = 0;
435 
436 	if (!nfs_client2_kstat && !nfs_client3_kstat && !nfs_server2_kstat &&
437 			!nfs_server3_kstat) {
438 		return (0);
439 	}
440 
441 	/*
442 	 * NFS client "getattr", NFS3 client "access", and NFS server "null"
443 	 * requests are excluded from consideration.
444 	 */
445 	if (nfs_client2_kstat) {
446 		if (kstat_read(kc, nfs_client2_kstat, NULL) == -1) {
447 			return (1);
448 		}
449 		kstat_ptr = KSTAT_NAMED_PTR(nfs_client2_kstat);
450 		for (index = 0; index < nfs_client2_kstat->ks_ndata; index++) {
451 			total_calls += kstat_ptr[index].value.ui64;
452 		}
453 		getattr_calls =
454 			((kstat_named_t *)safe_kstat_data_lookup(
455 			nfs_client2_kstat, "getattr"))->value.ui64;
456 	}
457 
458 	if (nfs_client3_kstat) {
459 		if (kstat_read(kc, nfs_client3_kstat, NULL) == -1) {
460 			return (1);
461 		}
462 		kstat_ptr = KSTAT_NAMED_PTR(nfs_client3_kstat);
463 		for (index = 0; index < nfs_client3_kstat->ks_ndata; index++) {
464 			total_calls += kstat_ptr[index].value.ui64;
465 		}
466 		getattr_calls +=
467 			((kstat_named_t *)safe_kstat_data_lookup(
468 			nfs_client3_kstat, "getattr"))->value.ui64;
469 		access_calls =
470 			((kstat_named_t *)safe_kstat_data_lookup(
471 			nfs_client3_kstat, "access"))->value.ui64;
472 	}
473 
474 	if (nfs_server2_kstat) {
475 		if (kstat_read(kc, nfs_server2_kstat, NULL) == -1) {
476 			return (1);
477 		}
478 		kstat_ptr = KSTAT_NAMED_PTR(nfs_server2_kstat);
479 		for (index = 0; index < nfs_server2_kstat->ks_ndata; index++) {
480 			total_calls += kstat_ptr[index].value.ui64;
481 		}
482 		null_calls =
483 			((kstat_named_t *)safe_kstat_data_lookup(
484 			nfs_server2_kstat, "null"))->value.ui64;
485 	}
486 
487 	if (nfs_server3_kstat) {
488 		if (kstat_read(kc, nfs_server3_kstat, NULL) == -1) {
489 			return (1);
490 		}
491 		kstat_ptr = KSTAT_NAMED_PTR(nfs_server3_kstat);
492 		for (index = 0; index < nfs_server3_kstat->ks_ndata; index++) {
493 			total_calls += kstat_ptr[index].value.ui64;
494 		}
495 		null_calls +=
496 			((kstat_named_t *)safe_kstat_data_lookup(
497 			nfs_server3_kstat, "null"))->value.ui64;
498 	}
499 
500 	old_nfs_calls = new_nfs_calls;
501 	new_nfs_calls = total_calls -
502 		(getattr_calls + access_calls + null_calls);
503 
504 	return (0);
505 }
506 
507 int
508 check_nfs(hrtime_t *hr_now, int threshold)
509 {
510 	int		delta;
511 	hrtime_t	time = 0;
512 
513 	while (kstat_chain_update(kc) || nfs_load()) {
514 		init_all();
515 	}
516 
517 	if (!nfs_client2_kstat && !nfs_client3_kstat && !nfs_server2_kstat &&
518 			!nfs_server3_kstat) {
519 		return (0);
520 	}
521 
522 	if (nfs_client2_kstat) {
523 		time = MAX(time, nfs_client2_kstat->ks_snaptime);
524 	}
525 	if (nfs_client3_kstat) {
526 		time = MAX(time, nfs_client3_kstat->ks_snaptime);
527 	}
528 	if (nfs_server2_kstat) {
529 		time = MAX(time, nfs_server2_kstat->ks_snaptime);
530 	}
531 	if (nfs_server3_kstat) {
532 		time = MAX(time, nfs_server3_kstat->ks_snaptime);
533 	}
534 	delta = (int)(new_nfs_calls - old_nfs_calls);
535 	keep_activity_data(&nfs_act_start, &nfs_act_end,
536 			&info->pd_nfsreqs_sum, delta, time);
537 #ifdef DEBUG
538 	(void) printf("    NFS requests = %d\n", delta);
539 #endif
540 	return (check_activity(nfs_act_start, info->pd_nfsreqs_sum, hr_now,
541 			threshold));
542 }
543 
544 static void
545 conskbd_init(void)
546 {
547 	conskbd_ksp = kstat_lookup(kc, "conskbd", 0, "activity");
548 }
549 
550 /*
551  * Return the number of seconds since the last keystroke on console keyboard.
552  * Caller responsible for error reporting.
553  */
554 long
555 conskbd_idle_time(void)
556 {
557 	void *p;
558 
559 	if (conskbd_ksp == NULL || kstat_read(kc, conskbd_ksp, NULL) == -1 ||
560 	    (p = kstat_data_lookup(conskbd_ksp, "idle_sec")) == NULL)
561 		return ((time_t)-1);
562 
563 	return (((kstat_named_t *)p)->value.l);
564 }
565 
566 static void
567 consms_init(void)
568 {
569 	consms_ksp = kstat_lookup(kc, "consms", 0, "activity");
570 }
571 
572 /*
573  * Return the number of seconds since the most recent action (movement or
574  * click) of the console mouse.  Caller responsible for error reporting.
575  */
576 long
577 consms_idle_time(void)
578 {
579 	void *p;
580 
581 	if (consms_ksp == NULL || kstat_read(kc, consms_ksp, NULL) == -1 ||
582 	    (p = kstat_data_lookup(consms_ksp, "idle_sec")) == NULL)
583 		return ((time_t)-1);
584 
585 	return (((kstat_named_t *)p)->value.l);
586 }
587 
588 static void
589 fail(char *fmt, ...)
590 {
591 	char new_fmt[256];
592 	va_list	args;
593 	size_t len;
594 
595 	len = sizeof (new_fmt);
596 	va_start(args, fmt);
597 	if (snprintf(new_fmt, len, "powerd: %s", fmt) > len)
598 		syslog(LOG_ERR, "powerd: syslog message too large");
599 	else
600 		vsyslog(LOG_ERR, new_fmt, args);
601 	va_end(args);
602 
603 	thr_exit((void *) 0);
604 }
605 
606 static void
607 safe_zalloc(void **ptr, int size, int free_first)
608 {
609 	if (free_first && *ptr != NULL) {
610 		free(*ptr);
611 	}
612 	if ((*ptr = (void *) malloc(size)) == NULL) {
613 		fail("malloc failed");
614 	}
615 	(void *) memset(*ptr, 0, size);
616 }
617 
618 static void *
619 safe_kstat_data_lookup(kstat_t *ksp, char *name)
620 {
621 	void *fp = kstat_data_lookup(ksp, name);
622 
623 	if (fp == NULL) {
624 		fail("kstat_data_lookup('%s', '%s') failed",
625 			ksp->ks_name, name);
626 	}
627 	return (fp);
628 }
629 
630 static int
631 kscmp(kstat_t *ks1, kstat_t *ks2)
632 {
633 	int cmp;
634 
635 	cmp = strcmp(ks1->ks_module, ks2->ks_module);
636 	if (cmp != 0) {
637 		return (cmp);
638 	}
639 	cmp = ks1->ks_instance - ks2->ks_instance;
640 	if (cmp != 0) {
641 		return (cmp);
642 	}
643 	return (strcmp(ks1->ks_name, ks2->ks_name));
644 }
645 
646 static void
647 keep_activity_data(activity_data_t **act_start, activity_data_t **act_end,
648 			int *delta_sum, int delta, hrtime_t time)
649 {
650 	activity_data_t *node = NULLACTIVITY;
651 	hrtime_t	hr_now;
652 	int		idle_time = info->pd_idle_time * 60;
653 
654 	/*
655 	 * Add new nodes to the beginning of the list.
656 	 */
657 	safe_zalloc((void **)&node, sizeof (activity_data_t), 0);
658 	node->activity_delta = delta;
659 	*delta_sum += delta;
660 	node->snaptime = time;
661 	node->next = *act_start;
662 	if (*act_start == NULLACTIVITY) {
663 		*act_end = node;
664 	} else {
665 		(*act_start)->prev = node;
666 	}
667 	*act_start = node;
668 
669 	/*
670 	 * Remove nodes that are time-stamped later than the idle time.
671 	 */
672 	hr_now = gethrtime();
673 	node = *act_end;
674 	while ((int)((hr_now - node->snaptime) / NANOSEC) > idle_time &&
675 			node->prev != NULLACTIVITY) {
676 		*delta_sum -= node->activity_delta;
677 		*act_end = node->prev;
678 		(*act_end)->next = NULLACTIVITY;
679 		free(node);
680 		node = *act_end;
681 	}
682 }
683 
684 static int
685 check_activity(activity_data_t *act_start, int delta_sum, hrtime_t *time,
686 			int thold)
687 {
688 	activity_data_t	*node;
689 	int		sum = 0;
690 	int		idle_time = info->pd_idle_time * 60;
691 
692 	/*
693 	 * No need to walk the list if the sum of the deltas are not greater
694 	 * than the threshold value.
695 	 */
696 	if (delta_sum <= thold) {
697 		return (idle_time);
698 	}
699 
700 	/*
701 	 * Walk through the list and add up the activity deltas.  When the
702 	 * sum is greater than the threshold value, difference of current
703 	 * time and the snaptime of that node will give us the idle time.
704 	 */
705 	node = act_start;
706 	while (node->next != NULLACTIVITY) {
707 		sum += node->activity_delta;
708 		if (sum > thold) {
709 			return ((*time - node->snaptime) / NANOSEC);
710 		}
711 		node = node->next;
712 	}
713 	sum += node->activity_delta;
714 	if (sum > thold) {
715 		return ((*time - node->snaptime) / NANOSEC);
716 	}
717 
718 	return (idle_time);
719 }
720 
721 static void
722 kstat_copy(kstat_t *src, kstat_t *dst, int fr)
723 {
724 	if (fr)
725 		free(dst->ks_data);
726 
727 	*dst = *src;
728 	if (src->ks_data != NULL) {
729 		safe_zalloc(&dst->ks_data, src->ks_data_size, 0);
730 		(void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size);
731 	} else {
732 		dst->ks_data = NULL;
733 		dst->ks_data_size = 0;
734 	}
735 }
736