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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * RCM backend for the DR Daemon
35 #include <libnvpair.h>
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
);
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 */
77 typedef int (*rcm_op_t
)(rcm_handle_t
*, char *, uint_t
, nvlist_t
*,
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)
85 /* global RCM handle used in all RCM operations */
86 static rcm_handle_t
*rcm_hdl
;
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
);
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
);
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
);
115 drd_dbg("drd_rcm_init...");
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
));
129 drd_dbg("drd_rcm_fini...");
132 rcm_free_handle(rcm_hdl
);
138 drd_rcm_cpu_config_request(drctl_rsrc_t
*rsrcs
, int nrsrc
)
142 drd_dbg("drd_rcm_cpu_config_request...");
143 dump_cpu_rsrc_list(NULL
, rsrcs
, nrsrc
);
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.
150 for (idx
= 0; idx
< nrsrc
; idx
++)
151 rsrcs
[idx
].status
= DRCTL_STATUS_ALLOW
;
153 dump_cpu_rsrc_list("returning:", rsrcs
, nrsrc
);
159 drd_rcm_cpu_config_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
163 drd_dbg("drd_rcm_cpu_config_notify...");
164 dump_cpu_rsrc_list(NULL
, rsrcs
, nrsrc
);
166 /* notify RCM about the newly added CPUs */
167 if (drd_rcm_online_cpu_notify(rsrcs
, nrsrc
) != 0) {
172 /* notify RCM about the increased CPU capacity */
173 if (drd_rcm_add_cpu_notify(rsrcs
, nrsrc
) != 0) {
178 dump_cpu_rsrc_list("returning:", rsrcs
, nrsrc
);
184 drd_rcm_cpu_unconfig_request(drctl_rsrc_t
*rsrcs
, int nrsrc
)
189 drd_dbg("drd_rcm_cpu_unconfig_request...");
190 dump_cpu_rsrc_list(NULL
, rsrcs
, nrsrc
);
192 /* contact RCM to request a decrease in CPU capacity */
193 if (drd_rcm_del_cpu_request(rsrcs
, nrsrc
) != 0) {
198 /* contact RCM to request the removal of CPUs */
199 if (drd_rcm_offline_cpu_request(rsrcs
, nrsrc
) != 0) {
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.
211 for (idx
= 0; idx
< nrsrc
; idx
++) {
212 if (rsrcs
[idx
].status
== DRCTL_STATUS_INIT
)
213 rsrcs
[idx
].status
= DRCTL_STATUS_DENY
;
216 dump_cpu_rsrc_list("returning:", rsrcs
, nrsrc
);
222 drd_rcm_cpu_unconfig_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
226 drd_dbg("drd_rcm_cpu_unconfig_notify...");
227 dump_cpu_rsrc_list(NULL
, rsrcs
, nrsrc
);
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.
234 (void) drd_rcm_remove_cpu_notify(rsrcs
, nrsrc
);
237 * Notify RCM about any CPUs that did not make it
238 * in to the unconfigured state.
240 if (drd_rcm_restore_cpu_notify(rsrcs
, nrsrc
) != 0) {
245 /* notify RCM about the decreased CPU capacity */
246 if (drd_rcm_del_cpu_notify(rsrcs
, nrsrc
) != 0) {
251 dump_cpu_rsrc_list("returning:", rsrcs
, nrsrc
);
257 drd_rcm_online_cpu_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
263 drd_dbg("drd_rcm_online_cpu_notify...");
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");
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
);
278 drd_rcm_cpu_rlist_fini(rlist
);
284 drd_rcm_add_cpu_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
286 cpuid_t
*cpus
= NULL
;
289 cpuid_t
*oldcpus
= NULL
;
290 cpuid_t
*newcpus
= NULL
;
293 nvlist_t
*nvl
= NULL
;
297 drd_dbg("drd_rcm_add_cpu_notify...");
299 if ((rsrcs
== NULL
) || (nrsrc
== 0)) {
300 drd_err("add_cpu_notify: cpu list empty");
305 cpus
= (cpuid_t
*)malloc(nrsrc
* sizeof (cpuid_t
));
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
;
312 /* allocate an nvlist for the RCM call */
313 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0)
317 * Added CPU capacity, so newcpus is the current list
318 * of CPUs in the system.
320 if (get_sys_cpuids(&newcpus
, &newncpus
) == -1)
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.
328 oldcpus
= (cpuid_t
*)calloc(newncpus
, sizeof (cpuid_t
));
332 for (idx
= 0; idx
< newncpus
; idx
++) {
333 if (!is_cpu_in_list(newcpus
[idx
], cpus
, ncpus
))
334 oldcpus
[oldncpus
++] = newcpus
[idx
];
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
);
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
)) {
351 rv
= rcm_notify_capacity_change(rcm_hdl
, RCM_CPU_ALL
, 0, nvl
, &rinfo
);
352 rv
= (rv
== RCM_SUCCESS
) ? 0 : -1;
364 drd_rcm_del_cpu_request(drctl_rsrc_t
*rsrcs
, int nrsrc
)
366 cpuid_t
*cpus
= NULL
;
369 cpuid_t
*oldcpus
= NULL
;
370 cpuid_t
*newcpus
= NULL
;
373 nvlist_t
*nvl
= NULL
;
377 drd_dbg("drd_rcm_del_cpu_request...");
379 if ((rsrcs
== NULL
) || (nrsrc
== 0)) {
380 drd_err("del_cpu_request: cpu list empty");
385 cpus
= (cpuid_t
*)malloc(nrsrc
* sizeof (cpuid_t
));
387 for (idx
= 0; idx
< nrsrc
; idx
++) {
388 cpus
[idx
] = rsrcs
[idx
].res_cpu_id
;
391 /* allocate an nvlist for the RCM call */
392 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0) {
397 * Removing CPU capacity, so oldcpus is the current
398 * list of CPUs in the system.
400 if (get_sys_cpuids(&oldcpus
, &oldncpus
) == -1) {
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.
409 newcpus
= (cpuid_t
*)calloc(oldncpus
, sizeof (cpuid_t
));
410 if (newcpus
== NULL
) {
414 for (idx
= 0; idx
< oldncpus
; idx
++) {
415 if (!is_cpu_in_list(oldcpus
[idx
], cpus
, ncpus
))
416 newcpus
[newncpus
++] = oldcpus
[idx
];
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
);
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
)) {
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
);
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
442 for (idx
= 0; idx
< nrsrc
; idx
++) {
443 rsrcs
[idx
].status
= DRCTL_STATUS_DENY
;
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");
465 drd_rcm_offline_cpu_request(drctl_rsrc_t
*rsrcs
, int nrsrc
)
472 rcm_info_t
*rinfo
= NULL
;
473 rcm_info_tuple_t
*tuple
= NULL
;
477 drd_dbg("drd_rcm_offline_cpu_request...");
479 if ((rlist
= drd_rcm_cpu_rlist_init(rsrcs
, nrsrc
,
480 DRCTL_STATUS_INIT
)) == NULL
) {
481 drd_err("unable to generate resource list");
485 rv
= rcm_request_offline_list(rcm_hdl
, rlist
, 0, &rinfo
);
486 if (rv
== RCM_SUCCESS
) {
487 drd_dbg("RCM success, rinfo=%p", rinfo
);
491 drd_dbg("RCM call failed (%d):", rv
);
494 * Loop through the result of the operation and add
495 * any error messages to the resource structure.
497 while ((tuple
= rcm_info_next(rinfo
, tuple
)) != NULL
) {
499 /* find the resource of interest */
500 rsrcstr
= rcm_info_rsrc(tuple
);
501 rsrc
= cpu_rsrcstr_to_rsrc(rsrcstr
, rsrcs
, nrsrc
);
504 drd_dbg("unable to find resource for %s", rsrcstr
);
508 errstr
= rcm_info_error(tuple
);
511 drd_dbg(" %s: '%s'", rsrcstr
, errstr
);
512 rsrc
->offset
= (uintptr_t)strdup(errstr
);
516 rcm_free_info(rinfo
);
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.
525 for (idx
= 0; rlist
[idx
] != NULL
; idx
++) {
528 rcm_get_rsrcstate(rcm_hdl
, rlist
[idx
], &state
);
530 /* find the resource of interest */
531 rsrc
= cpu_rsrcstr_to_rsrc(rlist
[idx
], rsrcs
, nrsrc
);
534 drd_dbg("unable to find resource for %s", rlist
[idx
]);
538 rsrc
->status
= ((state
== RCM_STATE_OFFLINE
) ?
539 DRCTL_STATUS_ALLOW
: DRCTL_STATUS_DENY
);
542 drd_rcm_cpu_rlist_fini(rlist
);
548 drd_rcm_remove_cpu_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
554 drd_dbg("drd_rcm_remove_cpu_notify...");
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");
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
);
569 drd_rcm_cpu_rlist_fini(rlist
);
575 drd_rcm_restore_cpu_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
585 drd_dbg("drd_rcm_restore_cpu_notify...");
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");
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.
600 /* allocate a zero filled array to ensure NULL terminated list */
601 rlist
= (char **)calloc((nrsrc
+ 1), sizeof (char *));
603 drd_err("calloc failed: %s", strerror(errno
));
608 for (idx
= 0, ridx
= 0; full_rlist
[idx
] != NULL
; idx
++) {
610 rcm_get_rsrcstate(rcm_hdl
, full_rlist
[idx
], &state
);
611 if (state
!= RCM_STATE_ONLINE
) {
612 rlist
[ridx
] = full_rlist
[idx
];
617 /* check if everything got filtered out */
619 drd_dbg(" all CPUs already online, nothing to do");
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
);
631 drd_rcm_cpu_rlist_fini(full_rlist
);
638 drd_rcm_del_cpu_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
640 cpuid_t
*cpus
= NULL
;
642 cpuid_t
*oldcpus
= NULL
;
643 cpuid_t
*newcpus
= NULL
;
646 nvlist_t
*nvl
= NULL
;
651 drd_dbg("drd_rcm_del_cpu_notify...");
653 if ((rsrcs
== NULL
) || (nrsrc
== 0)) {
654 drd_err("del_cpu_notify: cpu list empty");
658 cpus
= (cpuid_t
*)malloc(nrsrc
* sizeof (cpuid_t
));
661 * Filter out the CPUs that could not be unconfigured.
663 for (idx
= 0, cidx
= 0; idx
< nrsrc
; idx
++) {
664 if (rsrcs
[idx
].status
!= DRCTL_STATUS_CONFIG_SUCCESS
)
666 drd_dbg(" cpu[%d] = %d", idx
, rsrcs
[idx
].res_cpu_id
);
667 cpus
[cidx
] = rsrcs
[idx
].res_cpu_id
;
671 drd_dbg(" ncpus = %d", cidx
);
679 /* allocate an nvlist for the RCM call */
680 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0) {
685 * Removed CPU capacity, so newcpus is the current list
686 * of CPUs in the system.
688 if (get_sys_cpuids(&newcpus
, &newncpus
) == -1) {
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.
697 oldcpus
= (cpuid_t
*)calloc(newncpus
+ cidx
, sizeof (cpuid_t
));
698 if (oldcpus
== NULL
) {
702 for (idx
= 0; idx
< newncpus
; idx
++) {
703 if (!is_cpu_in_list(newcpus
[idx
], cpus
, cidx
))
704 oldcpus
[oldncpus
++] = newcpus
[idx
];
707 for (idx
= 0; idx
< cidx
; idx
++) {
708 oldcpus
[oldncpus
++] = cpus
[idx
];
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
);
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
)) {
725 rv
= rcm_notify_capacity_change(rcm_hdl
, RCM_CPU_ALL
, 0, nvl
, &rinfo
);
726 rv
= (rv
== RCM_SUCCESS
) ? 0 : -1;
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.
744 drd_rcm_cpu_rlist_init(drctl_rsrc_t
*rsrcs
, int nrsrc
, int status
)
746 char rbuf
[RCM_CPU_MAX_LEN
];
751 drd_dbg("drd_rcm_cpu_rlist_init...");
753 if ((rsrcs
== NULL
) || (nrsrc
== 0)) {
754 drd_dbg("cpu list is empty");
758 /* allocate a zero filled array to ensure NULL terminated list */
759 rlist
= (char **)calloc((nrsrc
+ 1), sizeof (char *));
761 drd_err("calloc failed: %s", strerror(errno
));
765 for (idx
= 0, ridx
= 0; idx
< nrsrc
; idx
++) {
767 drd_dbg(" checking cpu %d, status=%d, expected status=%d",
768 rsrcs
[idx
].res_cpu_id
, rsrcs
[idx
].status
, status
);
771 * Filter out the CPUs that are not in
772 * the requested state.
774 if (rsrcs
[idx
].status
!= status
)
777 /* generate the resource string */
778 (void) sprintf(rbuf
, "%s%d", RCM_CPU
, rsrcs
[idx
].res_cpu_id
);
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
);
790 /* cleanup if the list is empty */
795 drd_dbg("final rlist:");
796 dump_cpu_rlist(rlist
);
802 drd_rcm_cpu_rlist_fini(char **rlist
)
806 drd_dbg("drd_rcm_cpu_rlist_fini...");
808 dump_cpu_rlist(rlist
);
810 for (idx
= 0; rlist
[idx
] != NULL
; idx
++) {
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.
823 cpu_rsrcstr_to_cpuid(const char *rsrc
)
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.
833 cpuid_off
= strrchr(rsrc
, 'u');
836 cpuid
= atoi(cpuid_off
);
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
847 static drctl_rsrc_t
*
848 cpu_rsrcstr_to_rsrc(const char *rsrcstr
, drctl_rsrc_t
*rsrcs
, int nrsrc
)
853 cpuid
= cpu_rsrcstr_to_cpuid(rsrcstr
);
855 for (idx
= 0; idx
< nrsrc
; idx
++) {
856 if (rsrcs
[idx
].res_cpu_id
== cpuid
)
857 return (&rsrcs
[idx
]);
864 get_sys_cpuids(cpuid_t
**cpuids
, int *ncpuids
)
869 kstat_ctl_t
*kc
= NULL
;
872 drd_dbg("get_sys_cpuids...");
874 if ((maxncpu
= sysconf(_SC_NPROCESSORS_MAX
)) == -1)
877 if ((kc
= kstat_open()) == NULL
)
880 if ((cp
= (cpuid_t
*)calloc(maxncpu
, sizeof (cpuid_t
))) == NULL
) {
881 (void) kstat_close(kc
);
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
;
890 dump_cpu_list("syscpus: ", cp
, ncpu
);
892 (void) kstat_close(kc
);
901 is_cpu_in_list(cpuid_t cpuid
, cpuid_t
*list
, int len
)
908 for (idx
= 0; idx
< len
; idx
++) {
909 if (list
[idx
] == cpuid
)
916 #define CPUIDS_PER_LINE 16
917 #define LINEWIDTH (2 * (CPUIDS_PER_LINE * 4))
920 dump_cpu_list(char *prefix
, cpuid_t
*cpuids
, int ncpuids
)
922 char line
[LINEWIDTH
];
926 /* return if not debugging */
930 /* print just the prefix if CPU list is empty */
933 drd_dbg("%s", prefix
);
937 for (i
= 0; i
< ncpuids
; i
+= CPUIDS_PER_LINE
) {
939 bzero(line
, LINEWIDTH
);
942 /* start with the prefix */
943 (void) sprintf(curr
, "%s", (prefix
) ? prefix
: "");
944 curr
= line
+ strlen(line
);
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
);
957 dump_cpu_rsrc_list(char *prefix
, drctl_rsrc_t
*rsrcs
, int nrsrc
)
962 /* just return if not debugging */
967 drd_dbg("%s", prefix
);
969 for (idx
= 0; idx
< nrsrc
; idx
++) {
971 /* get a pointer to the error string */
972 errstr
= (char *)(uintptr_t)rsrcs
[idx
].offset
;
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
: "");
981 dump_cpu_rlist(char **rlist
)
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"
996 /* just return if not debugging */
1000 if (rlist
== NULL
) {
1001 drd_dbg(" empty rlist");
1005 for (idx
= 0; rlist
[idx
] != NULL
; idx
++) {
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
]);
1014 drd_rcm_io_config_request(drctl_rsrc_t
*rsrc
, int nrsrc
)
1016 drd_dbg("drd_rcm_io_config_request...");
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
;
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.
1031 rsrc
->status
= DRCTL_STATUS_ALLOW
;
1038 drd_rcm_io_config_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
1040 drd_dbg("drd_rcm_io_config_notify...");
1043 drd_dbg("drd_rcm_cpu_config_notify: only 1 resource "
1044 "allowed for I/O requests, passed %d resources\n", nrsrc
);
1054 drd_rcm_io_unconfig_request(drctl_rsrc_t
*rsrc
, int nrsrc
)
1057 char *dev
= rsrc
->res_dev_path
;
1058 rcm_info_t
*rinfo
= NULL
;
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
;
1068 if ((rv
= rcm_request_offline(rcm_hdl
, dev
, 0, &rinfo
)) == RCM_SUCCESS
)
1069 rsrc
->status
= DRCTL_STATUS_ALLOW
;
1071 rcm_notify_online(rcm_hdl
, dev
, 0, NULL
);
1072 rsrc
->status
= DRCTL_STATUS_DENY
;
1073 rsrc
->offset
= (uintptr_t)rcm_info_table(rinfo
);
1077 rcm_free_info(rinfo
);
1078 drd_dbg("drd_rcm_io_unconfig_request(%s) = %d", dev
, rv
);
1084 drd_rcm_io_unconfig_notify(drctl_rsrc_t
*rsrc
, int nrsrc
)
1086 drd_dbg("drd_rcm_io_unconfig_notify...");
1089 drd_dbg("drd_io_cpu_unconfig_notify: only 1 resource "
1090 "allowed for I/O requests, passed %d resources\n", nrsrc
);
1095 return (rcm_notify_remove(rcm_hdl
, rsrc
->res_dev_path
, 0, NULL
));
1098 #define MAX_FORMAT 80
1101 * Convert rcm_info_t data into a printable table.
1104 rcm_info_table(rcm_info_t
*rinfo
)
1111 size_t table_size
= 0;
1113 rcm_info_tuple_t
*tuple
= NULL
;
1117 static char format
[MAX_FORMAT
];
1118 const char *infostr
;
1120 /* Protect against invalid arguments */
1124 /* Set localized table header strings */
1125 rsrc
= dgettext(TEXT_DOMAIN
, "Resource");
1126 info
= dgettext(TEXT_DOMAIN
, "Information");
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
) {
1132 if ((w
= strlen(rcm_info_rsrc(tuple
))) > w_rsrc
)
1134 if ((w
= strlen(infostr
)) > w_info
)
1139 /* If nothing was sized up above, stop early */
1143 /* Adjust column widths for column headings */
1144 if ((w
= strlen(rsrc
)) > w_rsrc
)
1146 else if ((w_rsrc
- w
) % 2)
1148 if ((w
= strlen(info
)) > w_info
)
1150 else if ((w_info
- w
) % 2)
1154 * Compute the total line width of each line,
1155 * accounting for intercolumn spacing.
1157 width
= w_info
+ w_rsrc
+ 4;
1159 /* Allocate space for the table */
1160 table_size
= (2 + tuples
) * (width
+ 1) + 2;
1162 /* zero fill for the strcat() call below */
1163 table
= calloc(table_size
, sizeof (char));
1167 /* Place a table header into the string */
1169 /* The resource header */
1170 (void) strcat(table
, "\n");
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
, " ");
1178 /* The information header */
1179 (void) strcat(table
, " ");
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
, "-");
1194 /* Construct the format string */
1195 (void) snprintf(format
, MAX_FORMAT
, "%%-%ds %%-%ds",
1196 (int)w_rsrc
, (int)w_info
);
1198 /* Add the tuples to the table string */
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
),
1208 drd_dbg("rcm_info_table: %s\n", table
);
1214 dump_mem_rsrc_list(char *prefix
, drctl_rsrc_t
*rsrcs
, int nrsrc
)
1219 /* just return if not debugging */
1224 drd_dbg("%s", prefix
);
1226 for (idx
= 0; idx
< nrsrc
; idx
++) {
1228 /* get a pointer to the error string */
1229 errstr
= (char *)(uintptr_t)rsrcs
[idx
].offset
;
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
: "");
1239 drd_rcm_mem_op(rcm_op_t op
, uint64_t change
)
1245 nvlist_t
*nvl
= NULL
;
1248 /* allocate an nvlist for the RCM call */
1249 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0)
1252 if ((pgsize
= sysconf(_SC_PAGE_SIZE
)) == -1 ||
1253 (newpages
= sysconf(_SC_PHYS_PAGES
)) == -1)
1257 * If this is a notify add, the capacity change
1258 * was positive and the current page count reflects
1259 * the new capacity level.
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.
1265 assert(change
% pgsize
== 0);
1267 oldpages
= newpages
- (long)(change
/ pgsize
);
1269 assert(newpages
>= change
/ pgsize
);
1270 oldpages
= newpages
;
1271 newpages
= oldpages
+ (long)(change
/ pgsize
);
1274 drd_dbg("oldpages=%lld newpages=%lld delta=%lld",
1275 oldpages
, newpages
, newpages
- oldpages
);
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) {
1285 rv
= (*op
)(rcm_hdl
, RCM_MEM_ALL
, 0, nvl
, &rinfo
);
1286 rv
= (rv
== RCM_SUCCESS
) ? 0 : -1;
1295 * RCM clients can interpose only on removal of resources.
1298 drd_rcm_mem_config_request(drctl_rsrc_t
*rsrcs
, int nrsrc
)
1302 drd_dbg("drd_rcm_mem_config_request...");
1304 if ((rsrcs
== NULL
) || (nrsrc
== 0))
1306 dump_mem_rsrc_list(NULL
, rsrcs
, nrsrc
);
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.
1313 for (idx
= 0; idx
< nrsrc
; idx
++)
1314 rsrcs
[idx
].status
= DRCTL_STATUS_ALLOW
;
1316 dump_mem_rsrc_list("returning:", rsrcs
, nrsrc
);
1322 drd_rcm_mem_config_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
1326 uint64_t change
= 0;
1328 drd_dbg("drd_rcm_mem_config_notify...");
1330 if ((rsrcs
== NULL
) || (nrsrc
== 0)) {
1331 drd_err("mem_config_notify: mem list empty");
1334 dump_mem_rsrc_list(NULL
, rsrcs
, nrsrc
);
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
);
1343 rv
= drd_rcm_mem_op(rcm_notify_capacity_change
, change
);
1349 drd_rcm_mem_unconfig_request(drctl_rsrc_t
*rsrcs
, int nrsrc
)
1353 uint64_t change
= 0;
1355 drd_dbg("drd_rcm_del_mem_request...");
1357 if ((rsrcs
== NULL
) || (nrsrc
== 0)) {
1358 drd_err("mem_unconfig_request: mem list empty");
1361 dump_mem_rsrc_list(NULL
, rsrcs
, nrsrc
);
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
;
1369 rv
= drd_rcm_mem_op(rcm_request_capacity_change
, -change
);
1371 if (rv
!= RCM_SUCCESS
) {
1372 drd_dbg("RCM call failed: %d", rv
);
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
1379 for (idx
= 0; idx
< nrsrc
; idx
++) {
1380 rsrcs
[idx
].status
= DRCTL_STATUS_DENY
;
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");
1388 for (idx
= 0; idx
< nrsrc
; idx
++)
1389 rsrcs
[idx
].status
= DRCTL_STATUS_ALLOW
;
1395 dump_mem_rsrc_list("returning:", rsrcs
, nrsrc
);
1400 drd_rcm_mem_unconfig_notify(drctl_rsrc_t
*rsrcs
, int nrsrc
)
1404 uint64_t change
= 0;
1406 drd_dbg("drd_rcm_mem_unconfig_notify...");
1408 if ((rsrcs
== NULL
) || (nrsrc
== 0)) {
1409 drd_err("unconfig_mem_notify: mem list empty");
1412 dump_mem_rsrc_list(NULL
, rsrcs
, nrsrc
);
1415 * Filter out the memory that was configured.
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.
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
);
1430 rv
= drd_rcm_mem_op(rcm_notify_capacity_change
, change
);