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