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