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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 2012 by Delphix. All rights reserved.
  24  */
  25 
  26 #include "statcommon.h"
  27 #include "dsr.h"
  28 
  29 #include <stdlib.h>
  30 #include <unistd.h>
  31 #include <strings.h>
  32 #include <errno.h>
  33 #include <limits.h>
  34 #include <poll.h>
  35 
  36 #define ARRAY_SIZE(a)   (sizeof (a) / sizeof (*a))
  37 
  38 /*
  39  * The time we delay before retrying after an allocation
  40  * failure, in milliseconds
  41  */
  42 #define RETRY_DELAY 200
  43 
  44 static char *cpu_states[] = {
  45         "cpu_ticks_idle",
  46         "cpu_ticks_user",
  47         "cpu_ticks_kernel",
  48         "cpu_ticks_wait"
  49 };
  50 
  51 static kstat_t *
  52 kstat_lookup_read(kstat_ctl_t *kc, char *module,
  53     int instance, char *name)
  54 {
  55         kstat_t *ksp = kstat_lookup(kc, module, instance, name);
  56         if (ksp == NULL)
  57                 return (NULL);
  58         if (kstat_read(kc, ksp, NULL) == -1)
  59                 return (NULL);
  60         return (ksp);
  61 }
  62 
  63 /*
  64  * Note: the following helpers do not clean up on the failure case,
  65  * because it is left to the free_snapshot() in the acquire_snapshot()
  66  * failure path.
  67  */
  68 
  69 static int
  70 acquire_cpus(struct snapshot *ss, kstat_ctl_t *kc)
  71 {
  72         size_t i;
  73 
  74         ss->s_nr_cpus = sysconf(_SC_CPUID_MAX) + 1;
  75         ss->s_cpus = calloc(ss->s_nr_cpus, sizeof (struct cpu_snapshot));
  76         if (ss->s_cpus == NULL)
  77                 goto out;
  78 
  79         for (i = 0; i < ss->s_nr_cpus; i++) {
  80                 kstat_t *ksp;
  81 
  82                 ss->s_cpus[i].cs_id = ID_NO_CPU;
  83                 ss->s_cpus[i].cs_state = p_online(i, P_STATUS);
  84                 /* If no valid CPU is present, move on to the next one */
  85                 if (ss->s_cpus[i].cs_state == -1)
  86                         continue;
  87                 ss->s_cpus[i].cs_id = i;
  88 
  89                 if ((ksp = kstat_lookup_read(kc, "cpu_info", i, NULL)) == NULL)
  90                         goto out;
  91 
  92                 (void) pset_assign(PS_QUERY, i, &ss->s_cpus[i].cs_pset_id);
  93                 if (ss->s_cpus[i].cs_pset_id == PS_NONE)
  94                         ss->s_cpus[i].cs_pset_id = ID_NO_PSET;
  95 
  96                 if (!CPU_ACTIVE(&ss->s_cpus[i]))
  97                         continue;
  98 
  99                 if ((ksp = kstat_lookup_read(kc, "cpu", i, "vm")) == NULL)
 100                         goto out;
 101 
 102                 if (kstat_copy(ksp, &ss->s_cpus[i].cs_vm))
 103                         goto out;
 104 
 105                 if ((ksp = kstat_lookup_read(kc, "cpu", i, "sys")) == NULL)
 106                         goto out;
 107 
 108                 if (kstat_copy(ksp, &ss->s_cpus[i].cs_sys))
 109                         goto out;
 110         }
 111 
 112         errno = 0;
 113 out:
 114         return (errno);
 115 }
 116 
 117 static int
 118 acquire_psets(struct snapshot *ss)
 119 {
 120         psetid_t *pids = NULL;
 121         struct pset_snapshot *ps;
 122         size_t pids_nr;
 123         size_t i, j;
 124 
 125         /*
 126          * Careful in this code. We have to use pset_list
 127          * twice, but inbetween pids_nr can change at will.
 128          * We delay the setting of s_nr_psets until we have
 129          * the "final" value of pids_nr.
 130          */
 131 
 132         if (pset_list(NULL, &pids_nr) < 0)
 133                 return (errno);
 134 
 135         if ((pids = calloc(pids_nr, sizeof (psetid_t))) == NULL)
 136                 goto out;
 137 
 138         if (pset_list(pids, &pids_nr) < 0)
 139                 goto out;
 140 
 141         ss->s_psets = calloc(pids_nr + 1, sizeof (struct pset_snapshot));
 142         if (ss->s_psets == NULL)
 143                 goto out;
 144         ss->s_nr_psets = pids_nr + 1;
 145 
 146         /* CPUs not in any actual pset */
 147         ps = &ss->s_psets[0];
 148         ps->ps_id = 0;
 149         ps->ps_cpus = calloc(ss->s_nr_cpus, sizeof (struct cpu_snapshot *));
 150         if (ps->ps_cpus == NULL)
 151                 goto out;
 152 
 153         /* CPUs in a a pset */
 154         for (i = 1; i < ss->s_nr_psets; i++) {
 155                 ps = &ss->s_psets[i];
 156 
 157                 ps->ps_id = pids[i - 1];
 158                 ps->ps_cpus =
 159                     calloc(ss->s_nr_cpus, sizeof (struct cpu_snapshot *));
 160                 if (ps->ps_cpus == NULL)
 161                         goto out;
 162         }
 163 
 164         for (i = 0; i < ss->s_nr_psets; i++) {
 165                 ps = &ss->s_psets[i];
 166 
 167                 for (j = 0; j < ss->s_nr_cpus; j++) {
 168                         if (!CPU_ACTIVE(&ss->s_cpus[j]))
 169                                 continue;
 170                         if (ss->s_cpus[j].cs_pset_id != ps->ps_id)
 171                                 continue;
 172 
 173                         ps->ps_cpus[ps->ps_nr_cpus++] = &ss->s_cpus[j];
 174                 }
 175         }
 176 
 177         errno = 0;
 178 out:
 179         free(pids);
 180         return (errno);
 181 }
 182 
 183 static int
 184 acquire_intrs(struct snapshot *ss, kstat_ctl_t *kc)
 185 {
 186         kstat_t *ksp;
 187         size_t i = 0;
 188         kstat_t *sys_misc;
 189         kstat_named_t *clock;
 190 
 191         /* clock interrupt */
 192         ss->s_nr_intrs = 1;
 193 
 194         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
 195                 if (ksp->ks_type == KSTAT_TYPE_INTR)
 196                         ss->s_nr_intrs++;
 197         }
 198 
 199         ss->s_intrs = calloc(ss->s_nr_intrs, sizeof (struct intr_snapshot));
 200         if (ss->s_intrs == NULL)
 201                 return (errno);
 202 
 203         sys_misc = kstat_lookup_read(kc, "unix", 0, "system_misc");
 204         if (sys_misc == NULL)
 205                 goto out;
 206 
 207         clock = (kstat_named_t *)kstat_data_lookup(sys_misc, "clk_intr");
 208         if (clock == NULL)
 209                 goto out;
 210 
 211         (void) strlcpy(ss->s_intrs[0].is_name, "clock", KSTAT_STRLEN);
 212         ss->s_intrs[0].is_total = clock->value.ui32;
 213 
 214         i = 1;
 215 
 216         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
 217                 kstat_intr_t *ki;
 218                 int j;
 219 
 220                 if (ksp->ks_type != KSTAT_TYPE_INTR)
 221                         continue;
 222                 if (kstat_read(kc, ksp, NULL) == -1)
 223                         goto out;
 224 
 225                 ki = KSTAT_INTR_PTR(ksp);
 226 
 227                 (void) strlcpy(ss->s_intrs[i].is_name, ksp->ks_name,
 228                     KSTAT_STRLEN);
 229                 ss->s_intrs[i].is_total = 0;
 230 
 231                 for (j = 0; j < KSTAT_NUM_INTRS; j++)
 232                         ss->s_intrs[i].is_total += ki->intrs[j];
 233 
 234                 i++;
 235         }
 236 
 237         errno = 0;
 238 out:
 239         return (errno);
 240 }
 241 
 242 int
 243 acquire_sys(struct snapshot *ss, kstat_ctl_t *kc)
 244 {
 245         size_t i;
 246         kstat_named_t *knp;
 247         kstat_t *ksp;
 248 
 249         if ((ksp = kstat_lookup(kc, "unix", 0, "sysinfo")) == NULL)
 250                 return (errno);
 251 
 252         if (kstat_read(kc, ksp, &ss->s_sys.ss_sysinfo) == -1)
 253                 return (errno);
 254 
 255         if ((ksp = kstat_lookup(kc, "unix", 0, "vminfo")) == NULL)
 256                 return (errno);
 257 
 258         if (kstat_read(kc, ksp, &ss->s_sys.ss_vminfo) == -1)
 259                 return (errno);
 260 
 261         if ((ksp = kstat_lookup(kc, "unix", 0, "dnlcstats")) == NULL)
 262                 return (errno);
 263 
 264         if (kstat_read(kc, ksp, &ss->s_sys.ss_nc) == -1)
 265                 return (errno);
 266 
 267         if ((ksp = kstat_lookup(kc, "unix", 0, "system_misc")) == NULL)
 268                 return (errno);
 269 
 270         if (kstat_read(kc, ksp, NULL) == -1)
 271                 return (errno);
 272 
 273         knp = (kstat_named_t *)kstat_data_lookup(ksp, "clk_intr");
 274         if (knp == NULL)
 275                 return (errno);
 276 
 277         ss->s_sys.ss_ticks = knp->value.l;
 278 
 279         knp = (kstat_named_t *)kstat_data_lookup(ksp, "deficit");
 280         if (knp == NULL)
 281                 return (errno);
 282 
 283         ss->s_sys.ss_deficit = knp->value.l;
 284 
 285         for (i = 0; i < ss->s_nr_cpus; i++) {
 286                 if (!CPU_ACTIVE(&ss->s_cpus[i]))
 287                         continue;
 288 
 289                 if (kstat_add(&ss->s_cpus[i].cs_sys, &ss->s_sys.ss_agg_sys))
 290                         return (errno);
 291                 if (kstat_add(&ss->s_cpus[i].cs_vm, &ss->s_sys.ss_agg_vm))
 292                         return (errno);
 293                 ss->s_nr_active_cpus++;
 294         }
 295 
 296         return (0);
 297 }
 298 
 299 struct snapshot *
 300 acquire_snapshot(kstat_ctl_t *kc, int types, struct iodev_filter *iodev_filter)
 301 {
 302         struct snapshot *ss = NULL;
 303         int err;
 304 
 305 retry:
 306         err = 0;
 307         /* ensure any partial resources are freed on a retry */
 308         free_snapshot(ss);
 309 
 310         ss = safe_alloc(sizeof (struct snapshot));
 311 
 312         (void) memset(ss, 0, sizeof (struct snapshot));
 313 
 314         ss->s_types = types;
 315 
 316         /* wait for a possibly up-to-date chain */
 317         while (kstat_chain_update(kc) == -1) {
 318                 if (errno == EAGAIN)
 319                         (void) poll(NULL, 0, RETRY_DELAY);
 320                 else
 321                         fail(1, "kstat_chain_update failed");
 322         }
 323 
 324         if (!err && (types & SNAP_INTERRUPTS))
 325                 err = acquire_intrs(ss, kc);
 326 
 327         if (!err && (types & (SNAP_CPUS | SNAP_SYSTEM | SNAP_PSETS)))
 328                 err = acquire_cpus(ss, kc);
 329 
 330         if (!err && (types & SNAP_PSETS))
 331                 err = acquire_psets(ss);
 332 
 333         if (!err && (types & (SNAP_IODEVS | SNAP_CONTROLLERS |
 334             SNAP_IOPATHS_LI | SNAP_IOPATHS_LTI)))
 335                 err = acquire_iodevs(ss, kc, iodev_filter);
 336 
 337         if (!err && (types & SNAP_SYSTEM))
 338                 err = acquire_sys(ss, kc);
 339 
 340         switch (err) {
 341                 case 0:
 342                         break;
 343                 case EAGAIN:
 344                         (void) poll(NULL, 0, RETRY_DELAY);
 345                 /* a kstat disappeared from under us */
 346                 /*FALLTHRU*/
 347                 case ENXIO:
 348                 case ENOENT:
 349                         goto retry;
 350                 default:
 351                         fail(1, "acquiring snapshot failed");
 352         }
 353 
 354         return (ss);
 355 }
 356 
 357 void
 358 free_snapshot(struct snapshot *ss)
 359 {
 360         size_t i;
 361 
 362         if (ss == NULL)
 363                 return;
 364 
 365         while (ss->s_iodevs) {
 366                 struct iodev_snapshot *tmp = ss->s_iodevs;
 367                 ss->s_iodevs = ss->s_iodevs->is_next;
 368                 free_iodev(tmp);
 369         }
 370 
 371         if (ss->s_cpus) {
 372                 for (i = 0; i < ss->s_nr_cpus; i++) {
 373                         free(ss->s_cpus[i].cs_vm.ks_data);
 374                         free(ss->s_cpus[i].cs_sys.ks_data);
 375                 }
 376                 free(ss->s_cpus);
 377         }
 378 
 379         if (ss->s_psets) {
 380                 for (i = 0; i < ss->s_nr_psets; i++)
 381                         free(ss->s_psets[i].ps_cpus);
 382                 free(ss->s_psets);
 383         }
 384 
 385         free(ss->s_sys.ss_agg_sys.ks_data);
 386         free(ss->s_sys.ss_agg_vm.ks_data);
 387         free(ss);
 388 }
 389 
 390 kstat_ctl_t *
 391 open_kstat(void)
 392 {
 393         kstat_ctl_t *kc;
 394 
 395         while ((kc = kstat_open()) == NULL) {
 396                 if (errno == EAGAIN)
 397                         (void) poll(NULL, 0, RETRY_DELAY);
 398                 else
 399                         fail(1, "kstat_open failed");
 400         }
 401 
 402         return (kc);
 403 }
 404 
 405 void *
 406 safe_alloc(size_t size)
 407 {
 408         void *ptr;
 409 
 410         while ((ptr = malloc(size)) == NULL) {
 411                 if (errno == EAGAIN)
 412                         (void) poll(NULL, 0, RETRY_DELAY);
 413                 else
 414                         fail(1, "malloc failed");
 415         }
 416         return (ptr);
 417 }
 418 
 419 char *
 420 safe_strdup(char *str)
 421 {
 422         char *ret;
 423 
 424         if (str == NULL)
 425                 return (NULL);
 426 
 427         while ((ret = strdup(str)) == NULL) {
 428                 if (errno == EAGAIN)
 429                         (void) poll(NULL, 0, RETRY_DELAY);
 430                 else
 431                         fail(1, "malloc failed");
 432         }
 433         return (ret);
 434 }
 435 
 436 uint64_t
 437 kstat_delta(kstat_t *old, kstat_t *new, char *name)
 438 {
 439         kstat_named_t *knew = kstat_data_lookup(new, name);
 440         if (old && old->ks_data) {
 441                 kstat_named_t *kold = kstat_data_lookup(old, name);
 442                 return (knew->value.ui64 - kold->value.ui64);
 443         }
 444         return (knew->value.ui64);
 445 }
 446 
 447 int
 448 kstat_copy(const kstat_t *src, kstat_t *dst)
 449 {
 450         *dst = *src;
 451 
 452         if (src->ks_data != NULL) {
 453                 if ((dst->ks_data = malloc(src->ks_data_size)) == NULL)
 454                         return (-1);
 455                 bcopy(src->ks_data, dst->ks_data, src->ks_data_size);
 456         } else {
 457                 dst->ks_data = NULL;
 458                 dst->ks_data_size = 0;
 459         }
 460         return (0);
 461 }
 462 
 463 int
 464 kstat_add(const kstat_t *src, kstat_t *dst)
 465 {
 466         size_t i;
 467         kstat_named_t *from;
 468         kstat_named_t *to;
 469 
 470         if (dst->ks_data == NULL)
 471                 return (kstat_copy(src, dst));
 472 
 473         from = src->ks_data;
 474         to = dst->ks_data;
 475 
 476         for (i = 0; i < src->ks_ndata; i++) {
 477                 /* "addition" makes little sense for strings */
 478                 if (from->data_type != KSTAT_DATA_CHAR &&
 479                     from->data_type != KSTAT_DATA_STRING)
 480                         (to)->value.ui64 += (from)->value.ui64;
 481                 from++;
 482                 to++;
 483         }
 484 
 485         return (0);
 486 }
 487 
 488 uint64_t
 489 cpu_ticks_delta(kstat_t *old, kstat_t *new)
 490 {
 491         uint64_t ticks = 0;
 492         size_t i;
 493         for (i = 0; i < ARRAY_SIZE(cpu_states); i++)
 494                 ticks += kstat_delta(old, new, cpu_states[i]);
 495         return (ticks);
 496 }
 497 
 498 int
 499 nr_active_cpus(struct snapshot *ss)
 500 {
 501         size_t i;
 502         int count = 0;
 503         for (i = 0; i < ss->s_nr_cpus; i++) {
 504                 if (CPU_ACTIVE(&ss->s_cpus[i]))
 505                         count++;
 506         }
 507 
 508         return (count);
 509 }
 510 
 511 /*
 512  * Return the number of ticks delta between two hrtime_t
 513  * values. Attempt to cater for various kinds of overflow
 514  * in hrtime_t - no matter how improbable.
 515  */
 516 uint64_t
 517 hrtime_delta(hrtime_t old, hrtime_t new)
 518 {
 519         uint64_t del;
 520 
 521         if ((new >= old) && (old >= 0L))
 522                 return (new - old);
 523         else {
 524                 /*
 525                  * We've overflowed the positive portion of an
 526                  * hrtime_t.
 527                  */
 528                 if (new < 0L) {
 529                         /*
 530                          * The new value is negative. Handle the
 531                          * case where the old value is positive or
 532                          * negative.
 533                          */
 534                         uint64_t n1;
 535                         uint64_t o1;
 536 
 537                         n1 = -new;
 538                         if (old > 0L)
 539                                 return (n1 - old);
 540                         else {
 541                                 o1 = -old;
 542                                 del = n1 - o1;
 543                                 return (del);
 544                         }
 545                 } else {
 546                         /*
 547                          * Either we've just gone from being negative
 548                          * to positive *or* the last entry was positive
 549                          * and the new entry is also positive but *less*
 550                          * than the old entry. This implies we waited
 551                          * quite a few days on a very fast system between
 552                          * iostat displays.
 553                          */
 554                         if (old < 0L) {
 555                                 uint64_t o2;
 556 
 557                                 o2 = -old;
 558                                 del = UINT64_MAX - o2;
 559                         } else {
 560                                 del = UINT64_MAX - old;
 561                         }
 562                         del += new;
 563                         return (del);
 564                 }
 565         }
 566 }