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 <unistd.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <errno.h> 33 #include <time.h> 34 #include <kstat.h> 35 #include <sys/processor.h> /* for processorid_t */ 36 37 #include <libintl.h> 38 #include <locale.h> 39 40 #ifndef TEXT_DOMAIN 41 #define TEXT_DOMAIN "SYS_TEST" 42 #endif 43 44 #define NCPUSTATES 6 /* on-line, off-line, no-intr, faulted, */ 45 /* spare, power-off */ 46 47 /* 48 * Possible states that a cpu may be in, and their corresponding 49 * localized versions. 50 */ 51 static struct { 52 const char *state; /* State returned in kstat. */ 53 const char *lstate; /* Localized version of the state. */ 54 } cpu_states[NCPUSTATES]; 55 56 static const char cmdname[] = "psrinfo"; 57 58 #define CPUS_PER_CHIP_MAX 256 /* ABEN (Arbitrarily big-enough number) */ 59 static int chip_count = 0; 60 61 typedef struct chip { 62 int chip_id; 63 int visible; 64 int online; 65 int cpu_count; 66 char impl[128]; 67 char brand[128]; 68 struct chip *next; 69 processorid_t cpus[CPUS_PER_CHIP_MAX]; 70 } chipdata; 71 static chipdata *chiplist = NULL; 72 73 static void cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity, 74 int phys_view, int visible); 75 static void print_cpuid_list(processorid_t *ids, int count); 76 77 static chipdata *get_chipdata(int chip_id); 78 79 static void 80 usage(char *msg) 81 { 82 if (msg != NULL) 83 (void) fprintf(stderr, "%s: %s\n", cmdname, msg); 84 (void) fprintf(stderr, 85 gettext("usage: \n\t%s [-v] [-p] [processor_id ...]\n" 86 "\t%s -s [-p] processor_id\n"), cmdname, cmdname); 87 exit(2); 88 } 89 90 int 91 main(int argc, char *argv[]) 92 { 93 kstat_ctl_t *kc; 94 kstat_t *ksp; 95 int c; 96 processorid_t cpu; 97 int verbosity = 1; /* 0 = silent, 1 = normal, 3 = verbose */ 98 int phys_view = 0; 99 int errors = 0; 100 char *errptr; 101 102 (void) setlocale(LC_ALL, ""); 103 (void) textdomain(TEXT_DOMAIN); 104 105 while ((c = getopt(argc, argv, "psv")) != EOF) { 106 switch (c) { 107 case 'v': 108 verbosity |= 2; 109 break; 110 case 's': 111 verbosity &= ~1; 112 break; 113 case 'p': 114 phys_view = 1; 115 break; 116 default: 117 usage(NULL); 118 } 119 } 120 121 argc -= optind; 122 argv += optind; 123 124 if (verbosity == 2) 125 usage(gettext("options -s and -v are mutually exclusive")); 126 127 if (verbosity == 0 && argc != 1) 128 usage(gettext("must specify exactly one processor if -s used")); 129 130 if ((kc = kstat_open()) == NULL) { 131 (void) fprintf(stderr, gettext("%s: kstat_open() failed: %s\n"), 132 cmdname, strerror(errno)); 133 exit(1); 134 } 135 136 /* 137 * Build localized cpu state table. 138 */ 139 cpu_states[0].state = PS_ONLINE; 140 cpu_states[0].lstate = gettext(PS_ONLINE); 141 cpu_states[1].state = PS_POWEROFF; 142 cpu_states[1].lstate = gettext(PS_POWEROFF); 143 cpu_states[2].state = PS_NOINTR; 144 cpu_states[2].lstate = gettext(PS_NOINTR); 145 cpu_states[3].state = PS_FAULTED; 146 cpu_states[3].lstate = gettext(PS_FAULTED); 147 cpu_states[4].state = PS_SPARE; 148 cpu_states[4].lstate = gettext(PS_SPARE); 149 cpu_states[5].state = PS_OFFLINE; 150 cpu_states[5].lstate = gettext(PS_OFFLINE); 151 152 /* 153 * In the physical view, we want to display all the cpu info or 154 * none, even when the user specifies a range of CPUIDs. So for 155 * "psrinfo -pv <range>" or "psrinfo -ps <range>", we inventory 156 * every cpu_info kstat, and *then* we go through the user 157 * specified processors merely flipping on their "visible" 158 * flags. 159 */ 160 161 if (argc == 0 || (phys_view && (verbosity != 1))) { 162 /* 163 * No processors specified. Report on all of them. 164 * Or do a complete inventory in preparation for a 165 * specified list of physical processors. See note 166 * immediately above. 167 */ 168 processorid_t maxcpu = -1; 169 170 for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) 171 if (strcmp(ksp->ks_module, "cpu_info") == 0 && 172 ksp->ks_instance > maxcpu) 173 maxcpu = ksp->ks_instance; 174 175 /* 176 * N.B. We're assuming that instance number == cpuid. 177 * A number of things break if this isn't true. In 178 * particular, print_cpuid_list() assumes that c->cpus[] 179 * is numerically sorted, which is true only because we 180 * add each virtual processor in cpuid order here. 181 */ 182 for (cpu = 0; cpu <= maxcpu; cpu++) 183 if (ksp = kstat_lookup(kc, "cpu_info", cpu, NULL)) 184 cpu_info(kc, ksp, verbosity, phys_view, 185 argc == 0); 186 } 187 188 if (argc != 0) { 189 /* 190 * Report on specified processors. 191 */ 192 for (; argc > 0; argv++, argc--) { 193 if (strchr(*argv, '-') == NULL) { 194 /* individual processor id */ 195 char cpubuf[20]; 196 197 (void) sprintf(cpubuf, "cpu_info%.10s", *argv); 198 if (ksp = kstat_lookup(kc, "cpu_info", -1, 199 cpubuf)) 200 cpu_info(kc, ksp, verbosity, 201 phys_view, 1); 202 else { 203 (void) fprintf(stderr, 204 gettext("%s: processor %s: %s\n"), 205 cmdname, *argv, strerror(EINVAL)); 206 errors = 2; 207 } 208 } else { 209 /* range of processors */ 210 int first, last; 211 int found = 0; 212 213 if (verbosity == 0) { 214 usage(gettext("must specify exactly " 215 "one processor if -s used")); 216 } 217 first = (int)strtol(*argv, &errptr, 10); 218 if (*errptr++ != '-') { 219 (void) fprintf(stderr, 220 gettext("%s: invalid processor " 221 "range %s\n"), cmdname, *argv); 222 errors = 2; 223 continue; 224 } 225 last = (int)strtol(errptr, &errptr, 10); 226 if ((errptr != NULL && *errptr != '\0') || 227 last < first || first < 0) { 228 (void) fprintf(stderr, 229 gettext("%s: invalid processor " 230 "range %s\n"), cmdname, *argv); 231 errors = 2; 232 continue; 233 } 234 for (cpu = first; cpu <= last; cpu++) { 235 if (ksp = kstat_lookup(kc, "cpu_info", 236 cpu, NULL)) { 237 found = 1; 238 cpu_info(kc, ksp, verbosity, 239 phys_view, 1); 240 } 241 } 242 if (!found) { 243 (void) fprintf(stderr, 244 gettext("%s: no processors in " 245 "range %d-%d\n"), cmdname, 246 first, last); 247 } 248 } 249 } 250 } 251 252 if (phys_view) { 253 chipdata *c; 254 255 switch (verbosity) { 256 case 0: 257 /* 258 * Print "1" if all the cpus on this chip are 259 * online. "0" otherwise. 260 */ 261 for (c = chiplist; c != NULL; c = c->next) { 262 263 if (!c->visible) 264 continue; 265 (void) printf("%d\n", 266 c->online == c->cpu_count); 267 exit(0); 268 } 269 break; 270 case 1: 271 /* 272 * Print the number of unique chips represented by 273 * all the cpus specified on the command line 274 * (or, with no args, all the cpus in the system). 275 */ 276 (void) printf("%d\n", chip_count); 277 break; 278 case 3: 279 /* 280 * Print a report on each chip. 281 */ 282 for (c = chiplist; c != NULL; c = c->next) { 283 if (!c->visible) 284 continue; 285 286 (void) printf(gettext("The physical " 287 "processor has %d virtual %s ("), 288 c->cpu_count, 289 c->cpu_count == 1 ? 290 gettext("processor") : 291 gettext("processors")); 292 print_cpuid_list(c->cpus, c->cpu_count); 293 (void) printf(")\n"); 294 295 (void) printf(" %s\n", c->impl); 296 297 /* 298 * If the "brand" has already been embedded 299 * at the front of the "impl" string, don't 300 * print it out again .. otherwise give it 301 * a fresh line to bask upon .. 302 */ 303 if (strncmp(c->impl, c->brand, 304 strlen(c->brand)) != 0) 305 (void) printf("\t%s\n", c->brand); 306 } 307 break; 308 } 309 } 310 311 return (errors); 312 } 313 314 #define GETLONG(name) ((kstat_named_t *)kstat_data_lookup(ksp, name))->value.l 315 #define GETSTR(name) ((kstat_named_t *)kstat_data_lookup(ksp, name))->value.c 316 #define GETLONGSTR(name) \ 317 KSTAT_NAMED_STR_PTR((kstat_named_t *)kstat_data_lookup(ksp, name)) 318 319 /* 320 * Utility function to retrieve the localized version of the cpu state string. 321 */ 322 static const char * 323 get_cpu_state(const char *state) 324 { 325 int i; 326 327 for (i = 0; i < NCPUSTATES; i++) 328 if (strcmp(cpu_states[i].state, state) == 0) 329 return (cpu_states[i].lstate); 330 return (gettext("(unknown)")); 331 } 332 333 static void 334 cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity, int phys_view, 335 int visible) 336 { 337 char curtime[40], start[40]; 338 processorid_t cpu_id = ksp->ks_instance; 339 time_t now = time(NULL); 340 341 if (kstat_read(kc, ksp, NULL) == -1) { 342 (void) fprintf(stderr, 343 gettext("%s: kstat_read() failed for cpu %d: %s\n"), 344 cmdname, cpu_id, strerror(errno)); 345 exit(1); 346 } 347 348 if (phys_view) { 349 kstat_named_t *k = 350 (kstat_named_t *)kstat_data_lookup(ksp, "chip_id"); 351 chipdata *c; 352 353 if (k == NULL) { 354 (void) fprintf(stderr, 355 gettext("%s: Physical processor view " 356 "not supported\n"), 357 cmdname); 358 exit(1); 359 } 360 361 c = get_chipdata(k->value.i32); 362 363 if (visible && c->cpu_count != c->visible) { 364 /* 365 * We've already inventoried this chip. And the user 366 * specified a range of CPUIDs. So, now we just 367 * need to note that this is one of the chips to 368 * display. 369 */ 370 c->visible++; 371 return; 372 } 373 374 if (c->cpu_count == 0) { 375 char *str; 376 377 str = GETLONGSTR("implementation"); 378 (void) strlcpy(c->impl, str ? str : "(unknown)", 379 sizeof (c->impl)); 380 381 str = GETLONGSTR("brand"); 382 (void) strlcpy(c->brand, str ? str : "(unknown)", 383 sizeof (c->brand)); 384 385 chip_count++; 386 } 387 388 c->cpus[c->cpu_count] = cpu_id; 389 c->cpu_count++; 390 c->online += strcmp(GETSTR("state"), "on-line") == 0; 391 392 if (visible) 393 c->visible++; 394 return; 395 } 396 397 (void) strftime(start, sizeof (start), gettext("%m/%d/%Y %T"), 398 localtime((time_t *)&GETLONG("state_begin"))); 399 (void) strftime(curtime, sizeof (curtime), gettext("%m/%d/%Y %T"), 400 localtime(&now)); 401 402 if (verbosity == 0) { 403 (void) printf("%d\n", strcmp(GETSTR("state"), "on-line") == 0); 404 return; 405 } 406 if (verbosity == 1) { 407 (void) printf(gettext("%d\t%-8s since %s\n"), cpu_id, 408 get_cpu_state(GETSTR("state")), start); 409 return; 410 } 411 412 (void) printf(gettext("Status of virtual processor %d as of: %s\n"), 413 cpu_id, curtime); 414 (void) printf(gettext(" %s since %s.\n"), 415 get_cpu_state(GETSTR("state")), start); 416 417 if (GETLONG("clock_MHz") != 0) 418 (void) printf(gettext(" The %s processor operates at %d MHz"), 419 GETSTR("cpu_type"), GETLONG("clock_MHz")); 420 else 421 (void) printf(gettext(" The %s processor operates at an " 422 "unknown frequency"), GETSTR("cpu_type")); 423 424 if (GETSTR("fpu_type")[0] == '\0') 425 (void) printf(gettext(",\n\tand has no floating point " 426 "processor.\n")); 427 else if (strchr("aeiouy", GETSTR("fpu_type")[0])) 428 (void) printf(gettext(",\n\tand has an %s floating point " 429 "processor.\n"), GETSTR("fpu_type")); 430 else 431 (void) printf(gettext(",\n\tand has a %s floating point " 432 "processor.\n"), GETSTR("fpu_type")); 433 } 434 435 436 437 static void 438 print_cpuid_list(processorid_t *ids, int count) 439 { 440 int i; 441 char *separator = ""; 442 443 for (i = 0; i < count; ++i) { 444 /* 445 * if we're not at the ends, and the two adjacent numbers 446 * are sequential, then don't print this number. 447 */ 448 if (i > 0 && i < count-1 && 449 ids[i]-1 == ids[i-1] && 450 ids[i]+1 == ids[i+1]) { 451 separator = "-"; 452 continue; 453 } 454 455 (void) printf("%s%d", separator, ids[i]); 456 separator = " "; 457 } 458 } 459 460 461 /* 462 * get_chipdata(chip_id) 463 * Search for the chipdata structure (if allocated previously) that 464 * matches the chip_id argument and return a pointer to it. 465 * Allocate and return a pointer to a new chipdata if none 466 * is found matching the chip_id. The new chipdata will be inserted 467 * into the chiplist in ascending chip_id order. 468 * This routine allows us to support chip id (portid/deviceid) 469 * space which could be disjoint or/and larger from the logical cpuid space. 470 * Note that the number of chips visible to psrinfo is bounded by the 471 * number of (virtual) cpus. It is not possible to see more chips 472 * than (virtual) cpus. 473 */ 474 static chipdata * 475 get_chipdata(int chip_id) 476 { 477 chipdata *chip_ptr; 478 chipdata *newchip_ptr; 479 chipdata *prevchip_ptr = NULL; 480 481 if (chiplist == NULL) { 482 /* 483 * first time - allocate the chipdata structure for 484 * this new chip. 485 */ 486 if ((chiplist = calloc(1, sizeof (chipdata))) == NULL) { 487 (void) fprintf(stderr, 488 gettext("%s: Calloc failed\n"), cmdname); 489 exit(1); 490 } 491 chiplist->chip_id = chip_id; 492 chiplist->next = NULL; 493 return (chiplist); 494 } else { 495 /* 496 * look for the chipdata if it already 497 * been allocated. 498 */ 499 for (chip_ptr = chiplist; chip_ptr != NULL; 500 chip_ptr = chip_ptr->next) { 501 if (chip_ptr->chip_id == chip_id) { 502 /* found the allocated chipdata */ 503 return (chip_ptr); 504 } else if (chip_ptr->chip_id > chip_id) { 505 /* 506 * End search here since chiplist 507 * is sorted in ascending order 508 */ 509 break; 510 } 511 prevchip_ptr = chip_ptr; 512 } 513 514 /* 515 * Not found. Need to allocate a new chipdata 516 * structure and insert it into the chiplist in 517 * ascending chip_id order 518 */ 519 if ((newchip_ptr = calloc(1, sizeof (chipdata))) == NULL) { 520 (void) fprintf(stderr, 521 gettext("%s: Calloc failed\n"), cmdname); 522 exit(1); 523 } 524 newchip_ptr->chip_id = chip_id; 525 526 /* 527 * See if it needs to go in front of the list 528 * by looking at the result of the previous 529 * search, prevchip_ptr being NULL. 530 */ 531 if (prevchip_ptr == NULL) { 532 /* put it in front of the list */ 533 newchip_ptr->next = chiplist; 534 chiplist = newchip_ptr; 535 } else { 536 /* 537 * Insert the new chipdata in the list 538 * after the one pointed by prevchip_ptr 539 */ 540 newchip_ptr->next = prevchip_ptr->next; 541 prevchip_ptr->next = newchip_ptr; 542 } 543 return (newchip_ptr); 544 } 545 } 546