xref: /illumos-gate/usr/src/cmd/drd/drd_rcm.c (revision 4e5b757f)
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 /*
23  * Copyright 2006 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 /*
30  * RCM backend for the DR Daemon
31  */
32 
33 #include <unistd.h>
34 #include <strings.h>
35 #include <errno.h>
36 #include <kstat.h>
37 #include <libnvpair.h>
38 #include <librcm.h>
39 
40 #include "drd.h"
41 
42 /*
43  * RCM Backend Support
44  */
45 static int drd_rcm_init(void);
46 static int drd_rcm_fini(void);
47 static int drd_rcm_cpu_config_request(drctl_rsrc_t *rsrcs, int nrsrc);
48 static int drd_rcm_cpu_config_notify(drctl_rsrc_t *rsrcs, int nrsrc);
49 static int drd_rcm_cpu_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc);
50 static int drd_rcm_cpu_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc);
51 
52 drd_backend_t drd_rcm_backend = {
53 	drd_rcm_init,			/* init */
54 	drd_rcm_fini,			/* fini */
55 	drd_rcm_cpu_config_request,	/* cpu_config_request */
56 	drd_rcm_cpu_config_notify,	/* cpu_config_notify */
57 	drd_rcm_cpu_unconfig_request,	/* cpu_unconfig_request */
58 	drd_rcm_cpu_unconfig_notify	/* cpu_unconfig_notify */
59 };
60 
61 #define	RCM_CPU_ALL		"SUNW_cpu"
62 #define	RCM_CPU			RCM_CPU_ALL"/cpu"
63 #define	RCM_CPU_MAX_LEN		(32)
64 
65 /* global RCM handle used in all RCM operations */
66 static rcm_handle_t *rcm_hdl;
67 
68 /* functions that call into RCM */
69 static int drd_rcm_online_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
70 static int drd_rcm_add_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
71 static int drd_rcm_del_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc);
72 static int drd_rcm_offline_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc);
73 static int drd_rcm_remove_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
74 static int drd_rcm_restore_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
75 static int drd_rcm_del_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
76 
77 /* utility functions */
78 static char **drd_rcm_cpu_rlist_init(drctl_rsrc_t *, int nrsrc, int status);
79 static void drd_rcm_cpu_rlist_fini(char **rlist);
80 static drctl_rsrc_t *cpu_rsrcstr_to_rsrc(const char *, drctl_rsrc_t *, int);
81 static int get_sys_cpuids(cpuid_t **cpuids, int *ncpuids);
82 static boolean_t is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int len);
83 
84 /* debugging utility functions */
85 static void dump_cpu_list(char *prefix, cpuid_t *cpuids, int ncpuids);
86 static void dump_cpu_rsrc_list(char *prefix, drctl_rsrc_t *, int nrsrc);
87 static void dump_cpu_rlist(char **rlist);
88 
89 static int
90 drd_rcm_init(void)
91 {
92 	int	rv;
93 
94 	drd_dbg("drd_rcm_init...");
95 
96 	rv = rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl);
97 	if (rv == RCM_FAILURE) {
98 		drd_err("unable to allocate RCM handle: %s", strerror(errno));
99 		return (-1);
100 	}
101 
102 	return (0);
103 }
104 
105 static int
106 drd_rcm_fini(void)
107 {
108 	drd_dbg("drd_rcm_fini...");
109 
110 	if (rcm_hdl != NULL)
111 		rcm_free_handle(rcm_hdl);
112 
113 	return (0);
114 }
115 
116 static int
117 drd_rcm_cpu_config_request(drctl_rsrc_t *rsrcs, int nrsrc)
118 {
119 	int	idx;
120 
121 	drd_dbg("drd_rcm_cpu_config_request...");
122 	dump_cpu_rsrc_list(NULL, rsrcs, nrsrc);
123 
124 	/*
125 	 * There is no RCM operation to request the addition
126 	 * of resources. So, by definition, the operation for
127 	 * all the CPUs is allowed.
128 	 */
129 	for (idx = 0; idx < nrsrc; idx++)
130 		rsrcs[idx].status = DRCTL_STATUS_ALLOW;
131 
132 	dump_cpu_rsrc_list("returning:", rsrcs, nrsrc);
133 
134 	return (0);
135 }
136 
137 static int
138 drd_rcm_cpu_config_notify(drctl_rsrc_t *rsrcs, int nrsrc)
139 {
140 	int	rv = 0;
141 
142 	drd_dbg("drd_rcm_cpu_config_notify...");
143 	dump_cpu_rsrc_list(NULL, rsrcs, nrsrc);
144 
145 	/* notify RCM about the newly added CPUs */
146 	if (drd_rcm_online_cpu_notify(rsrcs, nrsrc) != 0) {
147 		rv = -1;
148 		goto done;
149 	}
150 
151 	/* notify RCM about the increased CPU capacity */
152 	if (drd_rcm_add_cpu_notify(rsrcs, nrsrc) != 0) {
153 		rv = -1;
154 	}
155 
156 done:
157 	dump_cpu_rsrc_list("returning:", rsrcs, nrsrc);
158 
159 	return (rv);
160 }
161 
162 static int
163 drd_rcm_cpu_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc)
164 {
165 	int	rv = 0;
166 	int	idx;
167 
168 	drd_dbg("drd_rcm_cpu_unconfig_request...");
169 	dump_cpu_rsrc_list(NULL, rsrcs, nrsrc);
170 
171 	/* contact RCM to request a decrease in CPU capacity */
172 	if (drd_rcm_del_cpu_request(rsrcs, nrsrc) != 0) {
173 		rv = -1;
174 		goto done;
175 	}
176 
177 	/* contact RCM to request the removal of CPUs */
178 	if (drd_rcm_offline_cpu_request(rsrcs, nrsrc) != 0) {
179 		rv = -1;
180 		goto done;
181 	}
182 
183 done:
184 	/*
185 	 * If any errors occurred, the status field for
186 	 * a CPU may still be in the INIT state. Set the
187 	 * status for any such CPU to DENY to ensure it
188 	 * gets processed properly.
189 	 */
190 	for (idx = 0; idx < nrsrc; idx++) {
191 		if (rsrcs[idx].status == DRCTL_STATUS_INIT)
192 			rsrcs[idx].status = DRCTL_STATUS_DENY;
193 	}
194 
195 	dump_cpu_rsrc_list("returning:", rsrcs, nrsrc);
196 
197 	return (rv);
198 }
199 
200 static int
201 drd_rcm_cpu_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc)
202 {
203 	int	rv = 0;
204 
205 	drd_dbg("drd_rcm_cpu_unconfig_notify...");
206 	dump_cpu_rsrc_list(NULL, rsrcs, nrsrc);
207 
208 	/*
209 	 * Notify RCM about the CPUs that were removed.
210 	 * Failures are ignored so that CPUs that could
211 	 * not be unconfigured can be processed by RCM.
212 	 */
213 	(void) drd_rcm_remove_cpu_notify(rsrcs, nrsrc);
214 
215 	/*
216 	 * Notify RCM about any CPUs that did not make it
217 	 * in to the unconfigured state.
218 	 */
219 	if (drd_rcm_restore_cpu_notify(rsrcs, nrsrc) != 0) {
220 		rv = -1;
221 		goto done;
222 	}
223 
224 	/* notify RCM about the decreased CPU capacity */
225 	if (drd_rcm_del_cpu_notify(rsrcs, nrsrc) != 0) {
226 		rv = -1;
227 	}
228 
229 done:
230 	dump_cpu_rsrc_list("returning:", rsrcs, nrsrc);
231 
232 	return (rv);
233 }
234 
235 static int
236 drd_rcm_online_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
237 {
238 	char		**rlist;
239 	int		rv = 0;
240 	rcm_info_t	*rinfo;
241 
242 	drd_dbg("drd_rcm_online_cpu_notify...");
243 
244 	if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc,
245 	    DRCTL_STATUS_CONFIG_SUCCESS)) == NULL) {
246 		drd_dbg("  no CPUs were successfully added, nothing to do");
247 		return (0);
248 	}
249 
250 	rcm_notify_online_list(rcm_hdl, rlist, 0, &rinfo);
251 	if (rv != RCM_SUCCESS) {
252 		drd_info("rcm_notify_online_list failed: %d", rv);
253 		rcm_free_info(rinfo);
254 		rv = -1;
255 	}
256 
257 	drd_rcm_cpu_rlist_fini(rlist);
258 
259 	return (rv);
260 }
261 
262 static int
263 drd_rcm_add_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
264 {
265 	cpuid_t		*cpus = NULL;
266 	int		ncpus;
267 	int		rv = -1;
268 	cpuid_t		*oldcpus = NULL;
269 	cpuid_t		*newcpus = NULL;
270 	int		oldncpus = 0;
271 	int		newncpus = 0;
272 	nvlist_t	*nvl = NULL;
273 	int		idx;
274 	rcm_info_t	*rinfo;
275 
276 	drd_dbg("drd_rcm_add_cpu_notify...");
277 
278 	if ((rsrcs == NULL) || (nrsrc == 0)) {
279 		drd_err("add_cpu_notify: cpu list empty");
280 		goto done;
281 	}
282 
283 	ncpus = nrsrc;
284 	cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t));
285 
286 	for (idx = 0; idx < nrsrc; idx++) {
287 		drd_dbg("  cpu[%d] = %d", idx, rsrcs[idx].res_cpu_id);
288 		cpus[idx] = rsrcs[idx].res_cpu_id;
289 	}
290 
291 	/* allocate an nvlist for the RCM call */
292 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
293 		goto done;
294 
295 	/*
296 	 * Added CPU capacity, so newcpus is the current list
297 	 * of CPUs in the system.
298 	 */
299 	if (get_sys_cpuids(&newcpus, &newncpus) == -1)
300 		goto done;
301 
302 	/*
303 	 * Since the operation added CPU capacity, the old CPU
304 	 * list is the new CPU list with the CPUs involved in
305 	 * the operation removed.
306 	 */
307 	oldcpus = (cpuid_t *)calloc(newncpus, sizeof (cpuid_t));
308 	if (oldcpus == NULL)
309 		goto done;
310 
311 	for (idx = 0; idx < newncpus; idx++) {
312 		if (!is_cpu_in_list(newcpus[idx], cpus, ncpus))
313 			oldcpus[oldncpus++] = newcpus[idx];
314 	}
315 
316 	/* dump pre and post lists */
317 	dump_cpu_list("oldcpus: ", oldcpus, oldncpus);
318 	dump_cpu_list("newcpus: ", newcpus, newncpus);
319 	dump_cpu_list("delta:   ", cpus, ncpus);
320 
321 	/* setup the nvlist for the RCM call */
322 	if (nvlist_add_string(nvl, "state", "capacity") ||
323 	    nvlist_add_int32(nvl, "old_total", oldncpus) ||
324 	    nvlist_add_int32(nvl, "new_total", newncpus) ||
325 	    nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) ||
326 	    nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) {
327 		goto done;
328 	}
329 
330 	rv = rcm_notify_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo);
331 	rv = (rv == RCM_SUCCESS) ? 0 : -1;
332 
333 done:
334 	s_nvfree(nvl);
335 	s_free(cpus);
336 	s_free(oldcpus);
337 	s_free(newcpus);
338 
339 	return (rv);
340 }
341 
342 static int
343 drd_rcm_del_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc)
344 {
345 	cpuid_t		*cpus = NULL;
346 	int		ncpus;
347 	int		rv = -1;
348 	cpuid_t		*oldcpus = NULL;
349 	cpuid_t		*newcpus = NULL;
350 	int		oldncpus = 0;
351 	int		newncpus = 0;
352 	nvlist_t	*nvl = NULL;
353 	int		idx;
354 	rcm_info_t	*rinfo;
355 
356 	drd_dbg("drd_rcm_del_cpu_request...");
357 
358 	if ((rsrcs == NULL) || (nrsrc == 0)) {
359 		drd_err("del_cpu_request: cpu list empty");
360 		goto done;
361 	}
362 
363 	ncpus = nrsrc;
364 	cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t));
365 
366 	for (idx = 0; idx < nrsrc; idx++) {
367 		cpus[idx] = rsrcs[idx].res_cpu_id;
368 	}
369 
370 	/* allocate an nvlist for the RCM call */
371 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
372 		goto done;
373 	}
374 
375 	/*
376 	 * Removing CPU capacity, so oldcpus is the current
377 	 * list of CPUs in the system.
378 	 */
379 	if (get_sys_cpuids(&oldcpus, &oldncpus) == -1) {
380 		goto done;
381 	}
382 
383 	/*
384 	 * Since this is a request to remove CPU capacity,
385 	 * the new CPU list is the old CPU list with the CPUs
386 	 * involved in the operation removed.
387 	 */
388 	newcpus = (cpuid_t *)calloc(oldncpus, sizeof (cpuid_t));
389 	if (newcpus == NULL) {
390 		goto done;
391 	}
392 
393 	for (idx = 0; idx < oldncpus; idx++) {
394 		if (!is_cpu_in_list(oldcpus[idx], cpus, ncpus))
395 			newcpus[newncpus++] = oldcpus[idx];
396 	}
397 
398 	/* dump pre and post lists */
399 	dump_cpu_list("oldcpus: ", oldcpus, oldncpus);
400 	dump_cpu_list("newcpus: ", newcpus, newncpus);
401 	dump_cpu_list("delta:   ", cpus, ncpus);
402 
403 	/* setup the nvlist for the RCM call */
404 	if (nvlist_add_string(nvl, "state", "capacity") ||
405 	    nvlist_add_int32(nvl, "old_total", oldncpus) ||
406 	    nvlist_add_int32(nvl, "new_total", newncpus) ||
407 	    nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) ||
408 	    nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) {
409 		goto done;
410 	}
411 
412 	rv = rcm_request_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo);
413 	if (rv != RCM_SUCCESS) {
414 		drd_dbg("RCM call failed: %d", rv);
415 		/*
416 		 * Since the capcity change was blocked, we
417 		 * mark all CPUs as blocked. It is up to the
418 		 * user to reframe the query so that it can
419 		 * succeed.
420 		 */
421 		for (idx = 0; idx < nrsrc; idx++) {
422 			rsrcs[idx].status = DRCTL_STATUS_DENY;
423 		}
424 
425 		/* tack on message to first resource */
426 		rsrcs[0].offset = (uintptr_t)strdup("unable to remove "
427 		    "specified number of CPUs");
428 		drd_dbg("  unable to remove specified number of CPUs");
429 		goto done;
430 	}
431 
432 	rv = 0;
433 
434 done:
435 	s_nvfree(nvl);
436 	s_free(cpus);
437 	s_free(oldcpus);
438 	s_free(newcpus);
439 
440 	return (rv);
441 }
442 
443 static int
444 drd_rcm_offline_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc)
445 {
446 	char		**rlist;
447 	drctl_rsrc_t	*rsrc;
448 	int		idx;
449 	int		state;
450 	int		rv = 0;
451 	rcm_info_t	*rinfo = NULL;
452 	rcm_info_tuple_t *tuple = NULL;
453 	const char	*rsrcstr;
454 	const char	*errstr;
455 
456 	drd_dbg("drd_rcm_offline_cpu_request...");
457 
458 	if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc,
459 	    DRCTL_STATUS_INIT)) == NULL) {
460 		drd_err("unable to generate resource list");
461 		return (-1);
462 	}
463 
464 	rv = rcm_request_offline_list(rcm_hdl, rlist, 0, &rinfo);
465 	if (rv == RCM_SUCCESS) {
466 		drd_dbg("RCM success, rinfo=%p", rinfo);
467 		goto done;
468 	}
469 
470 	drd_dbg("RCM call failed (%d):", rv);
471 
472 	/*
473 	 * Loop through the result of the operation and add
474 	 * any error messages to the resource structure.
475 	 */
476 	while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) {
477 
478 		/* find the resource of interest */
479 		rsrcstr = rcm_info_rsrc(tuple);
480 		rsrc = cpu_rsrcstr_to_rsrc(rsrcstr, rsrcs, nrsrc);
481 
482 		if (rsrc == NULL) {
483 			drd_dbg("unable to find resource for %s", rsrcstr);
484 			continue;
485 		}
486 
487 		errstr = rcm_info_error(tuple);
488 
489 		if (errstr) {
490 			drd_dbg("  %s: '%s'", rsrcstr, errstr);
491 			rsrc->offset = (uintptr_t)strdup(errstr);
492 		}
493 	}
494 
495 	rcm_free_info(rinfo);
496 	rv = 0;
497 
498 done:
499 	/*
500 	 * Set the state of the resource based on the RCM
501 	 * state. CPUs in the offline state have the ok to
502 	 * proceed. All others have been blocked.
503 	 */
504 	for (idx = 0; rlist[idx] != NULL; idx++) {
505 
506 		state = 0;
507 		rcm_get_rsrcstate(rcm_hdl, rlist[idx], &state);
508 
509 		/* find the resource of interest */
510 		rsrc = cpu_rsrcstr_to_rsrc(rlist[idx], rsrcs, nrsrc);
511 
512 		if (rsrc == NULL) {
513 			drd_dbg("unable to find resource for %s", rlist[idx]);
514 			continue;
515 		}
516 
517 		rsrc->status = ((state == RCM_STATE_OFFLINE) ?
518 		    DRCTL_STATUS_ALLOW : DRCTL_STATUS_DENY);
519 	}
520 
521 	drd_rcm_cpu_rlist_fini(rlist);
522 
523 	return (rv);
524 }
525 
526 static int
527 drd_rcm_remove_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
528 {
529 	char		**rlist;
530 	int		rv = 0;
531 	rcm_info_t	*rinfo;
532 
533 	drd_dbg("drd_rcm_remove_cpu_notify...");
534 
535 	if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc,
536 	    DRCTL_STATUS_CONFIG_SUCCESS)) == NULL) {
537 		drd_dbg("  no CPUs in the success state, nothing to do");
538 		return (0);
539 	}
540 
541 	rv = rcm_notify_remove_list(rcm_hdl, rlist, 0, &rinfo);
542 	if (rv != RCM_SUCCESS) {
543 		drd_info("rcm_notify_remove_list failed: %d", rv);
544 		rcm_free_info(rinfo);
545 		rv = -1;
546 	}
547 
548 	drd_rcm_cpu_rlist_fini(rlist);
549 
550 	return (rv);
551 }
552 
553 static int
554 drd_rcm_restore_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
555 {
556 	char		**rlist;
557 	char		**full_rlist;
558 	int		idx;
559 	int		ridx;
560 	int		state;
561 	int		rv = 0;
562 	rcm_info_t	*rinfo;
563 
564 	drd_dbg("drd_rcm_restore_cpu_notify...");
565 
566 	if ((full_rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc,
567 	    DRCTL_STATUS_CONFIG_FAILURE)) == NULL) {
568 		drd_dbg("  no CPUs in the failed state, nothing to do");
569 		return (0);
570 	}
571 
572 	/*
573 	 * Since the desired result of this operation is to
574 	 * restore resources to the online state, filter out
575 	 * the resources already in the online state before
576 	 * passing the list to RCM.
577 	 */
578 
579 	/* allocate a zero filled array to ensure NULL terminated list */
580 	rlist = (char **)calloc((nrsrc + 1), sizeof (char *));
581 	if (rlist == NULL) {
582 		drd_err("calloc failed: %s", strerror(errno));
583 		rv = -1;
584 		goto done;
585 	}
586 
587 	for (idx = 0, ridx = 0; full_rlist[idx] != NULL; idx++) {
588 		state = 0;
589 		rcm_get_rsrcstate(rcm_hdl, full_rlist[idx], &state);
590 		if (state != RCM_STATE_ONLINE) {
591 			rlist[ridx] = full_rlist[idx];
592 			ridx++;
593 		}
594 	}
595 
596 	/* check if everything got filtered out */
597 	if (ridx == 0) {
598 		drd_dbg("  all CPUs already online, nothing to do");
599 		goto done;
600 	}
601 
602 	rv = rcm_notify_online_list(rcm_hdl, rlist, 0, &rinfo);
603 	if (rv != RCM_SUCCESS) {
604 		drd_info("rcm_notify_online_list failed: %d", rv);
605 		rcm_free_info(rinfo);
606 		rv = -1;
607 	}
608 
609 done:
610 	drd_rcm_cpu_rlist_fini(full_rlist);
611 	s_free(rlist);
612 
613 	return (rv);
614 }
615 
616 static int
617 drd_rcm_del_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
618 {
619 	cpuid_t		*cpus = NULL;
620 	int		rv = -1;
621 	cpuid_t		*oldcpus = NULL;
622 	cpuid_t		*newcpus = NULL;
623 	int		oldncpus = 0;
624 	int		newncpus = 0;
625 	nvlist_t	*nvl = NULL;
626 	int		idx;
627 	int		cidx;
628 	rcm_info_t	*rinfo;
629 
630 	drd_dbg("drd_rcm_del_cpu_notify...");
631 
632 	if ((rsrcs == NULL) || (nrsrc == 0)) {
633 		drd_err("del_cpu_notify: cpu list empty");
634 		goto done;
635 	}
636 
637 	cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t));
638 
639 	/*
640 	 * Filter out the CPUs that could not be unconfigured.
641 	 */
642 	for (idx = 0, cidx = 0; idx < nrsrc; idx++) {
643 		if (rsrcs[idx].status != DRCTL_STATUS_CONFIG_SUCCESS)
644 			continue;
645 		drd_dbg("  cpu[%d] = %d", idx, rsrcs[idx].res_cpu_id);
646 		cpus[cidx] = rsrcs[idx].res_cpu_id;
647 		cidx++;
648 	}
649 
650 	drd_dbg("  ncpus = %d", cidx);
651 
652 	/* nothing to do */
653 	if (cidx == 0) {
654 		rv = 0;
655 		goto done;
656 	}
657 
658 	/* allocate an nvlist for the RCM call */
659 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
660 		goto done;
661 	}
662 
663 	/*
664 	 * Removed CPU capacity, so newcpus is the current list
665 	 * of CPUs in the system.
666 	 */
667 	if (get_sys_cpuids(&newcpus, &newncpus) == -1) {
668 		goto done;
669 	}
670 
671 	/*
672 	 * Since the operation removed CPU capacity, the old CPU
673 	 * list is the new CPU list with the CPUs involved in
674 	 * the operation added.
675 	 */
676 	oldcpus = (cpuid_t *)calloc(newncpus + cidx, sizeof (cpuid_t));
677 	if (oldcpus == NULL) {
678 		goto done;
679 	}
680 
681 	for (idx = 0; idx < newncpus; idx++) {
682 		if (!is_cpu_in_list(newcpus[idx], cpus, cidx))
683 			oldcpus[oldncpus++] = newcpus[idx];
684 	}
685 
686 	for (idx = 0; idx < cidx; idx++) {
687 		oldcpus[oldncpus++] = cpus[idx];
688 	}
689 
690 	/* dump pre and post lists */
691 	dump_cpu_list("oldcpus: ", oldcpus, oldncpus);
692 	dump_cpu_list("newcpus: ", newcpus, newncpus);
693 	dump_cpu_list("delta:   ", cpus, cidx);
694 
695 	/* setup the nvlist for the RCM call */
696 	if (nvlist_add_string(nvl, "state", "capacity") ||
697 	    nvlist_add_int32(nvl, "old_total", oldncpus) ||
698 	    nvlist_add_int32(nvl, "new_total", newncpus) ||
699 	    nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) ||
700 	    nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) {
701 		goto done;
702 	}
703 
704 	rv = rcm_notify_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo);
705 	rv = (rv == RCM_SUCCESS) ? 0 : -1;
706 
707 done:
708 	s_nvfree(nvl);
709 	s_free(cpus);
710 	s_free(oldcpus);
711 	s_free(newcpus);
712 
713 	return (rv);
714 }
715 
716 /*
717  * Given a list of resource structures, create a list of CPU
718  * resource strings formatted as expected by RCM. Only resources
719  * that are in the state specified by the status argument are
720  * included in the resulting list.
721  */
722 static char **
723 drd_rcm_cpu_rlist_init(drctl_rsrc_t *rsrcs, int nrsrc, int status)
724 {
725 	char	rbuf[RCM_CPU_MAX_LEN];
726 	char	**rlist;
727 	int	idx;
728 	int	ridx;
729 
730 	drd_dbg("drd_rcm_cpu_rlist_init...");
731 
732 	if ((rsrcs == NULL) || (nrsrc == 0)) {
733 		drd_dbg("cpu list is empty");
734 		return (NULL);
735 	}
736 
737 	/* allocate a zero filled array to ensure NULL terminated list */
738 	rlist = (char **)calloc((nrsrc + 1), sizeof (char *));
739 	if (rlist == NULL) {
740 		drd_err("calloc failed: %s", strerror(errno));
741 		return (NULL);
742 	}
743 
744 	for (idx = 0, ridx = 0; idx < nrsrc; idx++) {
745 
746 		drd_dbg("  checking cpu %d, status=%d, expected status=%d",
747 		    rsrcs[idx].res_cpu_id, rsrcs[idx].status, status);
748 
749 		/*
750 		 * Filter out the CPUs that are not in
751 		 * the requested state.
752 		 */
753 		if (rsrcs[idx].status != status)
754 			continue;
755 
756 		/* generate the resource string */
757 		(void) sprintf(rbuf, "%s%d", RCM_CPU, rsrcs[idx].res_cpu_id);
758 
759 		rlist[ridx] = strdup(rbuf);
760 		if (rlist[ridx] == NULL) {
761 			drd_err("strdup failed: %s", strerror(errno));
762 			drd_rcm_cpu_rlist_fini(rlist);
763 			return (NULL);
764 		}
765 
766 		ridx++;
767 	}
768 
769 	/* cleanup if the list is empty */
770 	if (ridx == 0) {
771 		s_free(rlist);
772 	}
773 
774 	drd_dbg("final rlist:");
775 	dump_cpu_rlist(rlist);
776 
777 	return (rlist);
778 }
779 
780 static void
781 drd_rcm_cpu_rlist_fini(char **rlist)
782 {
783 	int idx;
784 
785 	drd_dbg("drd_rcm_cpu_rlist_fini...");
786 
787 	dump_cpu_rlist(rlist);
788 
789 	for (idx = 0; rlist[idx] != NULL; idx++) {
790 		s_free(rlist[idx]);
791 	}
792 
793 	s_free(rlist);
794 }
795 
796 /*
797  * Convert an RCM CPU resource string into a numerical cpuid.
798  * Assumes the resource string has the form: "SUNW_cpu/cpu<C>"
799  * where "<C>" is the numerical cpuid of interest.
800  */
801 static cpuid_t
802 cpu_rsrcstr_to_cpuid(const char *rsrc)
803 {
804 	char	*cpuid_off;
805 	cpuid_t	cpuid;
806 
807 	/*
808 	 * Search for the last occurrance of 'u' in the
809 	 * expected RCM resource string "SUNW_cpu/cpu<C>".
810 	 * This will give a pointer to the cpuid portion.
811 	 */
812 	cpuid_off = strrchr(rsrc, 'u');
813 	cpuid_off++;
814 
815 	cpuid = atoi(cpuid_off);
816 
817 	return (cpuid);
818 }
819 
820 /*
821  * Given an RCM CPU resource string, return a pointer to the
822  * corresponding resource structure from the given resource list.
823  * NULL is returned if no matching resource structure can be
824  * found.
825  */
826 static drctl_rsrc_t *
827 cpu_rsrcstr_to_rsrc(const char *rsrcstr, drctl_rsrc_t *rsrcs, int nrsrc)
828 {
829 	cpuid_t	cpuid;
830 	int	idx;
831 
832 	cpuid = cpu_rsrcstr_to_cpuid(rsrcstr);
833 
834 	for (idx = 0; idx < nrsrc; idx++) {
835 		if (rsrcs[idx].res_cpu_id == cpuid)
836 			return (&rsrcs[idx]);
837 	}
838 
839 	return (NULL);
840 }
841 
842 static int
843 get_sys_cpuids(cpuid_t **cpuids, int *ncpuids)
844 {
845 	int		ncpu = 0;
846 	int		maxncpu;
847 	kstat_t		*ksp;
848 	kstat_ctl_t	*kc = NULL;
849 	cpuid_t		*cp;
850 
851 	drd_dbg("get_sys_cpuids...");
852 
853 	if ((maxncpu = sysconf(_SC_NPROCESSORS_MAX)) == -1)
854 		return (-1);
855 
856 	if ((kc = kstat_open()) == NULL)
857 		return (-1);
858 
859 	if ((cp = (cpuid_t *)calloc(maxncpu, sizeof (cpuid_t))) == NULL) {
860 		(void) kstat_close(kc);
861 		return (-1);
862 	}
863 
864 	for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
865 		if (strcmp(ksp->ks_module, "cpu_info") == 0)
866 			cp[ncpu++] = ksp->ks_instance;
867 	}
868 
869 	dump_cpu_list("syscpus: ", cp, ncpu);
870 
871 	(void) kstat_close(kc);
872 
873 	*cpuids = cp;
874 	*ncpuids = ncpu;
875 
876 	return (0);
877 }
878 
879 static boolean_t
880 is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int len)
881 {
882 	int idx;
883 
884 	if (list == NULL)
885 		return (B_FALSE);
886 
887 	for (idx = 0; idx < len; idx++) {
888 		if (list[idx] == cpuid)
889 			return (B_TRUE);
890 	}
891 
892 	return (B_FALSE);
893 }
894 
895 #define	CPUIDS_PER_LINE		16
896 #define	LINEWIDTH		(2 * (CPUIDS_PER_LINE * 4))
897 
898 static void
899 dump_cpu_list(char *prefix, cpuid_t *cpuids, int ncpuids)
900 {
901 	char	line[LINEWIDTH];
902 	char	*curr;
903 	int	i, j;
904 
905 	/* return if not debugging */
906 	if (drd_debug == 0)
907 		return;
908 
909 	/* print just the prefix if CPU list is empty */
910 	if (ncpuids == 0) {
911 		if (prefix)
912 			drd_dbg("%s", prefix);
913 		return;
914 	}
915 
916 	for (i = 0; i < ncpuids; i += CPUIDS_PER_LINE) {
917 
918 		bzero(line, LINEWIDTH);
919 		curr = line;
920 
921 		/* start with the prefix */
922 		(void) sprintf(curr, "%s", (prefix) ? prefix : "");
923 		curr = line + strlen(line);
924 
925 		/* format the CPUs for this line */
926 		for (j = 0; (j < CPUIDS_PER_LINE) && ((i + j) < ncpuids); j++) {
927 			(void) sprintf(curr, "%3d ", cpuids[i + j]);
928 			curr = line + strlen(line);
929 		}
930 
931 		drd_dbg("%s", line);
932 	}
933 }
934 
935 static void
936 dump_cpu_rsrc_list(char *prefix, drctl_rsrc_t *rsrcs, int nrsrc)
937 {
938 	int	idx;
939 	char	*errstr;
940 
941 	/* just return if not debugging */
942 	if (drd_debug == 0)
943 		return;
944 
945 	if (prefix)
946 		drd_dbg("%s", prefix);
947 
948 	for (idx = 0; idx < nrsrc; idx++) {
949 
950 		/* get a pointer to the error string */
951 		errstr = (char *)(uintptr_t)rsrcs[idx].offset;
952 
953 		drd_dbg("  cpu[%d]: cpuid=%d, status=%d, errstr='%s'", idx,
954 		    rsrcs[idx].res_cpu_id, rsrcs[idx].status,
955 		    (errstr != NULL) ? errstr : "");
956 	}
957 }
958 
959 static void
960 dump_cpu_rlist(char **rlist)
961 {
962 	int	idx;
963 	int	state;
964 
965 	static char *rcm_state_str[] = {
966 		"UNKNOWN",		"ONLINE",		"ONLINING",
967 		"OFFLINE_FAIL",		"OFFLINING",		"OFFLINE",
968 		"REMOVING",		"INVALID_7",		"INVALID_8",
969 		"INVALID_9",		"RESUMING",		"SUSPEND_FAIL",
970 		"SUSPENDING",		"SUSPEND",		"REMOVE",
971 		"OFFLINE_QUERYING",	"OFFLINE_QUERY_FAIL",	"OFFLINE_QUERY",
972 		"SUSPEND_QUERYING",	"SUSPEND_QUERY_FAIL",	"SUSPEND_QUERY"
973 	};
974 
975 	/* just return if not debugging */
976 	if (drd_debug == 0)
977 		return;
978 
979 	if (rlist == NULL) {
980 		drd_dbg("  empty rlist");
981 		return;
982 	}
983 
984 	for (idx = 0; rlist[idx] != NULL; idx++) {
985 		state = 0;
986 		rcm_get_rsrcstate(rcm_hdl, rlist[idx], &state);
987 		drd_dbg("  rlist[%d]: rsrc=%s, state=%-2d (%s)", idx,
988 		    rlist[idx], state, rcm_state_str[state]);
989 	}
990 }
991