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