xref: /illumos-gate/usr/src/cmd/power/sysstat.c (revision 241c90a06e8d1708235651863df515a2d522a03a)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <unistd.h>				/* sleep() */
31 #include <string.h>
32 #include <errno.h>
33 #include <syslog.h>
34 #include <thread.h>
35 #include <time.h>
36 #include <kstat.h>
37 #include <sys/sysinfo.h>
38 #include <sys/sysmacros.h>
39 #include "powerd.h"
40 
41 /*
42  * External Variables
43  */
44 extern	pwr_info_t	*info;
45 
46 /*
47  * State Variables
48  */
49 static	kstat_ctl_t	*kc;			/* libkstat cookie */
50 static	int		ncpus;
51 static	kstat_t		**cpu_stats_list = NULL;
52 static	kstat_t		old_cpu_stats, new_cpu_stats;
53 static	hrtime_t	tty_snaptime;
54 static	kstat_t		*load_ave_ksp;
55 static	ulong_t		load_ave;
56 static	hrtime_t	last_load_ave_change;
57 static	kstat_t		*conskbd_ksp, *consms_ksp;
58 static	kstat_t		*nfs_client2_kstat, *nfs_client3_kstat;
59 static	kstat_t		*nfs_server2_kstat, *nfs_server3_kstat;
60 static	uint64_t	old_nfs_calls, new_nfs_calls;
61 
62 typedef	struct activity_data {
63 		struct	activity_data	*next;
64 		struct	activity_data	*prev;
65 		int			activity_delta;
66 		hrtime_t		snaptime;
67 } activity_data_t;
68 
69 #define	NULLACTIVITY (activity_data_t *)0
70 static	activity_data_t	*disk_act_start = NULLACTIVITY;
71 static	activity_data_t	*disk_act_end = NULLACTIVITY;
72 static	activity_data_t	*tty_act_start = NULLACTIVITY;
73 static	activity_data_t	*tty_act_end = NULLACTIVITY;
74 static	activity_data_t	*nfs_act_start = NULLACTIVITY;
75 static	activity_data_t	*nfs_act_end = NULLACTIVITY;
76 
77 struct diskinfo {
78 	struct diskinfo *next;
79 	kstat_t 	*ks;
80 	kstat_io_t 	new_kios, old_kios;
81 };
82 
83 #define	NULLDISK (struct diskinfo *)0
84 static	struct diskinfo zerodisk = { NULL, NULL };
85 static	struct diskinfo *firstdisk = NULLDISK;
86 static	struct diskinfo *lastdisk = NULLDISK;
87 static	struct diskinfo *snip = NULLDISK;
88 
89 #define	CPU_STAT(ksp, name)	(((kstat_named_t *)safe_kstat_data_lookup( \
90 				    (ksp), (name)))->value.ui64)
91 #define	DISK_DELTA(x)	(disk->new_kios.x - disk->old_kios.x)
92 #define	CPU_DELTA(x)	(CPU_STAT(&new_cpu_stats, (x)) - \
93 			    CPU_STAT(&old_cpu_stats, (x)))
94 #define	FSHIFT		8
95 #define	FSCALE		(1<<FSHIFT)
96 
97 /*
98  * Local Functions
99  */
100 static	void	init_all(void);
101 static	void	init_disks(void);
102 static	void	cpu_stats_init(void);
103 static	void	load_ave_init(void);
104 static	void	nfs_init(void);
105 static	void	conskbd_init(void);
106 static	void	consms_init(void);
107 static	int	diskinfo_load(void);
108 static	int	cpu_stats_load(void);
109 static	int	load_ave_load(void);
110 static	int	nfs_load(void);
111 static	void	fail(char *, ...);
112 static	void	safe_zalloc(void **, int, int);
113 static	void	*safe_kstat_data_lookup(kstat_t *, char *);
114 static	int	kscmp(kstat_t *, kstat_t *);
115 static	void	keep_activity_data(activity_data_t **, activity_data_t **,
116 					int *, int, hrtime_t);
117 static	int	check_activity(activity_data_t *, int, hrtime_t *, int);
118 static	void	kstat_copy(kstat_t *, kstat_t *, int);
119 
120 void
121 sysstat_init()
122 {
123 	info->pd_ttychars_sum = 0;
124 	info->pd_loadaverage = 0;
125 	info->pd_diskreads_sum = 0;
126 	info->pd_nfsreqs_sum = 0;
127 
128 	if ((kc = kstat_open()) == NULL) {
129 		fail("kstat_open(): can't open /dev/kstat");
130 	}
131 	init_all();
132 }
133 
134 static void
135 init_all(void)
136 {
137 	char *msg = "kstat_read(): can't read kstat";
138 
139 	init_disks();
140 	if (diskinfo_load() != 0) {
141 		fail(msg);
142 	}
143 
144 	cpu_stats_init();
145 	if (cpu_stats_load() != 0) {
146 		fail(msg);
147 	}
148 
149 	load_ave_init();
150 	last_load_ave_change = gethrtime();
151 	if (load_ave_load() != 0) {
152 		fail(msg);
153 	}
154 
155 	nfs_init();
156 	if (nfs_load() != 0) {
157 		fail(msg);
158 	}
159 	conskbd_init();
160 	consms_init();
161 }
162 
163 int
164 last_disk_activity(hrtime_t *hr_now, int threshold)
165 {
166 	return (check_activity(disk_act_start, info->pd_diskreads_sum, hr_now,
167 			threshold));
168 }
169 
170 int
171 last_tty_activity(hrtime_t *hr_now, int threshold)
172 {
173 	return (check_activity(tty_act_start, info->pd_ttychars_sum, hr_now,
174 			threshold));
175 }
176 
177 int
178 last_load_ave_activity(hrtime_t *hr_now)
179 {
180 	return ((*hr_now - last_load_ave_change) / NANOSEC);
181 }
182 
183 int
184 last_nfs_activity(hrtime_t *hr_now, int threshold)
185 {
186 	return (check_activity(nfs_act_start, info->pd_nfsreqs_sum, hr_now,
187 			threshold));
188 }
189 
190 static void
191 init_disks(void)
192 {
193 	struct diskinfo	*disk, *prevdisk, *comp;
194 	kstat_t		*ksp;
195 
196 	disk = &zerodisk;
197 
198 	/*
199 	 * Patch the snip in the diskinfo list (see below)
200 	 */
201 	if (snip) {
202 		lastdisk->next = snip;
203 	}
204 
205 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
206 		if (ksp->ks_type != KSTAT_TYPE_IO ||
207 				strcmp(ksp->ks_class, "disk") != 0) {
208 			continue;
209 		}
210 		prevdisk = disk;
211 		if (disk->next) {
212 			disk = disk->next;
213 		} else {
214 			safe_zalloc((void **)&disk->next,
215 				sizeof (struct diskinfo), 0);
216 			disk = disk->next;
217 			disk->next = NULLDISK;
218 		}
219 		disk->ks = ksp;
220 		(void *) memset((void *)&disk->new_kios, 0,
221 			sizeof (kstat_io_t));
222 		disk->new_kios.wlastupdate = disk->ks->ks_crtime;
223 		disk->new_kios.rlastupdate = disk->ks->ks_crtime;
224 
225 		/*
226 		 * Insertion sort on (ks_module, ks_instance, ks_name)
227 		 */
228 		comp = &zerodisk;
229 		while (kscmp(disk->ks, comp->next->ks) > 0) {
230 			comp = comp->next;
231 		}
232 		if (prevdisk != comp) {
233 			prevdisk->next = disk->next;
234 			disk->next = comp->next;
235 			comp->next = disk;
236 			disk = prevdisk;
237 		}
238 	}
239 	/*
240 	 * Put a snip in the linked list of diskinfos.  The idea:
241 	 * If there was a state change such that now there are fewer
242 	 * disks, we snip the list and retain the tail, rather than
243 	 * freeing it.  At the next state change, we clip the tail back on.
244 	 * This prevents a lot of malloc/free activity, and it's simpler.
245 	 */
246 	lastdisk = disk;
247 	snip = disk->next;
248 	disk->next = NULLDISK;
249 
250 	firstdisk = zerodisk.next;
251 }
252 
253 static int
254 diskinfo_load(void)
255 {
256 	struct diskinfo *disk;
257 
258 	for (disk = firstdisk; disk; disk = disk->next) {
259 		disk->old_kios = disk->new_kios;
260 		if (kstat_read(kc, disk->ks,
261 				(void *)&disk->new_kios) == -1) {
262 			return (1);
263 		}
264 	}
265 
266 	return (0);
267 }
268 
269 int
270 check_disks(hrtime_t *hr_now, int threshold)
271 {
272 	struct diskinfo *disk;
273 	int		delta = 0;
274 	hrtime_t	time = 0;
275 
276 	while (kstat_chain_update(kc) || diskinfo_load()) {
277 		init_all();
278 	}
279 	for (disk = firstdisk; disk; disk = disk->next) {
280 		if (time == 0) {
281 			time = disk->new_kios.wlastupdate;
282 		}
283 		delta += DISK_DELTA(reads);
284 		if (DISK_DELTA(reads) > 0) {
285 			time = MAX(time, disk->new_kios.wlastupdate);
286 		}
287 	}
288 	keep_activity_data(&disk_act_start, &disk_act_end,
289 			&info->pd_diskreads_sum, delta, time);
290 #ifdef DEBUG
291 	(void) printf("    Disk reads = %d\n", delta);
292 #endif
293 	return (check_activity(disk_act_start, info->pd_diskreads_sum, hr_now,
294 			threshold));
295 }
296 
297 static void
298 cpu_stats_init(void)
299 {
300 	kstat_t	*ksp;
301 
302 	ncpus = 0;
303 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
304 		if (strcmp(ksp->ks_module, "cpu") == 0 &&
305 		    strcmp(ksp->ks_name, "sys") == 0)
306 			ncpus++;
307 	}
308 
309 	safe_zalloc((void **)&cpu_stats_list, ncpus * sizeof (*cpu_stats_list),
310 	    1);
311 
312 	ncpus = 0;
313 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
314 		if (strcmp(ksp->ks_module, "cpu") == 0 &&
315 		    strcmp(ksp->ks_name, "sys") == 0 &&
316 		    kstat_read(kc, ksp, NULL) != -1)
317 			cpu_stats_list[ncpus++] = ksp;
318 	}
319 
320 	if (ncpus == 0)
321 		fail("can't find any cpu statistics");
322 }
323 
324 static int
325 cpu_stats_load(void)
326 {
327 	int		i, j;
328 	kstat_named_t	*nkp, *tkp;
329 
330 	tty_snaptime = 0;
331 	kstat_copy(&new_cpu_stats, &old_cpu_stats, 1);
332 
333 	/*
334 	 * Sum across all cpus
335 	 */
336 	for (i = 0; i < ncpus; i++) {
337 		if (kstat_read(kc, cpu_stats_list[i], NULL) == -1)
338 			return (1);
339 
340 		if (i == 0) {
341 			kstat_copy(cpu_stats_list[i], &new_cpu_stats, 1);
342 			continue;
343 		} else {
344 			/*
345 			 * Other CPUs' statistics are accumulated in
346 			 * new_cpu_stats, initialized at the first iteration of
347 			 * the loop.
348 			 */
349 			nkp = (kstat_named_t *)new_cpu_stats.ks_data;
350 			tkp = (kstat_named_t *)cpu_stats_list[i]->ks_data;
351 			for (j = 0; j < cpu_stats_list[i]->ks_ndata; j++)
352 				(nkp++)->value.ui64 += (tkp++)->value.ui64;
353 			tty_snaptime = MAX(tty_snaptime,
354 			    cpu_stats_list[i]->ks_snaptime);
355 		}
356 	}
357 
358 	return (0);
359 }
360 
361 int
362 check_tty(hrtime_t *hr_now, int threshold)
363 {
364 	int	delta;
365 
366 	while (kstat_chain_update(kc) || cpu_stats_load()) {
367 		init_all();
368 	}
369 	delta = CPU_DELTA("rawch") + CPU_DELTA("outch");
370 	keep_activity_data(&tty_act_start, &tty_act_end,
371 			&info->pd_ttychars_sum, delta, tty_snaptime);
372 #ifdef DEBUG
373 	(void) printf("    Tty chars = %d\n", delta);
374 #endif
375 	return (check_activity(tty_act_start, info->pd_ttychars_sum, hr_now,
376 			threshold));
377 }
378 
379 static void
380 load_ave_init(void)
381 {
382 	if ((load_ave_ksp = kstat_lookup(kc, "unix", 0, "system_misc")) ==
383 			NULL) {
384 		fail("kstat_lookup('unix', 0, 'system_misc') failed");
385 	}
386 }
387 
388 static int
389 load_ave_load(void)
390 {
391 	if (kstat_read(kc, load_ave_ksp, NULL) == -1) {
392 		return (1);
393 	}
394 	load_ave = ((kstat_named_t *)safe_kstat_data_lookup(
395 		load_ave_ksp, "avenrun_1min"))->value.l;
396 
397 	return (0);
398 }
399 
400 int
401 check_load_ave(hrtime_t *hr_now, float threshold)
402 {
403 	while (kstat_chain_update(kc) || load_ave_load()) {
404 		init_all();
405 	}
406 	info->pd_loadaverage = (double)load_ave / FSCALE;
407 	if (info->pd_loadaverage > threshold) {
408 		last_load_ave_change = load_ave_ksp->ks_snaptime;
409 	}
410 #ifdef DEBUG
411 	(void) printf("    Load average = %f\n", ((double)load_ave / FSCALE));
412 #endif
413 	return ((*hr_now - last_load_ave_change) / NANOSEC);
414 }
415 
416 static void
417 nfs_init(void)
418 {
419 	nfs_client2_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v2");
420 	nfs_client3_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v3");
421 	nfs_server2_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v2");
422 	nfs_server3_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v3");
423 }
424 
425 static int
426 nfs_load(void)
427 {
428 	kstat_named_t	*kstat_ptr;
429 	int		index;
430 	uint64_t	total_calls = 0;
431 	uint64_t	getattr_calls = 0;
432 	uint64_t	null_calls = 0;
433 	uint64_t	access_calls = 0;
434 
435 	if (!nfs_client2_kstat && !nfs_client3_kstat && !nfs_server2_kstat &&
436 			!nfs_server3_kstat) {
437 		return (0);
438 	}
439 
440 	/*
441 	 * NFS client "getattr", NFS3 client "access", and NFS server "null"
442 	 * requests are excluded from consideration.
443 	 */
444 	if (nfs_client2_kstat) {
445 		if (kstat_read(kc, nfs_client2_kstat, NULL) == -1) {
446 			return (1);
447 		}
448 		kstat_ptr = KSTAT_NAMED_PTR(nfs_client2_kstat);
449 		for (index = 0; index < nfs_client2_kstat->ks_ndata; index++) {
450 			total_calls += kstat_ptr[index].value.ui64;
451 		}
452 		getattr_calls =
453 			((kstat_named_t *)safe_kstat_data_lookup(
454 			nfs_client2_kstat, "getattr"))->value.ui64;
455 	}
456 
457 	if (nfs_client3_kstat) {
458 		if (kstat_read(kc, nfs_client3_kstat, NULL) == -1) {
459 			return (1);
460 		}
461 		kstat_ptr = KSTAT_NAMED_PTR(nfs_client3_kstat);
462 		for (index = 0; index < nfs_client3_kstat->ks_ndata; index++) {
463 			total_calls += kstat_ptr[index].value.ui64;
464 		}
465 		getattr_calls +=
466 			((kstat_named_t *)safe_kstat_data_lookup(
467 			nfs_client3_kstat, "getattr"))->value.ui64;
468 		access_calls =
469 			((kstat_named_t *)safe_kstat_data_lookup(
470 			nfs_client3_kstat, "access"))->value.ui64;
471 	}
472 
473 	if (nfs_server2_kstat) {
474 		if (kstat_read(kc, nfs_server2_kstat, NULL) == -1) {
475 			return (1);
476 		}
477 		kstat_ptr = KSTAT_NAMED_PTR(nfs_server2_kstat);
478 		for (index = 0; index < nfs_server2_kstat->ks_ndata; index++) {
479 			total_calls += kstat_ptr[index].value.ui64;
480 		}
481 		null_calls =
482 			((kstat_named_t *)safe_kstat_data_lookup(
483 			nfs_server2_kstat, "null"))->value.ui64;
484 	}
485 
486 	if (nfs_server3_kstat) {
487 		if (kstat_read(kc, nfs_server3_kstat, NULL) == -1) {
488 			return (1);
489 		}
490 		kstat_ptr = KSTAT_NAMED_PTR(nfs_server3_kstat);
491 		for (index = 0; index < nfs_server3_kstat->ks_ndata; index++) {
492 			total_calls += kstat_ptr[index].value.ui64;
493 		}
494 		null_calls +=
495 			((kstat_named_t *)safe_kstat_data_lookup(
496 			nfs_server3_kstat, "null"))->value.ui64;
497 	}
498 
499 	old_nfs_calls = new_nfs_calls;
500 	new_nfs_calls = total_calls -
501 		(getattr_calls + access_calls + null_calls);
502 
503 	return (0);
504 }
505 
506 int
507 check_nfs(hrtime_t *hr_now, int threshold)
508 {
509 	int		delta;
510 	hrtime_t	time = 0;
511 
512 	while (kstat_chain_update(kc) || nfs_load()) {
513 		init_all();
514 	}
515 
516 	if (!nfs_client2_kstat && !nfs_client3_kstat && !nfs_server2_kstat &&
517 			!nfs_server3_kstat) {
518 		return (0);
519 	}
520 
521 	if (nfs_client2_kstat) {
522 		time = MAX(time, nfs_client2_kstat->ks_snaptime);
523 	}
524 	if (nfs_client3_kstat) {
525 		time = MAX(time, nfs_client3_kstat->ks_snaptime);
526 	}
527 	if (nfs_server2_kstat) {
528 		time = MAX(time, nfs_server2_kstat->ks_snaptime);
529 	}
530 	if (nfs_server3_kstat) {
531 		time = MAX(time, nfs_server3_kstat->ks_snaptime);
532 	}
533 	delta = (int)(new_nfs_calls - old_nfs_calls);
534 	keep_activity_data(&nfs_act_start, &nfs_act_end,
535 			&info->pd_nfsreqs_sum, delta, time);
536 #ifdef DEBUG
537 	(void) printf("    NFS requests = %d\n", delta);
538 #endif
539 	return (check_activity(nfs_act_start, info->pd_nfsreqs_sum, hr_now,
540 			threshold));
541 }
542 
543 static void
544 conskbd_init(void)
545 {
546 	conskbd_ksp = kstat_lookup(kc, "conskbd", 0, "activity");
547 }
548 
549 /*
550  * Return the number of seconds since the last keystroke on console keyboard.
551  * Caller responsible for error reporting.
552  */
553 long
554 conskbd_idle_time(void)
555 {
556 	void *p;
557 
558 	if (conskbd_ksp == NULL || kstat_read(kc, conskbd_ksp, NULL) == -1 ||
559 	    (p = kstat_data_lookup(conskbd_ksp, "idle_sec")) == NULL)
560 		return ((time_t)-1);
561 
562 	return (((kstat_named_t *)p)->value.l);
563 }
564 
565 static void
566 consms_init(void)
567 {
568 	consms_ksp = kstat_lookup(kc, "consms", 0, "activity");
569 }
570 
571 /*
572  * Return the number of seconds since the most recent action (movement or
573  * click) of the console mouse.  Caller responsible for error reporting.
574  */
575 long
576 consms_idle_time(void)
577 {
578 	void *p;
579 
580 	if (consms_ksp == NULL || kstat_read(kc, consms_ksp, NULL) == -1 ||
581 	    (p = kstat_data_lookup(consms_ksp, "idle_sec")) == NULL)
582 		return ((time_t)-1);
583 
584 	return (((kstat_named_t *)p)->value.l);
585 }
586 
587 static void
588 fail(char *fmt, ...)
589 {
590 	char new_fmt[256];
591 	const char *fmtptr = new_fmt;
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, fmtptr, 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