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