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