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