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