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 } 398