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