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