xref: /illumos-gate/usr/src/lib/cfgadm_plugins/sbd/common/ap_rcm.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <macros.h>
33 #include <errno.h>
34 #include <kstat.h>
35 #include <sys/kmem.h>
36 #include <dlfcn.h>
37 #include <libdevinfo.h>
38 #include <librcm.h>
39 #include <libintl.h>
40 #define	CFGA_PLUGIN_LIB
41 #include <config_admin.h>
42 #include <sys/sbd_ioctl.h>
43 #include "ap.h"
44 
45 typedef int32_t	cpuid_t;
46 
47 typedef struct {
48 	int valid;
49 	cfga_stat_t ostate;
50 	int ncap;
51 	union {
52 		long npages;
53 		cpuid_t cpuid[SBD_MAX_CORES_PER_CMP];
54 	} type;
55 } cap_info_t;
56 
57 typedef struct {
58 	int firstcm;		/* first component to operate on */
59 	int lastcm;		/* last component to operate on */
60 	void *lib;
61 	char **rlist;
62 	cap_info_t *capinfo;
63 	int ncpus;		/* # of CPUs in cpuids list */
64 	cpuid_t *cpuids;	/* List of cpuids */
65 	int capcpus;		/* # of CPUs - tracking capacity */
66 	int cappages;		/* # of memory pages - tracking capacity */
67 	rcm_handle_t *hd;
68 	rcm_info_t *rinfo;
69 	rcm_info_tuple_t *infot;
70 	int (*alloc_handle)(char *, uint_t, void *, rcm_handle_t **);
71 	void (*free_handle)(rcm_handle_t *);
72 	int (*get_info)(rcm_handle_t *, char *, uint_t, rcm_info_t **);
73 	void (*free_info)(rcm_info_t *);
74 	rcm_info_tuple_t *(*info_next)(rcm_info_t *, rcm_info_tuple_t *);
75 	int (*info_state)(rcm_info_tuple_t *);
76 	pid_t (*info_pid)(rcm_info_tuple_t *);
77 	const char *(*info_error)(rcm_info_tuple_t *);
78 	const char *(*info_info)(rcm_info_tuple_t *);
79 	const char *(*info_rsrc)(rcm_info_tuple_t *);
80 	int (*request_offline_list)(rcm_handle_t *, char **, uint_t,
81 	    rcm_info_t **);
82 	int (*notify_online_list)(rcm_handle_t *, char **, uint_t,
83 	    rcm_info_t **);
84 	int (*request_suspend)(rcm_handle_t *, char *, uint_t, timespec_t *,
85 		rcm_info_t **);
86 	int (*notify_resume)(rcm_handle_t *, char *, uint_t, rcm_info_t **);
87 	int (*notify_remove_list)(rcm_handle_t *, char **, uint_t,
88 	    rcm_info_t **);
89 	int (*request_capacity_change)(rcm_handle_t *, char *, uint_t,
90 		nvlist_t *, rcm_info_t **);
91 	int (*notify_capacity_change)(rcm_handle_t *, char *, uint_t,
92 		nvlist_t *, rcm_info_t **);
93 } rcmd_t;
94 
95 static char *
96 ap_rcm_ops[] = {
97 	"rcm_alloc_handle",
98 	"rcm_free_handle",
99 	"rcm_get_info",
100 	"rcm_free_info",
101 	"rcm_info_next",
102 	"rcm_info_state",
103 	"rcm_info_pid",
104 	"rcm_info_error",
105 	"rcm_info_info",
106 	"rcm_info_rsrc",
107 	"rcm_request_offline_list",
108 	"rcm_notify_online_list",
109 	"rcm_request_suspend",
110 	"rcm_notify_resume",
111 	"rcm_notify_remove_list",
112 	"rcm_request_capacity_change",
113 	"rcm_notify_capacity_change",
114 	NULL
115 };
116 
117 #define	ALLOC_HANDLE		0
118 #define	FREE_HANDLE		1
119 #define	GET_INFO		2
120 #define	FREE_INFO		3
121 #define	INFO_TUPLE_NEXT		4
122 #define	INFO_TUPLE_STATE	5
123 #define	INFO_TUPLE_ID		6
124 #define	INFO_TUPLE_ERROR	7
125 #define	INFO_TUPLE_INFO		8
126 #define	INFO_TUPLE_RSRC		9
127 #define	REQUEST_OFFLINE		10
128 #define	NOTIFY_ONLINE		11
129 #define	REQUEST_SUSPEND		12
130 #define	NOTIFY_RESUME		13
131 #define	NOTIFY_REMOVE		14
132 #define	REQUEST_CAP_CHANGE	15
133 #define	NOTIFY_CAP_CHANGE	16
134 
135 /*
136  * There is no consumer for SUNW_OS. This is defined here
137  * for generic OS quiescence.
138  */
139 #define	OS	"SUNW_OS"	/* XXX */
140 
141 /* Max width of an RCM formatted message line */
142 #define	RCM_MAX_FORMAT	80
143 
144 #ifdef	__sparcv9
145 #define	RCMLIB	"/lib/sparcv9/librcm.so";
146 #elif defined(__amd64)
147 #define	RCMLIB	"/lib/amd64/librcm.so";
148 #else
149 #define	RCMLIB	"/lib/librcm.so";
150 #endif
151 
152 static cfga_err_t
153 ap_capinfo(apd_t *a, int firstcm, int lastcm, cap_info_t **capinfo)
154 {
155 	int cm;
156 	int ncm;
157 	void *cap;
158 	int *ncap;
159 	cfga_stat_t *os;
160 	cap_info_t *cinfo, *cp;
161 
162 	DBG("ap_capinfo(%p)\n", (void *)a);
163 
164 	if (capinfo == NULL) {
165 		ap_err(a, ERR_PLUGIN, "null capinfo");
166 		return (CFGA_LIB_ERROR);
167 	}
168 
169 	/*
170 	 * Assume there are components with valid capacity
171 	 * information and allocate space for them.  If there
172 	 * are none at the end, free the allocated space.
173 	 */
174 	ncm = lastcm - firstcm + 1;
175 
176 	cinfo = (cap_info_t *)calloc(ncm, sizeof (cap_info_t));
177 	if (cinfo == NULL) {
178 		ap_err(a, ERR_NOMEM);
179 		return (CFGA_LIB_ERROR);
180 	}
181 
182 	*capinfo = NULL;
183 	ncm = 0;
184 	for (cp = cinfo, cm = firstcm; cm <= lastcm; cm++, cp++) {
185 		os = &cp->ostate;
186 		ncap = &cp->ncap;
187 
188 		switch (ap_cm_type(a, cm)) {
189 		case AP_CPU:
190 		case AP_CMP:
191 			cap = (void *)(cp->type.cpuid);
192 			break;
193 		case AP_MEM:
194 			cap = (void *)&(cp->type.npages);
195 			break;
196 		default:
197 			continue;
198 		}
199 		/*
200 		 * Remember which components have valid
201 		 * capacity information.
202 		 */
203 		if (ap_cm_capacity(a, cm, cap, ncap, os)) {
204 			cp->valid = 1;
205 			ncm++;
206 		}
207 	}
208 
209 	if (ncm == 0)
210 		free(cinfo);
211 	else
212 		*capinfo = cinfo;
213 
214 	return (CFGA_OK);
215 }
216 
217 static int
218 getsyscpuids(int *ncpuids, cpuid_t **cpuids)
219 {
220 	int		ncpu;
221 	int		maxncpu;
222 	kstat_t		*ksp;
223 	kstat_ctl_t	*kc = NULL;
224 	cpuid_t		*cp;
225 
226 	DBG("getsyscpuids\n");
227 
228 	if ((maxncpu = sysconf(_SC_NPROCESSORS_MAX)) == -1 ||
229 	    (kc = kstat_open()) == NULL ||
230 	    (cp = (cpuid_t *)calloc(maxncpu, sizeof (cpuid_t))) == NULL) {
231 		/* if calloc failed, clean up kstats */
232 		if (kc != NULL) {
233 			(void) kstat_close(kc);
234 		}
235 		return (-1);
236 	}
237 
238 	DBG("syscpuids: ");
239 	for (ncpu = 0, ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
240 		if (strcmp(ksp->ks_module, "cpu_info") == 0) {
241 			cp[ncpu++] = ksp->ks_instance;
242 			DBG("%d ", ksp->ks_instance);
243 		}
244 	}
245 	DBG("\n");
246 
247 	(void) kstat_close(kc);
248 	*cpuids = cp;
249 	*ncpuids = ncpu;
250 	return (0);
251 }
252 
253 cfga_err_t
254 ap_rcm_init(apd_t *a)
255 {
256 	int i;
257 	char *err;
258 	char *rcmlib;
259 	void *sym;
260 	void *lib;
261 	char **op;
262 	rcmd_t *rcm;
263 	cfga_err_t rc;
264 	struct stat buf;
265 
266 	DBG("ap_rcm_init(%p)\n", (void *)a);
267 
268 	/*
269 	 * If the initial command is status, or the RCM feature is not
270 	 * available, or the RCM interface has already been initialized,
271 	 * just return.
272 	 */
273 
274 	if ((a->statonly != 0) || (a->norcm != 0) ||
275 	    ((rcm = (rcmd_t *)a->rcm) != NULL)) {
276 		return (CFGA_OK);
277 	}
278 
279 	rcmlib = RCMLIB;
280 	rc = CFGA_LIB_ERROR;
281 
282 	DBG("Looking for %s\n", rcmlib);
283 	/*
284 	 * If the library is not present, there is nothing more
285 	 * to do.  The RCM offline/suspend steps become no-ops
286 	 * in that case.
287 	 */
288 	if (stat(rcmlib, &buf) == -1) {
289 		if (errno == ENOENT) {
290 			a->norcm++;
291 			ap_msg(a, MSG_NORCM);
292 			return (CFGA_OK);
293 		} else {
294 			ap_err(a, ERR_STAT, rcmlib);
295 			return (rc);
296 		}
297 	}
298 	DBG("%s found\n", rcmlib);
299 
300 	if ((a->rcm = calloc(1, sizeof (rcmd_t))) == NULL) {
301 		ap_err(a, ERR_NOMEM);
302 		return (rc);
303 	}
304 
305 	rcm = (rcmd_t *)a->rcm;
306 
307 	if ((lib = dlopen(rcmlib, RTLD_NOW)) == NULL) {
308 		if ((err = dlerror()) != NULL)
309 			err = strdup(err);
310 		ap_err(a, ERR_LIB_OPEN, rcmlib, err);
311 		if (err != NULL)
312 			free(err);
313 		return (rc);
314 	}
315 
316 	rcm->lib = lib;
317 
318 	for (i = 0, op = ap_rcm_ops; *op != NULL; op++, i++) {
319 		if ((sym = dlsym(lib, *op)) == NULL) {
320 			ap_err(a, ERR_LIB_SYM, rcmlib, *op);
321 			return (rc);
322 		}
323 		switch (i) {
324 		case ALLOC_HANDLE:
325 			rcm->alloc_handle = (int(*)
326 			    (char *, uint_t, void *, rcm_handle_t **))sym;
327 			break;
328 		case FREE_HANDLE:
329 			rcm->free_handle = (void (*)(rcm_handle_t *))sym;
330 			break;
331 		case GET_INFO:
332 			rcm->get_info = (int (*)
333 			    (rcm_handle_t *, char *, uint_t, rcm_info_t **))sym;
334 			break;
335 		case FREE_INFO:
336 			rcm->free_info = (void (*)(rcm_info_t *))sym;
337 			break;
338 		case INFO_TUPLE_NEXT:
339 			rcm->info_next = (rcm_info_tuple_t *(*)
340 			    (rcm_info_t *, rcm_info_tuple_t *))sym;
341 			break;
342 		case INFO_TUPLE_STATE:
343 			rcm->info_state = (int (*)(rcm_info_tuple_t *))sym;
344 			break;
345 		case INFO_TUPLE_ID:
346 			rcm->info_pid = (pid_t (*)(rcm_info_tuple_t *))sym;
347 			break;
348 		case INFO_TUPLE_ERROR:
349 			rcm->info_error = (const char *(*)
350 			    (rcm_info_tuple_t *))sym;
351 			break;
352 		case INFO_TUPLE_INFO:
353 			rcm->info_info = (const char *(*)
354 			    (rcm_info_tuple_t *))sym;
355 			break;
356 		case INFO_TUPLE_RSRC:
357 			rcm->info_rsrc = (const char *(*)
358 			    (rcm_info_tuple_t *))sym;
359 			break;
360 		case REQUEST_OFFLINE:
361 			rcm->request_offline_list = (int (*)
362 			    (rcm_handle_t *, char **, uint_t,
363 			    rcm_info_t **))sym;
364 			break;
365 		case NOTIFY_ONLINE:
366 			rcm->notify_online_list = (int (*)
367 			    (rcm_handle_t *, char **, uint_t,
368 			    rcm_info_t **))sym;
369 			break;
370 		case REQUEST_SUSPEND:
371 			rcm->request_suspend = (int (*)
372 			    (rcm_handle_t *, char *, uint_t,
373 			    timespec_t *, rcm_info_t **))sym;
374 			break;
375 		case NOTIFY_RESUME:
376 			rcm->notify_resume = (int (*)
377 			    (rcm_handle_t *, char *, uint_t,
378 			    rcm_info_t **))sym;
379 			break;
380 		case NOTIFY_REMOVE:
381 			rcm->notify_remove_list = (int (*)
382 			    (rcm_handle_t *, char **, uint_t,
383 			    rcm_info_t **))sym;
384 			break;
385 		case REQUEST_CAP_CHANGE:
386 			rcm->request_capacity_change = (int (*)
387 			    (rcm_handle_t *, char *, uint_t,
388 			    nvlist_t *, rcm_info_t **))sym;
389 			break;
390 		case NOTIFY_CAP_CHANGE:
391 			rcm->notify_capacity_change = (int (*)
392 			    (rcm_handle_t *, char *, uint_t,
393 			    nvlist_t *, rcm_info_t **))sym;
394 			break;
395 		default:
396 			break;
397 		}
398 	}
399 
400 	if (rcm->alloc_handle == NULL ||
401 	    (*rcm->alloc_handle)(NULL, RCM_NOPID, NULL, &rcm->hd)
402 	    != RCM_SUCCESS) {
403 		ap_err(a, ERR_RCM_HANDLE);
404 		return (CFGA_LIB_ERROR);
405 	}
406 
407 	/*
408 	 * Offlining/onlining a board means offlining/onlining
409 	 * all components on the board.  When operating on a
410 	 * single component no component sequence number is
411 	 * needed since the default is the current (target)
412 	 * component.
413 	 */
414 	if (a->tgt == AP_BOARD) {
415 		rcm->firstcm = 0;
416 		rcm->lastcm = a->ncm - 1;
417 	} else {
418 		rcm->firstcm = CM_DFLT;
419 		rcm->lastcm = CM_DFLT;
420 	}
421 
422 	if (rcm->cpuids == NULL) {
423 		int cm;
424 		int ncpu;
425 
426 		/*
427 		 * Allocate space for the cpu capacity change info.
428 		 * Not every cpu may be relevant to the capacity
429 		 * request, but allocating for the maximum makes
430 		 * it easier, and the space is insignifcant.
431 		 */
432 		for (ncpu = 0, cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
433 
434 			ap_target_t type = ap_cm_type(a, cm);
435 
436 			if ((type == AP_CPU) || (type == AP_CMP)) {
437 				ncpu += ap_cm_ncap(a, cm);
438 			}
439 		}
440 
441 		rcm->ncpus = ncpu;
442 		if ((rcm->cpuids = (cpuid_t *)calloc(ncpu, sizeof (cpuid_t)))
443 		    == NULL) {
444 			ap_err(a, ERR_NOMEM);
445 			return (CFGA_LIB_ERROR);
446 		}
447 	}
448 
449 	/*
450 	 * Remember initial capacity information.
451 	 * This information is based on the initial
452 	 * state of the ap_id, i.e. before any
453 	 * state change change operations were
454 	 * executed.  We will later get the
455 	 * current capacity information in order
456 	 * to figure out exactly what has changed
457 	 * as the result of the executed command
458 	 * sequence.
459 	 */
460 	rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &rcm->capinfo);
461 
462 	rcm->capcpus = sysconf(_SC_NPROCESSORS_CONF);
463 	rcm->cappages = sysconf(_SC_PHYS_PAGES);
464 
465 	return (rc);
466 }
467 
468 void
469 ap_rcm_fini(apd_t *a)
470 {
471 	rcmd_t *rcm;
472 	char **rp;
473 
474 	DBG("ap_rcm_fini(%p)\n", (void *)a);
475 
476 	if ((rcm = (rcmd_t *)a->rcm) == NULL)
477 		return;
478 
479 	if (rcm->hd)
480 		(*rcm->free_handle)(rcm->hd);
481 
482 	(void) dlclose(rcm->lib);
483 
484 	/*
485 	 * Free all the names in the resource list, followed
486 	 * by the resource list itself.
487 	 */
488 	if (rcm->rlist)
489 		for (rp = rcm->rlist; *rp; rp++)
490 			s_free(*rp);
491 	s_free(rcm->rlist);
492 	s_free(rcm->cpuids);
493 	s_free(rcm->capinfo);
494 	s_free(a->rcm);
495 }
496 
497 static cfga_err_t
498 ap_rcm_rlist(apd_t *a, int firstcm, int lastcm, char ***rlist, int cmd)
499 {
500 	int n;
501 	int cm;
502 	int ncap;
503 	char *path;
504 	char *cpuname;
505 	char **rp;
506 
507 	DBG("ap_rcm_rlist(%p)\n", (void *)a);
508 
509 	/*
510 	 * Allocate space for the maximum number of components
511 	 * that can be affected by this operation.
512 	 */
513 	for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
514 		ncap += ap_cm_ncap(a, cm);
515 	}
516 
517 	DBG("ncap=%d\n", ncap);
518 
519 	if ((rp = (char **)calloc(ncap + 1, sizeof (char *))) == NULL) {
520 		ap_err(a, ERR_NOMEM);
521 		return (CFGA_LIB_ERROR);
522 	}
523 
524 	n = 12;	/* SUNW_cpu/cpuCCC */
525 		/* <--- 12 --->    */
526 	cpuname = "SUNW_cpu/cpuCCC";
527 	/*
528 	 * Set the RCM resource name for each component:
529 	 *
530 	 * io:		<device-path>
531 	 * cpu:		SUNW_cpu/cpu<cpuid>
532 	 *
533 	 */
534 	for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
535 		switch (ap_cm_type(a, cm)) {
536 		case AP_CPU:
537 		case AP_CMP: {
538 			int		i;
539 			int		len;
540 			cap_info_t	cap;
541 			cfga_stat_t	os;
542 			cpuid_t		*cpuid;
543 			int		*nc;
544 			cap_info_t	*prevcap;
545 			rcmd_t		*rcm;
546 			int		allow_op;
547 			int		capindex;
548 
549 			cpuid = cap.type.cpuid;
550 			nc = &cap.ncap;
551 
552 			/*
553 			 * See if the request target is a single
554 			 * (default) component
555 			 */
556 			capindex = (cm == CM_DFLT) ? 0 : cm;
557 
558 			/* Get the previous capacity info */
559 			rcm = (rcmd_t *)a->rcm;
560 			prevcap = rcm->capinfo;
561 
562 			if (!ap_cm_capacity(a, cm, cpuid, nc, &os)) {
563 				break;
564 			}
565 
566 			len = (strlen(cpuname) - n) + 1;
567 
568 			/*
569 			 * For CMD_RCM_OFFLINE and REMOVE, add the CPU to the
570 			 * list if it is currently configured. For
571 			 * CMD_RCM_ONLINE, do so only if the state has changed
572 			 * to CFGA_STAT_CONFIGURED.
573 			 */
574 			allow_op = 0;
575 			if ((cmd == CMD_RCM_OFFLINE) ||
576 			    (cmd == CMD_RCM_REMOVE)) {
577 				if (os == CFGA_STAT_CONFIGURED)
578 					allow_op = 1;
579 			} else {
580 				if ((os == CFGA_STAT_CONFIGURED) &&
581 				    ((prevcap == NULL) ||
582 				    (prevcap[capindex].ostate != os)))
583 					allow_op = 1;
584 			}
585 
586 			if (allow_op) {
587 				for (i = 0; i < *nc; i++) {
588 					if ((path = strdup(cpuname)) == NULL) {
589 						ap_err(a, ERR_NOMEM);
590 						return (CFGA_LIB_ERROR);
591 					}
592 					(void) snprintf(&path[n], len, "%d",
593 					    cpuid[i]);
594 
595 					DBG("rp[%d]=%s\n", ncap, path);
596 					rp[ncap++] = path;
597 				}
598 			}
599 			break;
600 		}
601 		case AP_IO:
602 			if ((path = ap_cm_devpath(a, cm)) != NULL) {
603 				DBG("rp[%d]=%s\n", ncap, path);
604 				rp[ncap++] = path;
605 			}
606 			break;
607 		case AP_MEM:
608 			/*
609 			 * Nothing to do for AP_MEM since only capacity
610 			 * change notifications apply to SUNW_memory
611 			 */
612 		default:
613 			break;
614 		}
615 	}
616 
617 	rp[ncap] = NULL;
618 	if (rlist)
619 		*rlist = rp;
620 	return (CFGA_OK);
621 }
622 
623 /*
624  * Returns 1 if the cpu ID 'cpuid' is in the list of CPU IDs
625  * 'list' of length 'length'. Returns 0 otherwise.
626  */
627 static int
628 is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int length)
629 {
630 	int i;
631 
632 	DBG("is_cpu_in_list\n");
633 
634 	if (list == NULL)
635 		return (0);
636 
637 	for (i = 0; i < length; i++) {
638 		if (list[i] == cpuid)
639 			return (1);
640 	}
641 	return (0);
642 }
643 
644 static int
645 ap_rcm_cap_cpu(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
646 	rcm_info_t **rinfo, int cmd, int change)
647 {
648 	int i;
649 	int rv = RCM_FAILURE;
650 	int ncpuids;
651 	int oldncpuids;
652 	int newncpuids;
653 	char buf[32];
654 	const char *fmt;
655 	size_t size;
656 	nvlist_t *nvl = NULL;
657 	cpuid_t *cpuids = NULL;
658 	cpuid_t *oldcpuids = NULL;
659 	cpuid_t *newcpuids = NULL;
660 
661 	DBG("ap_rcm_cap_cpu(%p)\n", (void *)a);
662 
663 	/*
664 	 * Get the current number of configured cpus.
665 	 */
666 	if (getsyscpuids(&ncpuids, &cpuids) == -1)
667 		return (rv);
668 	else if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
669 		free(cpuids);
670 		goto done;
671 	}
672 
673 	if (change == 1)
674 		fmt = "(%d cpu)";
675 	else
676 		fmt = "(%d cpus)";
677 
678 	size = sizeof (cpuid_t);
679 
680 	if (cmd == CMD_RCM_CAP_DEL) {
681 		/*
682 		 * A delete request. rcm->cpuids represents the
683 		 * cpus that will be unconfigured. The current
684 		 * set of cpus, before the unconfigure operation,
685 		 * are the old CPUs. The new CPUs are those
686 		 * that would remain.
687 		 */
688 		oldncpuids = ncpuids;
689 		oldcpuids = cpuids;
690 
691 		/*
692 		 * Fill newcpuids with the CPU IDs in the cpuids array,
693 		 * but not in rcm->cpuids.
694 		 */
695 		newcpuids = (cpuid_t *)calloc(ncpuids, size);
696 		if (newcpuids == NULL)
697 			goto done;
698 
699 		newncpuids = 0;
700 		for (i = 0; i < ncpuids; i++) {
701 			if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
702 				newcpuids[newncpuids++] = cpuids[i];
703 		}
704 	} else if (cmd == CMD_RCM_CAP_NOTIFY) {
705 		/*
706 		 * An unconfigure capacity change notification. This
707 		 * notification is sent after a DR unconfigure, whether
708 		 * or not the DR was successful. rcm->cpuids represents
709 		 * the CPUs that have been unconfigured.
710 		 */
711 
712 		/* New CPU IDs are the CPUs configured right now. */
713 		newncpuids = ncpuids;
714 		newcpuids = cpuids;
715 
716 		/*
717 		 * Old CPU IDs are the CPUs configured right now
718 		 * in addition to those that have been unconfigured.
719 		 * We build the old CPU ID list by concatenating
720 		 * cpuids and rcm->cpuids.
721 		 */
722 		oldcpuids = (cpuid_t *)calloc(ncpuids + change, size);
723 		if (oldcpuids == NULL)
724 			goto done;
725 
726 		oldncpuids = 0;
727 		for (i = 0; i < ncpuids; i++) {
728 			if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
729 				oldcpuids[oldncpuids++] = cpuids[i];
730 		}
731 		for (i = 0; i < change; i++)
732 			oldcpuids[oldncpuids++] = rcm->cpuids[i];
733 	} else {
734 		DBG("ap_rcm_cap_cpu: CPU capacity, old = %d, new = %d \n",
735 		    rcm->capcpus, ncpuids);
736 		if (rcm->capcpus == ncpuids) {
737 			/* No real change in CPU capacity */
738 			rv = RCM_SUCCESS;
739 			goto done;
740 		}
741 
742 		/*
743 		 * An add notification.  rcm->cpuids represents the
744 		 * cpus that have been configured.  The current
745 		 * set of cpus, after the configure operation,
746 		 * are the new CPU IDs.
747 		 */
748 		newncpuids = ncpuids;
749 		newcpuids = cpuids;
750 
751 		/*
752 		 * Fill oldcpuids with the CPU IDs in the cpuids array,
753 		 * but not in rcm->cpuids.
754 		 */
755 		oldcpuids = (cpuid_t *)calloc(ncpuids, size);
756 		if (oldcpuids == NULL)
757 			goto done;
758 
759 		oldncpuids = 0;
760 		for (i = 0; i < ncpuids; i++) {
761 			if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
762 				oldcpuids[oldncpuids++] = cpuids[i];
763 		}
764 	}
765 
766 	DBG("oldcpuids: ");
767 	for (i = 0; i < oldncpuids; i++)
768 		DBG("%d ", oldcpuids[i]);
769 	DBG("\n");
770 	DBG("change   : ");
771 	for (i = 0; i < change; i++)
772 		DBG("%d ", rcm->cpuids[i]);
773 	DBG("\n");
774 	DBG("newcpuids: ");
775 	for (i = 0; i < newncpuids; i++)
776 		DBG("%d ", newcpuids[i]);
777 	DBG("\n");
778 
779 	if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
780 	    nvlist_add_int32(nvl, "old_total", oldncpuids) != 0 ||
781 	    nvlist_add_int32(nvl, "new_total", newncpuids) != 0 ||
782 	    nvlist_add_int32_array(nvl, "old_cpu_list", oldcpuids,
783 	    oldncpuids) != 0 ||
784 	    nvlist_add_int32_array(nvl, "new_cpu_list", newcpuids,
785 	    newncpuids) != 0)
786 		goto done;
787 
788 	(void) snprintf(buf, sizeof (buf), fmt, change);
789 	ap_msg(a, MSG_ISSUE, cmd, buf);
790 
791 	if (cmd == CMD_RCM_CAP_DEL) {
792 		rv = (*rcm->request_capacity_change)(hd, "SUNW_cpu",
793 		    flags, nvl, rinfo);
794 	} else {
795 		rv = (*rcm->notify_capacity_change)(hd, "SUNW_cpu",
796 		    flags & ~RCM_FORCE, nvl, rinfo);
797 	}
798 
799 done:
800 	nvlist_free(nvl);
801 	s_free(oldcpuids);
802 	s_free(newcpuids);
803 	return (rv);
804 }
805 
806 static int
807 ap_rcm_cap_mem(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
808 	rcm_info_t **rinfo, int cmd, long change)
809 {
810 	int rv;
811 	int pgsize;
812 	long oldpages;
813 	long newpages;
814 	long currpages;
815 	char buf[32];
816 	nvlist_t *nvl;
817 
818 	DBG("ap_rcm_cap_mem(%p)\n", (void *)a);
819 
820 	/*
821 	 * Get the current amount of configured memory.
822 	 */
823 	if ((pgsize = sysconf(_SC_PAGE_SIZE)) == -1 ||
824 	    (currpages = sysconf(_SC_PHYS_PAGES)) == -1 ||
825 	    nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) > 0)
826 		return (RCM_FAILURE);
827 
828 	/*
829 	 * If this is a (delete) request, change represents
830 	 * the amount of capacity that will be deleted from the
831 	 * system.  If this is an (add) notification, change
832 	 * represents the amount of capacity that has already
833 	 * been added to the system.
834 	 */
835 	if (cmd == CMD_RCM_CAP_DEL) {
836 		oldpages = currpages;
837 		newpages = currpages - change;
838 	} else if (cmd == CMD_RCM_CAP_NOTIFY) {
839 		newpages = currpages;
840 		oldpages = rcm->cappages;
841 	} else {
842 		if (rcm->cappages == currpages) {
843 			/* No real change in memory capacity */
844 			DBG("ap_rcm_cap_mem: no change in capacity.\n");
845 			nvlist_free(nvl);
846 			return (RCM_SUCCESS);
847 		}
848 
849 		oldpages = currpages - change;
850 		newpages = currpages;
851 	}
852 
853 	DBG("ap_rcm_cap_mem: Memory capacity, old = %ld, new = %ld\n",
854 	    oldpages, newpages);
855 
856 	if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
857 	    nvlist_add_int32(nvl, "page_size", pgsize) != 0 ||
858 	    nvlist_add_int32(nvl, "old_pages", oldpages) != 0 ||
859 	    nvlist_add_int32(nvl, "new_pages", newpages) != 0) {
860 		nvlist_free(nvl);
861 		return (RCM_FAILURE);
862 	}
863 
864 	(void) snprintf(buf, sizeof (buf), "(%ld pages)", change);
865 	ap_msg(a, MSG_ISSUE, cmd, buf);
866 
867 	if (cmd == CMD_RCM_CAP_DEL) {
868 		rv = (*rcm->request_capacity_change)(hd, "SUNW_memory",
869 		    flags, nvl, rinfo);
870 	} else {
871 		rv = (*rcm->notify_capacity_change)(hd, "SUNW_memory",
872 		    flags & ~RCM_FORCE, nvl, rinfo);
873 	}
874 
875 	nvlist_free(nvl);
876 
877 	return (rv);
878 }
879 
880 static cfga_err_t
881 ap_rcm_request_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
882 	int *rv, uint_t flags, rcm_info_t **rinfo)
883 {
884 	int cm;
885 	int ncpus;
886 	long npages;
887 	cap_info_t *capinfo;
888 	ap_target_t type;
889 
890 	DBG("ap_rcm_request_cap(%p)\n", (void *)a);
891 
892 	if ((capinfo = rcm->capinfo) == NULL) {
893 		ap_err(a, ERR_PLUGIN, "null capinfo");
894 		return (CFGA_LIB_ERROR);
895 	}
896 
897 	ncpus = npages = 0;
898 
899 	for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
900 		int i, j;
901 
902 		/*
903 		 * See if the request target is a single
904 		 * (default) component
905 		 */
906 		i = (cm == CM_DFLT) ? 0 : cm;
907 
908 		/*
909 		 * We are interested only in those components
910 		 * in the configured state since they represent
911 		 * available capacity.
912 		 */
913 		type = ap_cm_type(a, cm);
914 		if (capinfo[i].valid == 0 ||
915 		    capinfo[i].ostate != CFGA_STAT_CONFIGURED)
916 			continue;
917 		else if ((type == AP_CPU) || (type == AP_CMP)) {
918 			for (j = 0; j < capinfo[i].ncap; j++) {
919 				rcm->cpuids[ncpus++] = capinfo[i].type.cpuid[j];
920 			}
921 		} else if (type == AP_MEM)
922 			npages += capinfo[i].type.npages;
923 	}
924 
925 	if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
926 	    CMD_RCM_CAP_DEL, ncpus)) != RCM_SUCCESS)) {
927 		return (CFGA_LIB_ERROR);
928 	}
929 	if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
930 	    CMD_RCM_CAP_DEL, npages)) != RCM_SUCCESS)) {
931 		return (CFGA_LIB_ERROR);
932 	}
933 
934 	return (CFGA_OK);
935 }
936 
937 static cfga_err_t
938 ap_rcm_add_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
939 	int *rv, uint_t flags, rcm_info_t **rinfo)
940 {
941 	int cm;
942 	int ncpus;
943 	long npages;
944 	cap_info_t *capinfo, *prevcapinfo;
945 	cfga_err_t rc;
946 
947 	DBG("ap_rcm_add_cap(%p)\n", (void *)a);
948 
949 	/* Get the new capacity info to figure out what has changed */
950 	if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
951 	    CFGA_OK)
952 		return (rc);
953 
954 	if (capinfo == NULL) {
955 		DBG("no pertinent capacity info\n");
956 		return (CFGA_OK);
957 	}
958 
959 	ncpus = npages = 0;
960 	prevcapinfo = rcm->capinfo;
961 
962 	for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
963 		int i, j;
964 		cfga_stat_t os, prevos;
965 		int prevvalidity;
966 		ap_target_t type;
967 
968 		/*
969 		 * See if the request target is a single
970 		 * (default) component
971 		 */
972 		i = cm == CM_DFLT ? 0 : cm;
973 
974 		os = capinfo[i].ostate;
975 		if (prevcapinfo == NULL) {
976 			prevos = CFGA_STAT_EMPTY;
977 			prevvalidity = 1;
978 		} else {
979 			prevos = prevcapinfo[i].ostate;
980 			prevvalidity = prevcapinfo[i].valid;
981 		}
982 
983 		type = ap_cm_type(a, cm);
984 
985 		DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
986 		    cm, prevvalidity, type, prevos, os);
987 
988 		/*
989 		 * We are interested only in those components
990 		 * whose states have changed to configured as
991 		 * the result of the current cfgadm request.
992 		 */
993 		if (prevvalidity == 0 || os != CFGA_STAT_CONFIGURED) {
994 			capinfo[i].valid = 0;
995 			continue;
996 		} else if (prevos != CFGA_STAT_CONFIGURED) {
997 			/*
998 			 * The occupant state is configured, and
999 			 * the previous occupant state was not.
1000 			 */
1001 			if ((type == AP_CPU) || (type == AP_CMP)) {
1002 				for (j = 0; j < capinfo[i].ncap; j++) {
1003 					rcm->cpuids[ncpus++] =
1004 					    capinfo[i].type.cpuid[j];
1005 				}
1006 			} else if (type == AP_MEM)
1007 				npages += capinfo[i].type.npages;
1008 		}
1009 	}
1010 	free(capinfo);
1011 
1012 	if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1013 	    CMD_RCM_CAP_ADD, ncpus)) != RCM_SUCCESS)) {
1014 		return (CFGA_LIB_ERROR);
1015 	}
1016 	if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1017 	    CMD_RCM_CAP_ADD, npages)) != RCM_SUCCESS)) {
1018 		return (CFGA_LIB_ERROR);
1019 	}
1020 
1021 	return (CFGA_OK);
1022 }
1023 
1024 /*
1025  * ap_rcm_notify_cap:
1026  *
1027  * This routine handles the CMD_RCM_CAP_NOTIFY command. It
1028  * is called after a successful/failed DR unconfigure
1029  * operation. It filters out components that have changed
1030  * and passes this information on to ap_rcm_cap_{cpu,mem}.
1031  *
1032  * ap_rcm_cap_{cpu,mem} will still be called if all the
1033  * components have not changed and at least one {cpu,mem}
1034  * component was originally configured.
1035  */
1036 static cfga_err_t
1037 ap_rcm_notify_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
1038 	int *rv, uint_t flags, rcm_info_t **rinfo)
1039 {
1040 	cfga_err_t  rc;
1041 	cap_info_t  *capinfo;
1042 	cap_info_t  *prevcapinfo;
1043 	int	    cm;
1044 	long	    npages	= 0;
1045 	int	    ncpus	= 0;
1046 	int	    prev_mem	= 0; /* # of prev. configured mem components */
1047 	int	    prev_cpus	= 0; /* # of prev. configured CPUs */
1048 
1049 	DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
1050 
1051 	/* Get the new capacity info to figure out what has changed */
1052 	if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
1053 	    CFGA_OK)
1054 		return (rc);
1055 
1056 	if (capinfo == NULL) {
1057 		DBG("no pertinent capacity info\n");
1058 		return (CFGA_OK);
1059 	}
1060 
1061 	/* The original capacity info */
1062 	prevcapinfo = rcm->capinfo;
1063 
1064 	/*
1065 	 * Cycle through all components that we are operating
1066 	 * on. Record which components' occupant states have
1067 	 * changed.
1068 	 */
1069 	for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
1070 		int i;
1071 		cfga_stat_t prevos, os;
1072 		ap_target_t type;
1073 		int prev_conf = 0;
1074 		int now_conf  = 0;
1075 
1076 		/*
1077 		 * See if the request target is a single
1078 		 * (default) component
1079 		 */
1080 		i = cm == CM_DFLT ? 0 : cm;
1081 
1082 		os = capinfo[i].ostate;
1083 
1084 		if (prevcapinfo == NULL) {
1085 			prevos = CFGA_STAT_EMPTY;
1086 		} else {
1087 			prevos = prevcapinfo[i].ostate;
1088 			if (prevcapinfo[i].valid == 0) {
1089 				DBG("ap_rcm_notify_cap: skipping component "
1090 				    "due to prevvalidity == 0\n");
1091 				continue;
1092 			}
1093 		}
1094 
1095 		type = ap_cm_type(a, cm);
1096 
1097 		prev_conf = (prevos == CFGA_STAT_CONFIGURED);
1098 		now_conf  = (os == CFGA_STAT_CONFIGURED);
1099 
1100 		/*
1101 		 * Build up rcm->cpuids with the IDs of CPUs that
1102 		 * have been removed. Record the number of removed
1103 		 * CPUs and pages.
1104 		 */
1105 		if (type == AP_CPU || type == AP_CMP) {
1106 			if (prev_conf)
1107 				prev_cpus++;
1108 			if (prev_conf && !now_conf) {
1109 				int j;
1110 				for (j = 0; j < capinfo[i].ncap; j++) {
1111 					rcm->cpuids[ncpus++] =
1112 					    capinfo[i].type.cpuid[j];
1113 				}
1114 			}
1115 		} else if (type == AP_MEM) {
1116 			if (prev_conf)
1117 				prev_mem++;
1118 			if (prev_conf && !now_conf)
1119 				npages += capinfo[i].type.npages;
1120 		}
1121 	}
1122 	free(capinfo);
1123 
1124 	/*
1125 	 * If any CPU or memory components were operated on,
1126 	 * successfully or not, the rcm_notify_capacity_change()
1127 	 * routine must be called.
1128 	 */
1129 
1130 	if (prev_cpus) {
1131 		*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1132 		    CMD_RCM_CAP_NOTIFY, ncpus);
1133 
1134 		if (*rv != RCM_SUCCESS)
1135 			return (CFGA_LIB_ERROR);
1136 	}
1137 
1138 	if (prev_mem) {
1139 		*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1140 		    CMD_RCM_CAP_NOTIFY, npages);
1141 
1142 		if (*rv != RCM_SUCCESS)
1143 			return (CFGA_LIB_ERROR);
1144 	}
1145 
1146 	return (CFGA_OK);
1147 }
1148 
1149 cfga_err_t
1150 ap_rcm_ctl(apd_t *a, int cmd)
1151 {
1152 	int i;
1153 	int rv;
1154 	int noop;
1155 	int ncpus;
1156 	int cm;
1157 	uint_t flags;
1158 	char *rsrc;
1159 	char **rlist;
1160 	rcmd_t *rcm;
1161 	rcm_info_t *rinfo;
1162 	rcm_handle_t *hd;
1163 	cfga_err_t rc;
1164 	cpuid_t *growcpuids;
1165 
1166 	DBG("ap_rcm_ctl(%p)\n", (void *)a);
1167 
1168 	if ((rcm = (rcmd_t *)a->rcm) == NULL) {
1169 		ap_msg(a, MSG_SKIP, cmd, a->target);
1170 		return (CFGA_OK);
1171 	}
1172 
1173 	hd = rcm->hd;
1174 	rv = RCM_SUCCESS;
1175 	rc = CFGA_OK;
1176 	if (ap_getopt(a, OPT_FORCE))
1177 		flags = RCM_FORCE;
1178 	else
1179 		flags = 0;
1180 	rinfo = NULL;
1181 	rlist = NULL;
1182 	rsrc = NULL;
1183 	noop = 0;
1184 
1185 	switch (cmd) {
1186 	case CMD_RCM_CAP_DEL:
1187 		if (rcm->capinfo == NULL)
1188 			noop++;
1189 		else
1190 			rc = ap_rcm_request_cap(a, rcm, hd, &rv, flags, &rinfo);
1191 		break;
1192 	case CMD_RCM_CAP_ADD:
1193 		rc = ap_rcm_add_cap(a, rcm, hd, &rv, flags, &rinfo);
1194 		break;
1195 	case CMD_RCM_CAP_NOTIFY:
1196 		rc = ap_rcm_notify_cap(a, rcm, hd, &rv, flags, &rinfo);
1197 		break;
1198 	case CMD_RCM_ONLINE:
1199 		/* Refresh changed component states */
1200 		if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1201 			noop++;
1202 			break;
1203 		}
1204 
1205 		if (a->tgt == AP_BOARD) {
1206 			rcm->firstcm = 0;
1207 			rcm->lastcm = a->ncm - 1;
1208 
1209 			/* Check if we need to grow our cpuids list */
1210 			for (ncpus = 0, cm = rcm->firstcm; cm <= rcm->lastcm;
1211 			    cm++) {
1212 				ap_target_t type = ap_cm_type(a, cm);
1213 				if ((type == AP_CPU) || (type == AP_CMP))
1214 					ncpus += ap_cm_ncap(a, cm);
1215 			}
1216 
1217 			if (rcm->ncpus < ncpus) {
1218 				if ((growcpuids =
1219 				    (cpuid_t *)realloc(rcm->cpuids,
1220 				    (ncpus * sizeof (cpuid_t)))) == NULL) {
1221 					ap_err(a, ERR_NOMEM);
1222 					return (CFGA_LIB_ERROR);
1223 				}
1224 				rcm->ncpus = ncpus;
1225 				rcm->cpuids = growcpuids;
1226 			}
1227 
1228 		} else {
1229 			rcm->firstcm = CM_DFLT;
1230 			rcm->lastcm = CM_DFLT;
1231 		}
1232 
1233 		/*FALLTHROUGH*/
1234 
1235 	case CMD_RCM_OFFLINE:
1236 	case CMD_RCM_REMOVE: {
1237 		uint_t nrsrc;
1238 
1239 		if (cmd == CMD_RCM_REMOVE) {
1240 			/*
1241 			 * An unconfigure has just taken place, so
1242 			 * refresh the changed component states.
1243 			 */
1244 			if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1245 				noop++;
1246 				break;
1247 			}
1248 		}
1249 
1250 		/* Check if this is an empty board, i.e. no components */
1251 		if (a->ncm == 0) {
1252 			noop++;
1253 			break;
1254 		}
1255 
1256 		if ((rlist = rcm->rlist) == NULL) {
1257 			rc = ap_rcm_rlist(a, rcm->firstcm, rcm->lastcm, &rlist,
1258 			    cmd);
1259 			if ((rc == CFGA_OK) && (rlist != NULL) &&
1260 			    (rlist[0] != NULL)) {
1261 				rcm->rlist = rlist;
1262 			} else {
1263 				/* Do not pass up empty resource list to RCM */
1264 				noop++;
1265 				break;
1266 			}
1267 		}
1268 		for (nrsrc = 0; rlist[nrsrc] != NULL; nrsrc++)
1269 			ap_msg(a, MSG_ISSUE, cmd, rlist[nrsrc]);
1270 		if (cmd == CMD_RCM_OFFLINE)
1271 			rv = (*rcm->request_offline_list)(hd, rlist, flags,
1272 			    &rinfo);
1273 		else if (cmd == CMD_RCM_ONLINE)
1274 			rv = (*rcm->notify_online_list)(hd, rlist,
1275 			    flags & ~RCM_FORCE, &rinfo);
1276 		else
1277 			rv = (*rcm->notify_remove_list)(hd, rlist,
1278 			    flags & ~RCM_FORCE, &rinfo);
1279 		break;
1280 	}
1281 	case CMD_RCM_SUSPEND: {
1282 		timespec_t t;
1283 		t.tv_sec = (time_t)0;
1284 		t.tv_nsec = (long)0;
1285 		rsrc = OS;
1286 		ap_msg(a, MSG_ISSUE, cmd, rsrc);
1287 		rv = (*rcm->request_suspend)(hd, rsrc, flags, &t, &rinfo);
1288 		break;
1289 	}
1290 	case CMD_RCM_RESUME:
1291 		rsrc = OS;
1292 		ap_msg(a, MSG_ISSUE, cmd, rsrc);
1293 		rv = (*rcm->notify_resume)(hd, rsrc, 0, &rinfo);
1294 		break;
1295 	default:
1296 		ap_err(a, ERR_CMD_INVAL, cmd);
1297 		return (CFGA_INVAL);
1298 	}
1299 
1300 	if (rv != RCM_SUCCESS) {
1301 		rcm->rinfo = rinfo;
1302 		rcm->infot = NULL;
1303 		ap_err(a, ERR_RCM_CMD, cmd);
1304 		(*rcm->free_info)(rinfo);
1305 		if (rc == CFGA_OK)
1306 			rc = CFGA_LIB_ERROR;	/* make sure error is set */
1307 	}
1308 	if ((rc == CFGA_OK) && (noop == 0)) {
1309 		if (rlist)
1310 			for (i = 0; rlist[i]; i++)
1311 				ap_msg(a, MSG_DONE, cmd, rlist[i]);
1312 		else if (rsrc)
1313 			ap_msg(a, MSG_DONE, cmd, rsrc);
1314 		else
1315 			ap_msg(a, MSG_DONE, cmd, a->target);
1316 	}
1317 
1318 	return (rc);
1319 }
1320 
1321 /*
1322  * ap_rcm_info
1323  *
1324  * Takes an ap_id and a character pointer, and formats
1325  * the rcm_info_t data in the form of a table to the given character pointer.
1326  * Code duplicated from the scsi plugin.
1327  * Note: This function will go away when a generic librcm callback is
1328  *	implemented to format RCM messages for plugins.
1329  */
1330 int
1331 ap_rcm_info(apd_t *a, char **msg)
1332 {
1333 	rcmd_t *rcm;
1334 	rcm_info_t *rinfo;
1335 	int i;
1336 	size_t w;
1337 	size_t width = 0;
1338 	size_t w_rsrc = 0;
1339 	size_t w_info = 0;
1340 	size_t msg_size = 0;
1341 	uint_t tuples = 0;
1342 	rcm_info_tuple_t *tuple = NULL;
1343 	char *rsrc;
1344 	char *info;
1345 	char *newmsg;
1346 	static char format[RCM_MAX_FORMAT];
1347 	const char *infostr;
1348 
1349 
1350 	DBG("ap_rcm_info(%p)\n", (void *)a);
1351 
1352 	/* Protect against invalid arguments */
1353 	if ((a == NULL) || ((rcm = (rcmd_t *)a->rcm) == NULL) ||
1354 	    ((rinfo = rcm->rinfo) == NULL) || (msg == NULL)) {
1355 		return (-1);
1356 	}
1357 
1358 	/* Set localized table header strings */
1359 	rsrc = dgettext(TEXT_DOMAIN, "Resource");
1360 	info = dgettext(TEXT_DOMAIN, "Information");
1361 
1362 	/* A first pass, to size up the RCM information */
1363 	while (tuple = (*rcm->info_next)(rinfo, tuple)) {
1364 		if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1365 			tuples++;
1366 			if ((w = strlen((*rcm->info_rsrc)(tuple))) > w_rsrc)
1367 				w_rsrc = w;
1368 			if ((w = strlen(infostr)) > w_info)
1369 				w_info = w;
1370 		}
1371 	}
1372 
1373 	/* If nothing was sized up above, stop early */
1374 	if (tuples == 0)
1375 		return (0);
1376 
1377 	/* Adjust column widths for column headings */
1378 	if ((w = strlen(rsrc)) > w_rsrc)
1379 		w_rsrc = w;
1380 	else if ((w_rsrc - w) % 2)
1381 		w_rsrc++;
1382 	if ((w = strlen(info)) > w_info)
1383 		w_info = w;
1384 	else if ((w_info - w) % 2)
1385 		w_info++;
1386 
1387 	/*
1388 	 * Compute the total line width of each line,
1389 	 * accounting for intercolumn spacing.
1390 	 */
1391 	width = w_info + w_rsrc + 4;
1392 
1393 	/* Allocate space for the table */
1394 	msg_size = (2 + tuples) * (width + 1) + 2;
1395 	if (*msg == NULL) {
1396 		/* zero fill for the strcat() call below */
1397 		*msg = calloc(msg_size, sizeof (char));
1398 		if (*msg == NULL)
1399 			return (-1);
1400 	} else {
1401 		newmsg = realloc(*msg, strlen(*msg) + msg_size);
1402 		if (newmsg == NULL)
1403 			return (-1);
1404 		else
1405 			*msg = newmsg;
1406 	}
1407 
1408 	/* Place a table header into the string */
1409 
1410 	/* The resource header */
1411 	(void) strcat(*msg, "\n");
1412 	w = strlen(rsrc);
1413 	for (i = 0; i < ((w_rsrc - w) / 2); i++)
1414 		(void) strcat(*msg, " ");
1415 	(void) strcat(*msg, rsrc);
1416 	for (i = 0; i < ((w_rsrc - w) / 2); i++)
1417 		(void) strcat(*msg, " ");
1418 
1419 	/* The information header */
1420 	(void) strcat(*msg, "  ");
1421 	w = strlen(info);
1422 	for (i = 0; i < ((w_info - w) / 2); i++)
1423 		(void) strcat(*msg, " ");
1424 	(void) strcat(*msg, info);
1425 	for (i = 0; i < ((w_info - w) / 2); i++)
1426 		(void) strcat(*msg, " ");
1427 
1428 	/* Underline the headers */
1429 	(void) strcat(*msg, "\n");
1430 	for (i = 0; i < w_rsrc; i++)
1431 		(void) strcat(*msg, "-");
1432 	(void) strcat(*msg, "  ");
1433 	for (i = 0; i < w_info; i++)
1434 		(void) strcat(*msg, "-");
1435 
1436 	/* Construct the format string */
1437 	(void) snprintf(format, RCM_MAX_FORMAT, "%%-%ds  %%-%ds",
1438 	    (int)w_rsrc, (int)w_info);
1439 
1440 	/* Add the tuples to the table string */
1441 	tuple = NULL;
1442 	while ((tuple = (*rcm->info_next)(rinfo, tuple)) != NULL) {
1443 		if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1444 			(void) strcat(*msg, "\n");
1445 			(void) sprintf(&((*msg)[strlen(*msg)]), format,
1446 			    (*rcm->info_rsrc)(tuple), infostr);
1447 		}
1448 	}
1449 
1450 	DBG("ap_rcm_info(%p) success\n", (void *)a);
1451 	return (0);
1452 }
1453