xref: /illumos-gate/usr/src/cmd/psrinfo/psrinfo.c (revision 9b9d39d2a32ff806d2431dbcc50968ef1e6d46b2)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
14  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
15  * Copyright 2019 Joyent, Inc.
16  * Copyright 2022 Oxide Computer Co.
17  */
18 
19 /*
20  * This implements psrinfo(8), a utility to report various information
21  * about processors, cores, and threads (virtual cpus).  This is mostly
22  * intended for human consumption - this utility doesn't do much more than
23  * simply process kstats for human readability.
24  *
25  * All the relevant kstats are in the cpu_info kstat module.
26  */
27 
28 #include <sys/sysmacros.h>
29 
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <kstat.h>
36 #include <libintl.h>
37 #include <locale.h>
38 #include <libgen.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <err.h>
42 
43 #include <libdevinfo.h>
44 
45 #define	_(x)	gettext(x)
46 #if XGETTEXT
47 	/* These CPU states are here for benefit of xgettext */
48 	_("on-line")
49 	_("off-line")
50 	_("faulted")
51 	_("powered-off")
52 	_("no-intr")
53 	_("spare")
54 	_("unknown")
55 	_("disabled")
56 #endif
57 
58 /*
59  * We deal with sorted linked lists, where the sort key is usually the
60  * cpu id, core id, or chip id.  We generalize this with simple node.
61  */
62 struct link {
63 	long		l_id;
64 	struct link	*l_next;
65 	void		*l_ptr;
66 };
67 
68 /*
69  * A physical chip.  A chip can contain multiple cores and virtual cpus.
70  */
71 struct pchip {
72 	struct link	p_link;
73 	int		p_ncore;
74 	int		p_nvcpu;
75 	struct link	*p_cores;
76 	struct link	*p_vcpus;
77 	int		p_doit;
78 };
79 
80 struct core {
81 	struct link	c_link;
82 	struct link	c_link_pchip;
83 
84 	int		c_nvcpu;
85 	int		c_doit;
86 
87 	struct pchip	*c_pchip;
88 	struct link	*c_vcpus;
89 };
90 
91 struct vcpu {
92 	struct link	v_link;
93 
94 	struct link	v_link_core;
95 	struct link	v_link_pchip;
96 
97 	int		v_doit;
98 
99 	struct pchip	*v_pchip;
100 	struct core	*v_core;
101 
102 	char		*v_state;
103 	long		v_state_begin;
104 	char		*v_cpu_type;
105 	char		*v_fpu_type;
106 	long		v_clock_mhz;
107 	long		v_pchip_id;	/* 1 per socket */
108 	char		*v_impl;
109 	char		*v_brand;
110 	char		*v_socket;
111 	long		v_core_id;	/* n per chip_id */
112 };
113 
114 static struct link *pchips = NULL;
115 static struct link *cores = NULL;
116 static struct link *vcpus = NULL;
117 
118 static uint_t nr_cpus;
119 static uint_t nr_cores;
120 static uint_t nr_chips;
121 
122 static const char *cmdname;
123 
124 static void
125 usage(char *msg)
126 {
127 	if (msg != NULL)
128 		(void) fprintf(stderr, "%s: %s\n", cmdname, msg);
129 	(void) fprintf(stderr, _("usage: \n"
130 	    "\t%s -r propname\n"
131 	    "\t%s [-v] [-p] [processor_id ...]\n"
132 	    "\t%s -s [-p] processor_id\n"
133 	    "\t%s -t [-S <state> | -c | -p]\n"),
134 	    cmdname, cmdname, cmdname, cmdname);
135 	exit(2);
136 }
137 
138 /* like perror, but includes the command name */
139 static void
140 die(const char *msg)
141 {
142 	(void) fprintf(stderr, "%s: %s: %s\n", cmdname, msg, strerror(errno));
143 	exit(2);
144 }
145 
146 static char *
147 mystrdup(const char *src)
148 {
149 	char *dst;
150 
151 	if ((dst = strdup(src)) == NULL)
152 		die(_("strdup() failed"));
153 	return (dst);
154 }
155 
156 static void *
157 zalloc(size_t size)
158 {
159 	void *ptr;
160 
161 	if ((ptr = calloc(1, size)) == NULL)
162 		die(_("calloc() failed"));
163 	return (ptr);
164 }
165 
166 /*
167  * Insert a new node on a list, at the insertion point given.
168  */
169 static void
170 ins_link(struct link **ins, struct link *item)
171 {
172 	item->l_next = *ins;
173 	*ins = item;
174 }
175 
176 /*
177  * Find an id on a sorted list.  If the requested id is not found,
178  * then the insertpt will be set (if not null) to the location where
179  * a new node should be inserted with ins_link (see above).
180  */
181 static void *
182 find_link(void *list, int id, struct link ***insertpt)
183 {
184 	struct link **ins = list;
185 	struct link *l;
186 
187 	while ((l = *ins) != NULL) {
188 		if (l->l_id == id)
189 			return (l->l_ptr);
190 		if (l->l_id > id)
191 			break;
192 		ins = &l->l_next;
193 	}
194 	if (insertpt != NULL)
195 		*insertpt = ins;
196 	return (NULL);
197 }
198 
199 /*
200  * Print the linked list of ids in parens, taking care to collapse
201  * ranges, so instead of (0 1 2 3) it should print (0-3).
202  */
203 static void
204 print_links(struct link *l)
205 {
206 	int	start = -1;
207 	int	end = 0;
208 
209 	(void) printf(" (");
210 	while (l != NULL) {
211 		if (start < 0) {
212 			start = l->l_id;
213 		}
214 		end = l->l_id;
215 		if ((l->l_next == NULL) ||
216 		    (l->l_next->l_id > (l->l_id + 1))) {
217 			/* end of the contiguous group */
218 			if (start == end) {
219 				(void) printf("%d", start);
220 			} else {
221 				(void) printf("%d-%d", start, end);
222 			}
223 			if (l->l_next)
224 				(void) printf(" ");
225 			start = -1;
226 		}
227 		l = l->l_next;
228 	}
229 	(void) printf(")");
230 }
231 
232 static const char *
233 timestr(long t)
234 {
235 	static char buffer[256];
236 	(void) strftime(buffer, sizeof (buffer), _("%m/%d/%Y %T"),
237 	    localtime(&t));
238 	return (buffer);
239 }
240 
241 static void
242 print_vp(int nspec)
243 {
244 	struct pchip *chip;
245 	struct core *core;
246 	struct vcpu *vcpu;
247 	struct link *l1, *l2;
248 	int len;
249 	for (l1 = pchips; l1; l1 = l1->l_next) {
250 
251 		chip = l1->l_ptr;
252 
253 		if ((nspec != 0) && (chip->p_doit == 0))
254 			continue;
255 
256 		vcpu = chip->p_vcpus->l_ptr;
257 
258 		/*
259 		 * Note that some of the way these strings are broken up are
260 		 * to accommodate the legacy translations so that we won't
261 		 * have to retranslate for this utility.
262 		 */
263 		if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) {
264 			(void) printf(_("%s has %d virtual %s"),
265 			    _("The physical processor"),
266 			    chip->p_nvcpu,
267 			    chip->p_nvcpu > 1 ?
268 			    _("processors") :
269 			    _("processor"));
270 		} else {
271 			(void) printf(_("%s has %d %s and %d virtual %s"),
272 			    _("The physical processor"),
273 			    chip->p_ncore, _("cores"),
274 			    chip->p_nvcpu,
275 			    chip->p_nvcpu > 1 ?
276 			    _("processors") : _("processor"));
277 		}
278 
279 		print_links(chip->p_vcpus);
280 		(void) putchar('\n');
281 
282 		if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) {
283 			if (strlen(vcpu->v_impl)) {
284 				(void) printf("  %s\n", vcpu->v_impl);
285 			}
286 			if (((len = strlen(vcpu->v_brand)) != 0) &&
287 			    (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0))
288 				(void) printf("\t%s", vcpu->v_brand);
289 		} else {
290 			for (l2 = chip->p_cores; l2; l2 = l2->l_next) {
291 				core = l2->l_ptr;
292 				(void) printf(_("  %s has %d virtual %s"),
293 				    _("The core"),
294 				    core->c_nvcpu,
295 				    chip->p_nvcpu > 1 ?
296 				    _("processors") : _("processor"));
297 				print_links(core->c_vcpus);
298 				(void) putchar('\n');
299 			}
300 			if (strlen(vcpu->v_impl)) {
301 				(void) printf("    %s\n", vcpu->v_impl);
302 			}
303 			if (((len = strlen(vcpu->v_brand)) != 0) &&
304 			    (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0))
305 				(void) printf("      %s", vcpu->v_brand);
306 		}
307 		if (strcmp(vcpu->v_socket, "Unknown") != 0) {
308 			(void) printf("\t[ %s: %s ]", _("Socket"),
309 			    vcpu->v_socket);
310 		}
311 		(void) putchar('\n');
312 	}
313 }
314 
315 static void
316 print_ps(void)
317 {
318 	int online = 1;
319 	struct pchip *p = NULL;
320 	struct vcpu *v;
321 	struct link *l;
322 
323 	/*
324 	 * Report "1" if all cpus colocated on the same chip are online.
325 	 */
326 	for (l = pchips; l != NULL; l = l->l_next) {
327 		p = l->l_ptr;
328 		if (p->p_doit)
329 			break;
330 	}
331 	if (p == NULL)
332 		return;	/* should never happen! */
333 	for (l = p->p_vcpus; l != NULL; l = l->l_next) {
334 		v = l->l_ptr;
335 		if (strcmp(v->v_state, "on-line") != 0) {
336 			online = 0;
337 			break;
338 		}
339 	}
340 
341 	(void) printf("%d\n", online);
342 }
343 
344 static void
345 print_s(void)
346 {
347 	struct link *l;
348 
349 	/*
350 	 * Find the processor (there will be only one) that we selected,
351 	 * and report whether or not it is online.
352 	 */
353 	for (l = vcpus; l != NULL; l = l->l_next) {
354 		struct vcpu *v = l->l_ptr;
355 		if (v->v_doit) {
356 			(void) printf("%d\n",
357 			    strcmp(v->v_state, "on-line") == 0 ? 1 : 0);
358 			return;
359 		}
360 	}
361 }
362 
363 static void
364 print_p(int nspec)
365 {
366 	struct		link *l1, *l2;
367 	int		online = 0;
368 
369 	/*
370 	 * Print the number of physical packages with at least one processor
371 	 * online.
372 	 */
373 	for (l1 = pchips; l1 != NULL; l1 = l1->l_next) {
374 		struct pchip *p = l1->l_ptr;
375 		if ((nspec == 0) || (p->p_doit)) {
376 
377 			for (l2 = p->p_vcpus; l2 != NULL; l2 = l2->l_next) {
378 				struct vcpu *v = l2->l_ptr;
379 				if (strcmp(v->v_state, "on-line") == 0) {
380 					online++;
381 					break;
382 				}
383 			}
384 		}
385 	}
386 	(void) printf("%d\n", online);
387 }
388 
389 static void
390 print_v(int nspec)
391 {
392 	struct link	*l;
393 
394 	for (l = vcpus; l != NULL; l = l->l_next) {
395 		struct vcpu *v = l->l_ptr;
396 
397 		if ((nspec != 0) && (!v->v_doit))
398 			continue;
399 		(void) printf(_("Status of virtual processor %d as of: "),
400 		    l->l_id);
401 		(void) printf("%s\n", timestr(time(NULL)));
402 		(void) printf(_("  %s since %s.\n"),
403 		    _(v->v_state), timestr(v->v_state_begin));
404 		if (v->v_clock_mhz) {
405 			(void) printf(
406 			    _("  The %s processor operates at %llu MHz,\n"),
407 			    v->v_cpu_type, (unsigned long long)v->v_clock_mhz);
408 		} else {
409 			(void) printf(
410 			    _("  The %s processor operates at " \
411 			    "an unknown frequency,\n"), v->v_cpu_type);
412 		}
413 		switch (*v->v_fpu_type) {
414 		case '\0':
415 			(void) printf(
416 			    _("\tand has no floating point processor.\n"));
417 			break;
418 		case 'a': case 'A':
419 		case 'e': case 'E':
420 		case 'i': case 'I':
421 		case 'o': case 'O':
422 		case 'u': case 'U':
423 		case 'y': case 'Y':
424 			(void) printf(
425 			    _("\tand has an %s floating point processor.\n"),
426 			    v->v_fpu_type);
427 			break;
428 		default:
429 			(void) printf(
430 			    _("\tand has a %s floating point processor.\n"),
431 			    v->v_fpu_type);
432 			break;
433 		}
434 	}
435 }
436 
437 static void
438 print_normal(int nspec)
439 {
440 	struct link	*l;
441 	struct vcpu	*v;
442 
443 	for (l = vcpus; l != NULL; l = l->l_next) {
444 		v = l->l_ptr;
445 		if ((nspec == 0) || (v->v_doit)) {
446 			(void) printf(_("%d\t%-8s  since %s\n"),
447 			    l->l_id, _(v->v_state), timestr(v->v_state_begin));
448 		}
449 	}
450 }
451 
452 static bool
453 valid_propname(const char *propname)
454 {
455 	size_t i;
456 
457 	const char *props[] = {
458 		"smt_enabled",
459 	};
460 
461 	for (i = 0; i < ARRAY_SIZE(props); i++) {
462 		if (strcmp(propname, props[i]) == 0)
463 			break;
464 	}
465 
466 	return (i != ARRAY_SIZE(props));
467 }
468 
469 static void
470 read_property(const char *propname)
471 {
472 	di_prop_t prop = DI_PROP_NIL;
473 	di_node_t root_node;
474 	bool show_all = strcmp(propname, "all") == 0;
475 
476 	if (!show_all && !valid_propname(propname))
477 		errx(EXIT_FAILURE, _("unknown CPU property %s"), propname);
478 
479 	if ((root_node = di_init("/", DINFOPROP)) == NULL)
480 		err(EXIT_FAILURE, _("failed to read root node"));
481 
482 	while ((prop = di_prop_sys_next(root_node, prop)) != DI_PROP_NIL) {
483 		const char *name = di_prop_name(prop);
484 		char *val;
485 		int nr_vals;
486 
487 		if (!valid_propname(name))
488 			continue;
489 
490 		if (!show_all && strcmp(di_prop_name(prop), propname) != 0)
491 			continue;
492 
493 		if ((nr_vals = di_prop_strings(prop, &val)) < 1) {
494 			err(EXIT_FAILURE,
495 			    _("error reading property %s"), name);
496 		} else if (nr_vals != 1) {
497 			errx(EXIT_FAILURE, _("invalid property %s"), name);
498 		}
499 
500 		printf("%s=%s\n", name, val);
501 
502 		if (!show_all)
503 			exit(EXIT_SUCCESS);
504 	}
505 
506 	if (!show_all)
507 		errx(EXIT_FAILURE, _("property %s was not found"), propname);
508 
509 	di_fini(root_node);
510 }
511 
512 static void
513 print_total(int opt_c, int opt_p, const char *opt_S)
514 {
515 	uint_t count = 0;
516 
517 	if (opt_c) {
518 		printf("%u\n", nr_cores);
519 		return;
520 	} else if (opt_p) {
521 		printf("%u\n", nr_chips);
522 		return;
523 	} else if (opt_S == NULL || strcmp(opt_S, "all") == 0) {
524 		printf("%u\n", nr_cpus);
525 		return;
526 	}
527 
528 
529 	for (struct link *l = vcpus; l != NULL; l = l->l_next) {
530 		struct vcpu *v = l->l_ptr;
531 		if (strcmp(opt_S, v->v_state) == 0)
532 			count++;
533 	}
534 
535 	printf("%u\n", count);
536 }
537 
538 int
539 main(int argc, char **argv)
540 {
541 	kstat_ctl_t	*kc;
542 	kstat_t		*ksp;
543 	kstat_named_t	*knp;
544 	struct vcpu	*vc;
545 	struct core	*core;
546 	struct pchip	*chip;
547 	struct link	**ins;
548 	char		*s;
549 	int		nspec;
550 	int		optc;
551 	int		opt_c = 0;
552 	int		opt_p = 0;
553 	const char	*opt_r = NULL;
554 	const char	*opt_S = NULL;
555 	int		opt_s = 0;
556 	int		opt_t = 0;
557 	int		opt_v = 0;
558 	int		ex = 0;
559 
560 	cmdname = basename(argv[0]);
561 
562 
563 	(void) setlocale(LC_ALL, "");
564 #if !defined(TEXT_DOMAIN)
565 #define	TEXT_DOMAIN	"SYS_TEST"
566 #endif
567 	(void) textdomain(TEXT_DOMAIN);
568 
569 	/* collect the kstats */
570 	if ((kc = kstat_open()) == NULL)
571 		die(_("kstat_open() failed"));
572 
573 	if ((ksp = kstat_lookup(kc, "cpu_info", -1, NULL)) == NULL)
574 		die(_("kstat_lookup() failed"));
575 
576 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
577 
578 		if (strcmp(ksp->ks_module, "cpu_info") != 0)
579 			continue;
580 		if (kstat_read(kc, ksp, NULL) == -1)
581 			die(_("kstat_read() failed"));
582 
583 		vc = find_link(&vcpus, ksp->ks_instance, &ins);
584 		if (vc == NULL) {
585 			vc = zalloc(sizeof (struct vcpu));
586 			vc->v_link.l_id = ksp->ks_instance;
587 			vc->v_link_core.l_id = ksp->ks_instance;
588 			vc->v_link_pchip.l_id = ksp->ks_instance;
589 			vc->v_link.l_ptr = vc;
590 			vc->v_link_core.l_ptr = vc;
591 			vc->v_link_pchip.l_ptr = vc;
592 			ins_link(ins, &vc->v_link);
593 			nr_cpus++;
594 		}
595 
596 		if ((knp = kstat_data_lookup(ksp, "state")) != NULL) {
597 			vc->v_state = mystrdup(knp->value.c);
598 		} else {
599 			vc->v_state = "unknown";
600 		}
601 
602 		if ((knp = kstat_data_lookup(ksp, "cpu_type")) != NULL) {
603 			vc->v_cpu_type = mystrdup(knp->value.c);
604 		}
605 		if ((knp = kstat_data_lookup(ksp, "fpu_type")) != NULL) {
606 			vc->v_fpu_type = mystrdup(knp->value.c);
607 		}
608 
609 		if ((knp = kstat_data_lookup(ksp, "state_begin")) != NULL) {
610 			vc->v_state_begin = knp->value.l;
611 		}
612 
613 		if ((knp = kstat_data_lookup(ksp, "clock_MHz")) != NULL) {
614 			vc->v_clock_mhz = knp->value.l;
615 		}
616 
617 		if ((knp = kstat_data_lookup(ksp, "brand")) == NULL) {
618 			vc->v_brand = _("(unknown)");
619 		} else {
620 			vc->v_brand = mystrdup(knp->value.str.addr.ptr);
621 		}
622 
623 		if ((knp = kstat_data_lookup(ksp, "socket_type")) == NULL) {
624 			vc->v_socket = "Unknown";
625 		} else {
626 			vc->v_socket = mystrdup(knp->value.str.addr.ptr);
627 		}
628 
629 		if ((knp = kstat_data_lookup(ksp, "implementation")) == NULL) {
630 			vc->v_impl = _("(unknown)");
631 		} else {
632 			vc->v_impl = mystrdup(knp->value.str.addr.ptr);
633 		}
634 		/*
635 		 * Legacy code removed the chipid and cpuid fields... we
636 		 * do the same for compatibility.  Note that the original
637 		 * pattern is a bit strange, and we have to emulate this because
638 		 * on SPARC we *do* emit these.  The original pattern we are
639 		 * emulating is: $impl =~ s/(cpuid|chipid)\s*\w+\s+//;
640 		 */
641 		if ((s = strstr(vc->v_impl, "chipid")) != NULL) {
642 			char *x = s + strlen("chipid");
643 			while (isspace(*x))
644 				x++;
645 			if ((!isalnum(*x)) && (*x != '_'))
646 				goto nochipid;
647 			while (isalnum(*x) || (*x == '_'))
648 				x++;
649 			if (!isspace(*x))
650 				goto nochipid;
651 			while (isspace(*x))
652 				x++;
653 			(void) strcpy(s, x);
654 		}
655 nochipid:
656 		if ((s = strstr(vc->v_impl, "cpuid")) != NULL) {
657 			char *x = s + strlen("cpuid");
658 			while (isspace(*x))
659 				x++;
660 			if ((!isalnum(*x)) && (*x != '_'))
661 				goto nocpuid;
662 			while (isalnum(*x) || (*x == '_'))
663 				x++;
664 			if (!isspace(*x))
665 				goto nocpuid;
666 			while (isspace(*x))
667 				x++;
668 			(void) strcpy(s, x);
669 		}
670 nocpuid:
671 
672 		if ((knp = kstat_data_lookup(ksp, "chip_id")) != NULL)
673 			vc->v_pchip_id = knp->value.l;
674 		chip = find_link(&pchips, vc->v_pchip_id, &ins);
675 		if (chip == NULL) {
676 			chip = zalloc(sizeof (struct pchip));
677 			chip->p_link.l_id = vc->v_pchip_id;
678 			chip->p_link.l_ptr = chip;
679 			ins_link(ins, &chip->p_link);
680 			nr_chips++;
681 		}
682 		vc->v_pchip = chip;
683 
684 		if ((knp = kstat_data_lookup(ksp, "core_id")) != NULL)
685 			vc->v_core_id = knp->value.l;
686 		core = find_link(&cores, vc->v_core_id, &ins);
687 		if (core == NULL) {
688 			core = zalloc(sizeof (struct core));
689 			core->c_link.l_id = vc->v_core_id;
690 			core->c_link.l_ptr = core;
691 			core->c_link_pchip.l_id = vc->v_core_id;
692 			core->c_link_pchip.l_ptr = core;
693 			core->c_pchip = chip;
694 			ins_link(ins, &core->c_link);
695 			chip->p_ncore++;
696 			(void) find_link(&chip->p_cores, core->c_link.l_id,
697 			    &ins);
698 			ins_link(ins, &core->c_link_pchip);
699 			nr_cores++;
700 		}
701 		vc->v_core = core;
702 
703 
704 
705 		/* now put other linkages in place */
706 		(void) find_link(&chip->p_vcpus, vc->v_link.l_id, &ins);
707 		ins_link(ins, &vc->v_link_pchip);
708 		chip->p_nvcpu++;
709 
710 		(void) find_link(&core->c_vcpus, vc->v_link.l_id, &ins);
711 		ins_link(ins, &vc->v_link_core);
712 		core->c_nvcpu++;
713 	}
714 
715 	(void) kstat_close(kc);
716 
717 	nspec = 0;
718 
719 	while ((optc = getopt(argc, argv, "cpr:S:stv")) != EOF) {
720 		switch (optc) {
721 		case 'c':
722 			opt_c = 1;
723 			break;
724 		case 'p':
725 			opt_p = 1;
726 			break;
727 		case 'r':
728 			opt_r = optarg;
729 			break;
730 		case 'S':
731 			opt_S = optarg;
732 			break;
733 		case 's':
734 			opt_s = 1;
735 			break;
736 		case 't':
737 			opt_t = 1;
738 			break;
739 		case 'v':
740 			opt_v = 1;
741 			break;
742 		default:
743 			usage(NULL);
744 		}
745 	}
746 
747 	if (opt_r != NULL) {
748 		if (optind != argc)
749 			usage(_("cannot specify CPUs with -r"));
750 		if (opt_c || opt_p || opt_S != NULL || opt_s || opt_t || opt_v)
751 			usage(_("cannot specify other arguments with -r"));
752 
753 		read_property(opt_r);
754 		return (EXIT_SUCCESS);
755 	}
756 
757 	if (opt_t != 0) {
758 		if (optind != argc)
759 			usage(_("cannot specify CPUs with -t"));
760 		if (opt_s || opt_v)
761 			usage(_("cannot specify -s or -v with -t"));
762 		if (opt_S != NULL && (opt_c || opt_p))
763 			usage(_("cannot specify CPU state with -c or -p"));
764 		if (opt_c && opt_p)
765 			usage(_("cannot specify -c and -p"));
766 
767 		print_total(opt_c, opt_p, opt_S);
768 		return (EXIT_SUCCESS);
769 	}
770 
771 	if (opt_S != NULL || opt_c)
772 		usage(_("cannot specify -S or -c without -t"));
773 
774 	while (optind < argc) {
775 		long id;
776 		char *eptr;
777 		struct link *l;
778 		id = strtol(argv[optind], &eptr, 10);
779 		l = find_link(&vcpus, id, NULL);
780 		if ((*eptr != '\0') || (l == NULL)) {
781 			(void) fprintf(stderr,
782 			    _("%s: processor %s: Invalid argument\n"),
783 			    cmdname, argv[optind]);
784 			ex = 2;
785 		} else {
786 			((struct vcpu *)l->l_ptr)->v_doit = 1;
787 			((struct vcpu *)l->l_ptr)->v_pchip->p_doit = 1;
788 			((struct vcpu *)l->l_ptr)->v_core->c_doit = 1;
789 		}
790 		nspec++;
791 		optind++;
792 	}
793 
794 	if (opt_s && opt_v) {
795 		usage(_("options -s and -v are mutually exclusive"));
796 	}
797 	if (opt_s && nspec != 1) {
798 		usage(_("must specify exactly one processor if -s used"));
799 	}
800 	if (opt_v && opt_p) {
801 		print_vp(nspec);
802 	} else if (opt_s && opt_p) {
803 		print_ps();
804 	} else if (opt_p) {
805 		print_p(nspec);
806 	} else if (opt_v) {
807 		print_v(nspec);
808 	} else if (opt_s) {
809 		print_s();
810 	} else {
811 		print_normal(nspec);
812 	}
813 
814 	return (ex);
815 }
816