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 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include "statcommon.h"
  29 
  30 #include <string.h>
  31 #include <errno.h>
  32 
  33 /* max size of report change annotations */
  34 #define LIST_SIZE 512
  35 
  36 static char cpus_added[LIST_SIZE];
  37 static char cpus_removed[LIST_SIZE];
  38 
  39 static int
  40 cpu_walk(struct snapshot *old, struct snapshot *new,
  41     snapshot_cb cb, void *data)
  42 {
  43         int changed = 0;
  44         int i;
  45 
  46         /* CPUs can change state but not re-order */
  47         for (i = 0; i < new->s_nr_cpus; i++) {
  48                 struct cpu_snapshot *cpu = NULL;
  49                 struct cpu_snapshot *newcpu = &new->s_cpus[i];
  50                 if (old)
  51                         cpu = &old->s_cpus[i];
  52                 cb(cpu, newcpu, data);
  53                 if (cpu == NULL)
  54                         changed = 1;
  55                 else {
  56                         /*
  57                          * We only care about off/on line transitions
  58                          */
  59                         if ((CPU_ACTIVE(cpu) && !CPU_ACTIVE(newcpu)) ||
  60                             (!CPU_ACTIVE(cpu) && CPU_ACTIVE(newcpu)))
  61                                 changed = 1;
  62                         if ((new->s_types & SNAP_PSETS) &&
  63                                 cpu->cs_pset_id != newcpu->cs_pset_id)
  64                                 changed = 1;
  65                 }
  66 
  67         }
  68 
  69         return (changed);
  70 }
  71 
  72 static int
  73 pset_walk(struct snapshot *old, struct snapshot *new,
  74     snapshot_cb cb, void *data)
  75 {
  76         int i = 0;
  77         int j = 0;
  78         int changed = 0;
  79 
  80         while (old && i < old->s_nr_psets && j < new->s_nr_psets) {
  81                 if (old->s_psets[i].ps_id < new->s_psets[j].ps_id) {
  82                         cb(&old->s_psets[i], NULL, data);
  83                         i++;
  84                         changed = 1;
  85                 } else if (old->s_psets[i].ps_id > new->s_psets[j].ps_id) {
  86                         cb(NULL, &new->s_psets[j], data);
  87                         j++;
  88                         changed = 1;
  89                 } else {
  90                         cb(&old->s_psets[i], &new->s_psets[j], data);
  91                         i++;
  92                         j++;
  93                 }
  94         }
  95 
  96         while (old && i < old->s_nr_psets) {
  97                 cb(&old->s_psets[i], NULL, data);
  98                 i++;
  99                 changed = 1;
 100         }
 101 
 102         while (j < new->s_nr_psets) {
 103                 cb(NULL, &new->s_psets[j], data);
 104                 j++;
 105                 changed = 1;
 106         }
 107 
 108         return (changed);
 109 }
 110 
 111 static int
 112 iodev_walk(struct iodev_snapshot *d1, struct iodev_snapshot *d2,
 113     snapshot_cb cb, void *data)
 114 {
 115         int changed = 0;
 116 
 117         while (d1 && d2) {
 118                 if (strcmp(d1->is_name, d2->is_name) < 0) {
 119                         changed = 1;
 120                         cb(d1, NULL, data);
 121                         (void) iodev_walk(d1->is_children, NULL, cb, data);
 122                         d1 = d1->is_next;
 123                 } else if (strcmp(d1->is_name, d2->is_name) > 0) {
 124                         changed = 1;
 125                         cb(NULL, d2, data);
 126                         (void) iodev_walk(NULL, d2->is_children, cb, data);
 127                         d2 = d2->is_next;
 128                 } else {
 129                         cb(d1, d2, data);
 130                         changed |= iodev_walk(d1->is_children,
 131                                         d2->is_children, cb, data);
 132                         d1 = d1->is_next;
 133                         d2 = d2->is_next;
 134                 }
 135         }
 136 
 137         while (d1) {
 138                 changed = 1;
 139                 cb(d1, NULL, data);
 140                 (void) iodev_walk(d1->is_children, NULL, cb, data);
 141                 d1 = d1->is_next;
 142         }
 143 
 144         while (d2) {
 145                 changed = 1;
 146                 cb(NULL, d2, data);
 147                 (void) iodev_walk(NULL, d2->is_children, cb, data);
 148                 d2 = d2->is_next;
 149         }
 150 
 151         return (changed);
 152 }
 153 
 154 int
 155 snapshot_walk(enum snapshot_types type, struct snapshot *old,
 156     struct snapshot *new, snapshot_cb cb, void *data)
 157 {
 158         int changed = 0;
 159 
 160         switch (type) {
 161         case SNAP_CPUS:
 162                 changed = cpu_walk(old, new, cb, data);
 163                 break;
 164 
 165         case SNAP_PSETS:
 166                 changed = pset_walk(old, new, cb, data);
 167                 break;
 168 
 169         case SNAP_CONTROLLERS:
 170         case SNAP_IODEVS:
 171         case SNAP_IOPATHS_LI:
 172         case SNAP_IOPATHS_LTI:
 173                 changed = iodev_walk(old ? old->s_iodevs : NULL,
 174                     new->s_iodevs, cb, data);
 175                 break;
 176 
 177         default:
 178                 break;
 179         }
 180 
 181         return (changed);
 182 }
 183 
 184 static void
 185 add_nr_to_list(char *buf, unsigned long nr)
 186 {
 187         char tmp[LIST_SIZE];
 188 
 189         (void) snprintf(tmp, LIST_SIZE, "%lu", nr);
 190 
 191         if (strlen(buf))
 192                 (void) strlcat(buf, ", ", LIST_SIZE);
 193 
 194         (void) strlcat(buf, tmp, LIST_SIZE);
 195 }
 196 
 197 static void
 198 cpu_report(void *v1, void *v2, void *data)
 199 {
 200         int *pset = (int *)data;
 201         struct cpu_snapshot *c1 = (struct cpu_snapshot *)v1;
 202         struct cpu_snapshot *c2 = (struct cpu_snapshot *)v2;
 203 
 204         if (*pset && c1->cs_pset_id != c2->cs_pset_id) {
 205                 (void) printf("<<processor %d moved from pset: %d to: %d>>\n",
 206                     c1->cs_id, c1->cs_pset_id, c2->cs_pset_id);
 207         }
 208 
 209         if (c1->cs_state == c2->cs_state)
 210                 return;
 211 
 212         if (CPU_ONLINE(c1->cs_state) && !CPU_ONLINE(c2->cs_state))
 213                 add_nr_to_list(cpus_removed, c1->cs_id);
 214 
 215         if (!CPU_ONLINE(c1->cs_state) && CPU_ONLINE(c2->cs_state))
 216                 add_nr_to_list(cpus_added, c2->cs_id);
 217 }
 218 
 219 /*ARGSUSED*/
 220 static void
 221 pset_report(void *v1, void *v2, void *data)
 222 {
 223         struct pset_snapshot *p1 = (struct pset_snapshot *)v1;
 224         struct pset_snapshot *p2 = (struct pset_snapshot *)v2;
 225 
 226         if (p2 == NULL) {
 227                 (void) printf("<<pset destroyed: %u>>\n", p1->ps_id);
 228                 return;
 229         }
 230 
 231         if (p1 == NULL)
 232                 (void) printf("<<pset created: %u>>\n", p2->ps_id);
 233 }
 234 
 235 static void
 236 get_child_list(struct iodev_snapshot *iodev, char *buf)
 237 {
 238         char tmp[LIST_SIZE];
 239         struct iodev_snapshot *pos = iodev->is_children;
 240 
 241         while (pos) {
 242                 if (pos->is_type == IODEV_PARTITION) {
 243                         add_nr_to_list(buf, pos->is_id.id);
 244                 } else if (pos->is_type == IODEV_DISK) {
 245                         if (strlen(buf))
 246                                 (void) strlcat(buf, ", ", LIST_SIZE);
 247                         (void) strlcat(buf, "t", LIST_SIZE);
 248                         (void) strlcat(buf, pos->is_id.tid, LIST_SIZE);
 249                         (void) strlcat(buf, "d", LIST_SIZE);
 250                         *tmp = '\0';
 251                         add_nr_to_list(tmp, pos->is_id.id);
 252                         (void) strlcat(buf, tmp, LIST_SIZE);
 253                 }
 254                 pos = pos->is_next;
 255         }
 256 }
 257 
 258 static void
 259 iodev_changed(struct iodev_snapshot *iodev, int added)
 260 {
 261         char tmp[LIST_SIZE];
 262         int is_disk = iodev->is_type == IODEV_DISK;
 263         char *name = iodev->is_name;
 264 
 265         if (iodev->is_pretty)
 266                 name = iodev->is_pretty;
 267 
 268         switch (iodev->is_type) {
 269         case IODEV_IOPATH_LT:
 270         case IODEV_IOPATH_LI:
 271         case IODEV_IOPATH_LTI:
 272                 (void) printf("<<multi-path %s: %s>>\n",
 273                     added ? "added" : "removed", name);
 274                 break;
 275         case IODEV_PARTITION:
 276                 (void) printf("<<partition %s: %s>>\n",
 277                     added ? "added" : "removed", name);
 278                 break;
 279         case IODEV_NFS:
 280                 (void) printf("<<NFS %s: %s>>\n",
 281                     added ? "mounted" : "unmounted", name);
 282                 break;
 283         case IODEV_TAPE:
 284                 (void) printf("<<device %s: %s>>\n",
 285                     added ? "added" : "removed", name);
 286                 break;
 287         case IODEV_CONTROLLER:
 288         case IODEV_DISK:
 289                 *tmp = '\0';
 290                 get_child_list(iodev, tmp);
 291                 (void) printf("<<%s %s: %s", is_disk ? "disk" : "controller",
 292                     added ? "added" : "removed", name);
 293                 if (!*tmp) {
 294                         (void) printf(">>\n");
 295                         return;
 296                 }
 297                 (void) printf(" (%s %s)>>\n", is_disk ? "slices" : "disks",
 298                     tmp);
 299                 break;
 300         };
 301 }
 302 
 303 static void
 304 iodev_report(struct iodev_snapshot *d1, struct iodev_snapshot *d2)
 305 {
 306         while (d1 && d2) {
 307                 if (iodev_cmp(d1, d2) < 0) {
 308                         iodev_changed(d1, 0);
 309                         d1 = d1->is_next;
 310                 } else if (iodev_cmp(d1, d2) > 0) {
 311                         iodev_changed(d2, 1);
 312                         d2 = d2->is_next;
 313                 } else {
 314                         iodev_report(d1->is_children, d2->is_children);
 315                         d1 = d1->is_next;
 316                         d2 = d2->is_next;
 317                 }
 318         }
 319 
 320         while (d1) {
 321                 iodev_changed(d1, 0);
 322                 d1 = d1->is_next;
 323         }
 324 
 325         while (d2) {
 326                 iodev_changed(d2, 1);
 327                 d2 = d2->is_next;
 328         }
 329 }
 330 
 331 void
 332 snapshot_report_changes(struct snapshot *old, struct snapshot *new)
 333 {
 334         int pset;
 335 
 336         if (old == NULL || new == NULL)
 337                 return;
 338 
 339         if (old->s_types != new->s_types)
 340                 return;
 341 
 342         pset = old->s_types & SNAP_PSETS;
 343 
 344         cpus_removed[0] = '\0';
 345         cpus_added[0] = '\0';
 346 
 347         if (old->s_types & SNAP_CPUS)
 348                 (void) snapshot_walk(SNAP_CPUS, old, new, cpu_report, &pset);
 349 
 350         if (cpus_added[0]) {
 351                 (void) printf("<<processors added: %s>>\n",
 352                     cpus_added);
 353         }
 354         if (cpus_removed[0]) {
 355                 (void) printf("<<processors removed: %s>>\n",
 356                     cpus_removed);
 357         }
 358         if (pset) {
 359                 (void) snapshot_walk(SNAP_PSETS, old, new,
 360                     pset_report, NULL);
 361         }
 362 
 363         iodev_report(old->s_iodevs, new->s_iodevs);
 364 }
 365 
 366 /*ARGSUSED*/
 367 static void
 368 dummy_cb(void *v1, void *v2, void *data)
 369 {
 370 }
 371 
 372 int
 373 snapshot_has_changed(struct snapshot *old, struct snapshot *new)
 374 {
 375         int ret = 0;
 376         int cpu_mask = SNAP_CPUS | SNAP_PSETS | SNAP_SYSTEM;
 377         int iodev_mask = SNAP_CONTROLLERS | SNAP_IODEVS |
 378                         SNAP_IOPATHS_LI | SNAP_IOPATHS_LTI;
 379 
 380         if (old == NULL)
 381                 return (1);
 382 
 383         if (new == NULL)
 384                 return (EINVAL);
 385 
 386         if (old->s_types != new->s_types)
 387                 return (EINVAL);
 388 
 389         if (!ret && (old->s_types & cpu_mask))
 390                 ret = snapshot_walk(SNAP_CPUS, old, new, dummy_cb, NULL);
 391         if (!ret && (old->s_types & SNAP_PSETS))
 392                 ret = snapshot_walk(SNAP_PSETS, old, new, dummy_cb, NULL);
 393         if (!ret && (old->s_types & iodev_mask))
 394                 ret = snapshot_walk(SNAP_IODEVS, old, new, dummy_cb, NULL);
 395 
 396         return (ret);
 397 }