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.
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.
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]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <unistd.h> /* sleep() */
37 #include <sys/sysinfo.h>
38 #include <sys/sysmacros.h>
44 extern pwr_info_t
*info
;
49 static kstat_ctl_t
*kc
; /* libkstat cookie */
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
;
62 typedef struct activity_data
{
63 struct activity_data
*next
;
64 struct activity_data
*prev
;
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
;
78 struct diskinfo
*next
;
80 kstat_io_t new_kios
, old_kios
;
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
;
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)))
95 #define FSCALE (1<<FSHIFT)
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);
123 info
->pd_ttychars_sum
= 0;
124 info
->pd_loadaverage
= 0;
125 info
->pd_diskreads_sum
= 0;
126 info
->pd_nfsreqs_sum
= 0;
128 if ((kc
= kstat_open()) == NULL
) {
129 fail("kstat_open(): can't open /dev/kstat");
137 char *msg
= "kstat_read(): can't read kstat";
140 if (diskinfo_load() != 0) {
145 if (cpu_stats_load() != 0) {
150 last_load_ave_change
= gethrtime();
151 if (load_ave_load() != 0) {
156 if (nfs_load() != 0) {
164 last_disk_activity(hrtime_t
*hr_now
, int threshold
)
166 return (check_activity(disk_act_start
, info
->pd_diskreads_sum
, hr_now
,
171 last_tty_activity(hrtime_t
*hr_now
, int threshold
)
173 return (check_activity(tty_act_start
, info
->pd_ttychars_sum
, hr_now
,
178 last_load_ave_activity(hrtime_t
*hr_now
)
180 return ((*hr_now
- last_load_ave_change
) / NANOSEC
);
184 last_nfs_activity(hrtime_t
*hr_now
, int threshold
)
186 return (check_activity(nfs_act_start
, info
->pd_nfsreqs_sum
, hr_now
,
193 struct diskinfo
*disk
, *prevdisk
, *comp
;
199 * Patch the snip in the diskinfo list (see below)
202 lastdisk
->next
= snip
;
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) {
214 safe_zalloc((void **)&disk
->next
,
215 sizeof (struct diskinfo
), 0);
217 disk
->next
= NULLDISK
;
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
;
226 * Insertion sort on (ks_module, ks_instance, ks_name)
229 while (kscmp(disk
->ks
, comp
->next
->ks
) > 0) {
232 if (prevdisk
!= comp
) {
233 prevdisk
->next
= disk
->next
;
234 disk
->next
= comp
->next
;
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.
248 disk
->next
= NULLDISK
;
250 firstdisk
= zerodisk
.next
;
256 struct diskinfo
*disk
;
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) {
270 check_disks(hrtime_t
*hr_now
, int threshold
)
272 struct diskinfo
*disk
;
276 while (kstat_chain_update(kc
) || diskinfo_load()) {
279 for (disk
= firstdisk
; disk
; disk
= disk
->next
) {
281 time
= disk
->new_kios
.wlastupdate
;
283 delta
+= DISK_DELTA(reads
);
284 if (DISK_DELTA(reads
) > 0) {
285 time
= MAX(time
, disk
->new_kios
.wlastupdate
);
288 keep_activity_data(&disk_act_start
, &disk_act_end
,
289 &info
->pd_diskreads_sum
, delta
, time
);
291 (void) printf(" Disk reads = %d\n", delta
);
293 return (check_activity(disk_act_start
, info
->pd_diskreads_sum
, hr_now
,
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)
309 safe_zalloc((void **)&cpu_stats_list
, ncpus
* sizeof (*cpu_stats_list
),
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
;
321 fail("can't find any cpu statistics");
328 kstat_named_t
*nkp
, *tkp
;
331 kstat_copy(&new_cpu_stats
, &old_cpu_stats
, 1);
334 * Sum across all cpus
336 for (i
= 0; i
< ncpus
; i
++) {
337 if (kstat_read(kc
, cpu_stats_list
[i
], NULL
) == -1)
341 kstat_copy(cpu_stats_list
[i
], &new_cpu_stats
, 1);
345 * Other CPUs' statistics are accumulated in
346 * new_cpu_stats, initialized at the first iteration of
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
);
362 check_tty(hrtime_t
*hr_now
, int threshold
)
366 while (kstat_chain_update(kc
) || cpu_stats_load()) {
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
);
373 (void) printf(" Tty chars = %d\n", delta
);
375 return (check_activity(tty_act_start
, info
->pd_ttychars_sum
, hr_now
,
382 if ((load_ave_ksp
= kstat_lookup(kc
, "unix", 0, "system_misc")) ==
384 fail("kstat_lookup('unix', 0, 'system_misc') failed");
391 if (kstat_read(kc
, load_ave_ksp
, NULL
) == -1) {
394 load_ave
= ((kstat_named_t
*)safe_kstat_data_lookup(
395 load_ave_ksp
, "avenrun_1min"))->value
.l
;
401 check_load_ave(hrtime_t
*hr_now
, float threshold
)
403 while (kstat_chain_update(kc
) || load_ave_load()) {
406 info
->pd_loadaverage
= (double)load_ave
/ FSCALE
;
407 if (info
->pd_loadaverage
> threshold
) {
408 last_load_ave_change
= load_ave_ksp
->ks_snaptime
;
411 (void) printf(" Load average = %f\n", ((double)load_ave
/ FSCALE
));
413 return ((*hr_now
- last_load_ave_change
) / NANOSEC
);
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");
428 kstat_named_t
*kstat_ptr
;
430 uint64_t total_calls
= 0;
431 uint64_t getattr_calls
= 0;
432 uint64_t null_calls
= 0;
433 uint64_t access_calls
= 0;
435 if (!nfs_client2_kstat
&& !nfs_client3_kstat
&& !nfs_server2_kstat
&&
436 !nfs_server3_kstat
) {
441 * NFS client "getattr", NFS3 client "access", and NFS server "null"
442 * requests are excluded from consideration.
444 if (nfs_client2_kstat
) {
445 if (kstat_read(kc
, nfs_client2_kstat
, NULL
) == -1) {
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
;
453 ((kstat_named_t
*)safe_kstat_data_lookup(
454 nfs_client2_kstat
, "getattr"))->value
.ui64
;
457 if (nfs_client3_kstat
) {
458 if (kstat_read(kc
, nfs_client3_kstat
, NULL
) == -1) {
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
;
466 ((kstat_named_t
*)safe_kstat_data_lookup(
467 nfs_client3_kstat
, "getattr"))->value
.ui64
;
469 ((kstat_named_t
*)safe_kstat_data_lookup(
470 nfs_client3_kstat
, "access"))->value
.ui64
;
473 if (nfs_server2_kstat
) {
474 if (kstat_read(kc
, nfs_server2_kstat
, NULL
) == -1) {
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
;
482 ((kstat_named_t
*)safe_kstat_data_lookup(
483 nfs_server2_kstat
, "null"))->value
.ui64
;
486 if (nfs_server3_kstat
) {
487 if (kstat_read(kc
, nfs_server3_kstat
, NULL
) == -1) {
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
;
495 ((kstat_named_t
*)safe_kstat_data_lookup(
496 nfs_server3_kstat
, "null"))->value
.ui64
;
499 old_nfs_calls
= new_nfs_calls
;
500 new_nfs_calls
= total_calls
-
501 (getattr_calls
+ access_calls
+ null_calls
);
507 check_nfs(hrtime_t
*hr_now
, int threshold
)
512 while (kstat_chain_update(kc
) || nfs_load()) {
516 if (!nfs_client2_kstat
&& !nfs_client3_kstat
&& !nfs_server2_kstat
&&
517 !nfs_server3_kstat
) {
521 if (nfs_client2_kstat
) {
522 time
= MAX(time
, nfs_client2_kstat
->ks_snaptime
);
524 if (nfs_client3_kstat
) {
525 time
= MAX(time
, nfs_client3_kstat
->ks_snaptime
);
527 if (nfs_server2_kstat
) {
528 time
= MAX(time
, nfs_server2_kstat
->ks_snaptime
);
530 if (nfs_server3_kstat
) {
531 time
= MAX(time
, nfs_server3_kstat
->ks_snaptime
);
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
);
537 (void) printf(" NFS requests = %d\n", delta
);
539 return (check_activity(nfs_act_start
, info
->pd_nfsreqs_sum
, hr_now
,
546 conskbd_ksp
= kstat_lookup(kc
, "conskbd", 0, "activity");
550 * Return the number of seconds since the last keystroke on console keyboard.
551 * Caller responsible for error reporting.
554 conskbd_idle_time(void)
558 if (conskbd_ksp
== NULL
|| kstat_read(kc
, conskbd_ksp
, NULL
) == -1 ||
559 (p
= kstat_data_lookup(conskbd_ksp
, "idle_sec")) == NULL
)
562 return (((kstat_named_t
*)p
)->value
.l
);
568 consms_ksp
= kstat_lookup(kc
, "consms", 0, "activity");
572 * Return the number of seconds since the most recent action (movement or
573 * click) of the console mouse. Caller responsible for error reporting.
576 consms_idle_time(void)
580 if (consms_ksp
== NULL
|| kstat_read(kc
, consms_ksp
, NULL
) == -1 ||
581 (p
= kstat_data_lookup(consms_ksp
, "idle_sec")) == NULL
)
584 return (((kstat_named_t
*)p
)->value
.l
);
591 const char *fmtptr
= new_fmt
;
595 len
= sizeof (new_fmt
);
597 if (snprintf(new_fmt
, len
, "powerd: %s", fmt
) > len
)
598 syslog(LOG_ERR
, "powerd: syslog message too large");
600 vsyslog(LOG_ERR
, fmtptr
, args
);
603 thr_exit((void *) 0);
607 safe_zalloc(void **ptr
, int size
, int free_first
)
609 if (free_first
&& *ptr
!= NULL
) {
612 if ((*ptr
= (void *) malloc(size
)) == NULL
) {
613 fail("malloc failed");
615 (void *) memset(*ptr
, 0, size
);
619 safe_kstat_data_lookup(kstat_t
*ksp
, char *name
)
621 void *fp
= kstat_data_lookup(ksp
, name
);
624 fail("kstat_data_lookup('%s', '%s') failed",
631 kscmp(kstat_t
*ks1
, kstat_t
*ks2
)
635 cmp
= strcmp(ks1
->ks_module
, ks2
->ks_module
);
639 cmp
= ks1
->ks_instance
- ks2
->ks_instance
;
643 return (strcmp(ks1
->ks_name
, ks2
->ks_name
));
647 keep_activity_data(activity_data_t
**act_start
, activity_data_t
**act_end
,
648 int *delta_sum
, int delta
, hrtime_t time
)
650 activity_data_t
*node
= NULLACTIVITY
;
652 int idle_time
= info
->pd_idle_time
* 60;
655 * Add new nodes to the beginning of the list.
657 safe_zalloc((void **)&node
, sizeof (activity_data_t
), 0);
658 node
->activity_delta
= delta
;
660 node
->snaptime
= time
;
661 node
->next
= *act_start
;
662 if (*act_start
== NULLACTIVITY
) {
665 (*act_start
)->prev
= node
;
670 * Remove nodes that are time-stamped later than the idle time.
672 hr_now
= gethrtime();
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
;
685 check_activity(activity_data_t
*act_start
, int delta_sum
, hrtime_t
*time
,
688 activity_data_t
*node
;
690 int idle_time
= info
->pd_idle_time
* 60;
693 * No need to walk the list if the sum of the deltas are not greater
694 * than the threshold value.
696 if (delta_sum
<= thold
) {
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.
706 while (node
->next
!= NULLACTIVITY
) {
707 sum
+= node
->activity_delta
;
709 return ((*time
- node
->snaptime
) / NANOSEC
);
713 sum
+= node
->activity_delta
;
715 return ((*time
- node
->snaptime
) / NANOSEC
);
722 kstat_copy(kstat_t
*src
, kstat_t
*dst
, int fr
)
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
);
733 dst
->ks_data_size
= 0;