xref: /illumos-gate/usr/src/lib/cfgadm_plugins/sbd/common/ap_rcm.c (revision b1d7ec75953cd517f5b7c3d9cb427ff8ec5d7d07)
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 	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 
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 	}
930 	if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
931 	    CMD_RCM_CAP_DEL, npages)) != RCM_SUCCESS)) {
932 		return (CFGA_LIB_ERROR);
933 	}
934 
935 	return (CFGA_OK);
936 }
937 
938 static cfga_err_t
939 ap_rcm_add_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
940 	int *rv, uint_t flags, rcm_info_t **rinfo)
941 {
942 	int cm;
943 	int ncpus;
944 	long npages;
945 	cap_info_t *capinfo, *prevcapinfo;
946 	cfga_err_t rc;
947 
948 	DBG("ap_rcm_add_cap(%p)\n", (void *)a);
949 
950 	/* Get the new capacity info to figure out what has changed */
951 	if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
952 	    CFGA_OK)
953 		return (rc);
954 
955 	if (capinfo == NULL) {
956 		DBG("no pertinent capacity info\n");
957 		return (CFGA_OK);
958 	}
959 
960 	ncpus = npages = 0;
961 	prevcapinfo = rcm->capinfo;
962 
963 	for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
964 		int i, j;
965 		cfga_stat_t os, prevos;
966 		int prevvalidity;
967 		ap_target_t type;
968 
969 		/*
970 		 * See if the request target is a single
971 		 * (default) component
972 		 */
973 		i = cm == CM_DFLT ? 0 : cm;
974 
975 		os = capinfo[i].ostate;
976 		if (prevcapinfo == NULL) {
977 			prevos = CFGA_STAT_EMPTY;
978 			prevvalidity = 1;
979 		} else {
980 			prevos = prevcapinfo[i].ostate;
981 			prevvalidity = prevcapinfo[i].valid;
982 		}
983 
984 		type = ap_cm_type(a, cm);
985 
986 		DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
987 		    cm, prevvalidity, type, prevos, os);
988 
989 		/*
990 		 * We are interested only in those components
991 		 * whose states have changed to configured as
992 		 * the result of the current cfgadm request.
993 		 */
994 		if (prevvalidity == 0 || os != CFGA_STAT_CONFIGURED) {
995 			capinfo[i].valid = 0;
996 			continue;
997 		} else if (prevos != CFGA_STAT_CONFIGURED) {
998 			/*
999 			 * The occupant state is configured, and
1000 			 * the previous occupant state was not.
1001 			 */
1002 			if ((type == AP_CPU) || (type == AP_CMP)) {
1003 				for (j = 0; j < capinfo[i].ncap; j++) {
1004 					rcm->cpuids[ncpus++] =
1005 					    capinfo[i].type.cpuid[j];
1006 				}
1007 			} else if (type == AP_MEM)
1008 				npages += capinfo[i].type.npages;
1009 		}
1010 	}
1011 	free(capinfo);
1012 
1013 	if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1014 	    CMD_RCM_CAP_ADD, ncpus)) != RCM_SUCCESS)) {
1015 		return (CFGA_LIB_ERROR);
1016 	}
1017 	if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1018 	    CMD_RCM_CAP_ADD, npages)) != RCM_SUCCESS)) {
1019 		return (CFGA_LIB_ERROR);
1020 	}
1021 
1022 	return (CFGA_OK);
1023 }
1024 
1025 /*
1026  * ap_rcm_notify_cap:
1027  *
1028  * This routine handles the CMD_RCM_CAP_NOTIFY command. It
1029  * is called after a successful/failed DR unconfigure
1030  * operation. It filters out components that have changed
1031  * and passes this information on to ap_rcm_cap_{cpu,mem}.
1032  *
1033  * ap_rcm_cap_{cpu,mem} will still be called if all the
1034  * components have not changed and at least one {cpu,mem}
1035  * component was originally configured.
1036  */
1037 static cfga_err_t
1038 ap_rcm_notify_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
1039 	int *rv, uint_t flags, rcm_info_t **rinfo)
1040 {
1041 	cfga_err_t  rc;
1042 	cap_info_t  *capinfo;
1043 	cap_info_t  *prevcapinfo;
1044 	int	    cm;
1045 	long	    npages	= 0;
1046 	int	    ncpus	= 0;
1047 	int	    prev_mem	= 0; /* # of prev. configured mem components */
1048 	int	    prev_cpus	= 0; /* # of prev. configured CPUs */
1049 
1050 	DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
1051 
1052 	/* Get the new capacity info to figure out what has changed */
1053 	if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
1054 	    CFGA_OK)
1055 		return (rc);
1056 
1057 	if (capinfo == NULL) {
1058 		DBG("no pertinent capacity info\n");
1059 		return (CFGA_OK);
1060 	}
1061 
1062 	/* The original capacity info */
1063 	prevcapinfo = rcm->capinfo;
1064 
1065 	/*
1066 	 * Cycle through all components that we are operating
1067 	 * on. Record which components' occupant states have
1068 	 * changed.
1069 	 */
1070 	for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
1071 		int i;
1072 		cfga_stat_t prevos, os;
1073 		ap_target_t type;
1074 		int prev_conf = 0;
1075 		int now_conf  = 0;
1076 
1077 		/*
1078 		 * See if the request target is a single
1079 		 * (default) component
1080 		 */
1081 		i = cm == CM_DFLT ? 0 : cm;
1082 
1083 		os = capinfo[i].ostate;
1084 
1085 		if (prevcapinfo == NULL) {
1086 			prevos = CFGA_STAT_EMPTY;
1087 		} else {
1088 			prevos = prevcapinfo[i].ostate;
1089 			if (prevcapinfo[i].valid == 0) {
1090 				DBG("ap_rcm_notify_cap: skipping component "
1091 				    "due to prevvalidity == 0\n");
1092 				continue;
1093 			}
1094 		}
1095 
1096 		type = ap_cm_type(a, cm);
1097 
1098 		prev_conf = (prevos == CFGA_STAT_CONFIGURED);
1099 		now_conf  = (os == CFGA_STAT_CONFIGURED);
1100 
1101 		/*
1102 		 * Build up rcm->cpuids with the IDs of CPUs that
1103 		 * have been removed. Record the number of removed
1104 		 * CPUs and pages.
1105 		 */
1106 		if (type == AP_CPU || type == AP_CMP) {
1107 			if (prev_conf)
1108 				prev_cpus++;
1109 			if (prev_conf && !now_conf) {
1110 				int j;
1111 				for (j = 0; j < capinfo[i].ncap; j++) {
1112 					rcm->cpuids[ncpus++] =
1113 					    capinfo[i].type.cpuid[j];
1114 				}
1115 			}
1116 		} else if (type == AP_MEM) {
1117 			if (prev_conf)
1118 				prev_mem++;
1119 			if (prev_conf && !now_conf)
1120 				npages += capinfo[i].type.npages;
1121 		}
1122 	}
1123 	free(capinfo);
1124 
1125 	/*
1126 	 * If any CPU or memory components were operated on,
1127 	 * successfully or not, the rcm_notify_capacity_change()
1128 	 * routine must be called.
1129 	 */
1130 
1131 	if (prev_cpus) {
1132 		*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1133 		    CMD_RCM_CAP_NOTIFY, ncpus);
1134 
1135 		if (*rv != RCM_SUCCESS)
1136 			return (CFGA_LIB_ERROR);
1137 	}
1138 
1139 	if (prev_mem) {
1140 		*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1141 		    CMD_RCM_CAP_NOTIFY, npages);
1142 
1143 		if (*rv != RCM_SUCCESS)
1144 			return (CFGA_LIB_ERROR);
1145 	}
1146 
1147 	return (CFGA_OK);
1148 }
1149 
1150 cfga_err_t
1151 ap_rcm_ctl(apd_t *a, int cmd)
1152 {
1153 	int i;
1154 	int rv;
1155 	int noop;
1156 	int ncpus;
1157 	int cm;
1158 	uint_t flags;
1159 	char *rsrc;
1160 	char **rlist;
1161 	rcmd_t *rcm;
1162 	rcm_info_t *rinfo;
1163 	rcm_handle_t *hd;
1164 	cfga_err_t rc;
1165 	cpuid_t *growcpuids;
1166 
1167 	DBG("ap_rcm_ctl(%p)\n", (void *)a);
1168 
1169 	if ((rcm = (rcmd_t *)a->rcm) == NULL) {
1170 		ap_msg(a, MSG_SKIP, cmd, a->target);
1171 		return (CFGA_OK);
1172 	}
1173 
1174 	hd = rcm->hd;
1175 	rv = RCM_SUCCESS;
1176 	rc = CFGA_OK;
1177 	if (ap_getopt(a, OPT_FORCE))
1178 		flags = RCM_FORCE;
1179 	else
1180 		flags = 0;
1181 	rinfo = NULL;
1182 	rlist = NULL;
1183 	rsrc = NULL;
1184 	noop = 0;
1185 
1186 	switch (cmd) {
1187 	case CMD_RCM_CAP_DEL:
1188 		if (rcm->capinfo == NULL)
1189 			noop++;
1190 		else
1191 			rc = ap_rcm_request_cap(a, rcm, hd, &rv, flags, &rinfo);
1192 		break;
1193 	case CMD_RCM_CAP_ADD:
1194 		rc = ap_rcm_add_cap(a, rcm, hd, &rv, flags, &rinfo);
1195 		break;
1196 	case CMD_RCM_CAP_NOTIFY:
1197 		rc = ap_rcm_notify_cap(a, rcm, hd, &rv, flags, &rinfo);
1198 		break;
1199 	case CMD_RCM_ONLINE:
1200 		/* Refresh changed component states */
1201 		if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1202 			noop++;
1203 			break;
1204 		}
1205 
1206 		if (a->tgt == AP_BOARD) {
1207 			rcm->firstcm = 0;
1208 			rcm->lastcm = a->ncm - 1;
1209 
1210 			/* Check if we need to grow our cpuids list */
1211 			for (ncpus = 0, cm = rcm->firstcm; cm <= rcm->lastcm;
1212 			    cm++) {
1213 				ap_target_t type = ap_cm_type(a, cm);
1214 				if ((type == AP_CPU) || (type == AP_CMP))
1215 					ncpus += ap_cm_ncap(a, cm);
1216 			}
1217 
1218 			if (rcm->ncpus < ncpus) {
1219 				if ((growcpuids =
1220 				    (cpuid_t *)realloc(rcm->cpuids,
1221 				    (ncpus * sizeof (cpuid_t)))) == NULL) {
1222 					ap_err(a, ERR_NOMEM);
1223 					return (CFGA_LIB_ERROR);
1224 				}
1225 				rcm->ncpus = ncpus;
1226 				rcm->cpuids = growcpuids;
1227 			}
1228 
1229 		} else {
1230 			rcm->firstcm = CM_DFLT;
1231 			rcm->lastcm = CM_DFLT;
1232 		}
1233 
1234 		/*FALLTHROUGH*/
1235 
1236 	case CMD_RCM_OFFLINE:
1237 	case CMD_RCM_REMOVE: {
1238 		uint_t nrsrc;
1239 
1240 		if (cmd == CMD_RCM_REMOVE) {
1241 			/*
1242 			 * An unconfigure has just taken place, so
1243 			 * refresh the changed component states.
1244 			 */
1245 			if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1246 				noop++;
1247 				break;
1248 			}
1249 		}
1250 
1251 		/* Check if this is an empty board, i.e. no components */
1252 		if (a->ncm == 0) {
1253 			noop++;
1254 			break;
1255 		}
1256 
1257 		if ((rlist = rcm->rlist) == NULL) {
1258 			rc = ap_rcm_rlist(a, rcm->firstcm, rcm->lastcm, &rlist,
1259 			    cmd);
1260 			if ((rc == CFGA_OK) && (rlist != NULL) &&
1261 			    (rlist[0] != NULL)) {
1262 				rcm->rlist = rlist;
1263 			} else {
1264 				/* Do not pass up empty resource list to RCM */
1265 				noop++;
1266 				break;
1267 			}
1268 		}
1269 		for (nrsrc = 0; rlist[nrsrc] != NULL; nrsrc++)
1270 			ap_msg(a, MSG_ISSUE, cmd, rlist[nrsrc]);
1271 		if (cmd == CMD_RCM_OFFLINE)
1272 			rv = (*rcm->request_offline_list)(hd, rlist, flags,
1273 			    &rinfo);
1274 		else if (cmd == CMD_RCM_ONLINE)
1275 			rv = (*rcm->notify_online_list)(hd, rlist,
1276 			    flags & ~RCM_FORCE, &rinfo);
1277 		else
1278 			rv = (*rcm->notify_remove_list)(hd, rlist,
1279 			    flags & ~RCM_FORCE, &rinfo);
1280 		break;
1281 	}
1282 	case CMD_RCM_SUSPEND: {
1283 		timespec_t t;
1284 		t.tv_sec = (time_t)0;
1285 		t.tv_nsec = (long)0;
1286 		rsrc = OS;
1287 		ap_msg(a, MSG_ISSUE, cmd, rsrc);
1288 		rv = (*rcm->request_suspend)(hd, rsrc, flags, &t, &rinfo);
1289 		break;
1290 	}
1291 	case CMD_RCM_RESUME:
1292 		rsrc = OS;
1293 		ap_msg(a, MSG_ISSUE, cmd, rsrc);
1294 		rv = (*rcm->notify_resume)(hd, rsrc, 0, &rinfo);
1295 		break;
1296 	default:
1297 		ap_err(a, ERR_CMD_INVAL, cmd);
1298 		return (CFGA_INVAL);
1299 	}
1300 
1301 	if (rv != RCM_SUCCESS) {
1302 		rcm->rinfo = rinfo;
1303 		rcm->infot = NULL;
1304 		ap_err(a, ERR_RCM_CMD, cmd);
1305 		(*rcm->free_info)(rinfo);
1306 		if (rc == CFGA_OK)
1307 			rc = CFGA_LIB_ERROR;	/* make sure error is set */
1308 	}
1309 	if ((rc == CFGA_OK) && (noop == 0)) {
1310 		if (rlist)
1311 			for (i = 0; rlist[i]; i++)
1312 				ap_msg(a, MSG_DONE, cmd, rlist[i]);
1313 		else if (rsrc)
1314 			ap_msg(a, MSG_DONE, cmd, rsrc);
1315 		else
1316 			ap_msg(a, MSG_DONE, cmd, a->target);
1317 	}
1318 
1319 	return (rc);
1320 }
1321 
1322 /*
1323  * ap_rcm_info
1324  *
1325  * Takes an ap_id and a character pointer, and formats
1326  * the rcm_info_t data in the form of a table to the given character pointer.
1327  * Code duplicated from the scsi plugin.
1328  * Note: This function will go away when a generic librcm callback is
1329  *	implemented to format RCM messages for plugins.
1330  */
1331 int
1332 ap_rcm_info(apd_t *a, char **msg)
1333 {
1334 	rcmd_t *rcm;
1335 	rcm_info_t *rinfo;
1336 	int i;
1337 	size_t w;
1338 	size_t width = 0;
1339 	size_t w_rsrc = 0;
1340 	size_t w_info = 0;
1341 	size_t msg_size = 0;
1342 	uint_t tuples = 0;
1343 	rcm_info_tuple_t *tuple = NULL;
1344 	char *rsrc;
1345 	char *info;
1346 	char *newmsg;
1347 	static char format[RCM_MAX_FORMAT];
1348 	const char *infostr;
1349 
1350 
1351 	DBG("ap_rcm_info(%p)\n", (void *)a);
1352 
1353 	/* Protect against invalid arguments */
1354 	if ((a == NULL) || ((rcm = (rcmd_t *)a->rcm) == NULL) ||
1355 	    ((rinfo = rcm->rinfo) == NULL) || (msg == NULL)) {
1356 		return (-1);
1357 	}
1358 
1359 	/* Set localized table header strings */
1360 	rsrc = dgettext(TEXT_DOMAIN, "Resource");
1361 	info = dgettext(TEXT_DOMAIN, "Information");
1362 
1363 	/* A first pass, to size up the RCM information */
1364 	while (tuple = (*rcm->info_next)(rinfo, tuple)) {
1365 		if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1366 			tuples++;
1367 			if ((w = strlen((*rcm->info_rsrc)(tuple))) > w_rsrc)
1368 				w_rsrc = w;
1369 			if ((w = strlen(infostr)) > w_info)
1370 				w_info = w;
1371 		}
1372 	}
1373 
1374 	/* If nothing was sized up above, stop early */
1375 	if (tuples == 0)
1376 		return (0);
1377 
1378 	/* Adjust column widths for column headings */
1379 	if ((w = strlen(rsrc)) > w_rsrc)
1380 		w_rsrc = w;
1381 	else if ((w_rsrc - w) % 2)
1382 		w_rsrc++;
1383 	if ((w = strlen(info)) > w_info)
1384 		w_info = w;
1385 	else if ((w_info - w) % 2)
1386 		w_info++;
1387 
1388 	/*
1389 	 * Compute the total line width of each line,
1390 	 * accounting for intercolumn spacing.
1391 	 */
1392 	width = w_info + w_rsrc + 4;
1393 
1394 	/* Allocate space for the table */
1395 	msg_size = (2 + tuples) * (width + 1) + 2;
1396 	if (*msg == NULL) {
1397 		/* zero fill for the strcat() call below */
1398 		*msg = calloc(msg_size, sizeof (char));
1399 		if (*msg == NULL)
1400 			return (-1);
1401 	} else {
1402 		newmsg = realloc(*msg, strlen(*msg) + msg_size);
1403 		if (newmsg == NULL)
1404 			return (-1);
1405 		else
1406 			*msg = newmsg;
1407 	}
1408 
1409 	/* Place a table header into the string */
1410 
1411 	/* The resource header */
1412 	(void) strcat(*msg, "\n");
1413 	w = strlen(rsrc);
1414 	for (i = 0; i < ((w_rsrc - w) / 2); i++)
1415 		(void) strcat(*msg, " ");
1416 	(void) strcat(*msg, rsrc);
1417 	for (i = 0; i < ((w_rsrc - w) / 2); i++)
1418 		(void) strcat(*msg, " ");
1419 
1420 	/* The information header */
1421 	(void) strcat(*msg, "  ");
1422 	w = strlen(info);
1423 	for (i = 0; i < ((w_info - w) / 2); i++)
1424 		(void) strcat(*msg, " ");
1425 	(void) strcat(*msg, info);
1426 	for (i = 0; i < ((w_info - w) / 2); i++)
1427 		(void) strcat(*msg, " ");
1428 
1429 	/* Underline the headers */
1430 	(void) strcat(*msg, "\n");
1431 	for (i = 0; i < w_rsrc; i++)
1432 		(void) strcat(*msg, "-");
1433 	(void) strcat(*msg, "  ");
1434 	for (i = 0; i < w_info; i++)
1435 		(void) strcat(*msg, "-");
1436 
1437 	/* Construct the format string */
1438 	(void) snprintf(format, RCM_MAX_FORMAT, "%%-%ds  %%-%ds",
1439 	    (int)w_rsrc, (int)w_info);
1440 
1441 	/* Add the tuples to the table string */
1442 	tuple = NULL;
1443 	while ((tuple = (*rcm->info_next)(rinfo, tuple)) != NULL) {
1444 		if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1445 			(void) strcat(*msg, "\n");
1446 			(void) sprintf(&((*msg)[strlen(*msg)]), format,
1447 			    (*rcm->info_rsrc)(tuple), infostr);
1448 		}
1449 	}
1450 
1451 	DBG("ap_rcm_info(%p) success\n", (void *)a);
1452 	return (0);
1453 }
1454