dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / cfgadm_plugins / sbd / common / ap_rcm.c
blob9a39d0d617f6fffe86b34d03adf0fab60eb2c514
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <macros.h>
33 #include <errno.h>
34 #include <kstat.h>
35 #include <sys/kmem.h>
36 #include <dlfcn.h>
37 #include <libdevinfo.h>
38 #include <librcm.h>
39 #include <libintl.h>
40 #define CFGA_PLUGIN_LIB
41 #include <config_admin.h>
42 #include <sys/sbd_ioctl.h>
43 #include "ap.h"
45 typedef int32_t cpuid_t;
47 typedef struct {
48 int valid;
49 cfga_stat_t ostate;
50 int ncap;
51 union {
52 long npages;
53 cpuid_t cpuid[SBD_MAX_CORES_PER_CMP];
54 } type;
55 } cap_info_t;
57 typedef struct {
58 int firstcm; /* first component to operate on */
59 int lastcm; /* last component to operate on */
60 void *lib;
61 char **rlist;
62 cap_info_t *capinfo;
63 int ncpus; /* # of CPUs in cpuids list */
64 cpuid_t *cpuids; /* List of cpuids */
65 int capcpus; /* # of CPUs - tracking capacity */
66 int cappages; /* # of memory pages - tracking capacity */
67 rcm_handle_t *hd;
68 rcm_info_t *rinfo;
69 rcm_info_tuple_t *infot;
70 int (*alloc_handle)(char *, uint_t, void *, rcm_handle_t **);
71 void (*free_handle)(rcm_handle_t *);
72 int (*get_info)(rcm_handle_t *, char *, uint_t, rcm_info_t **);
73 void (*free_info)(rcm_info_t *);
74 rcm_info_tuple_t *(*info_next)(rcm_info_t *, rcm_info_tuple_t *);
75 int (*info_state)(rcm_info_tuple_t *);
76 pid_t (*info_pid)(rcm_info_tuple_t *);
77 const char *(*info_error)(rcm_info_tuple_t *);
78 const char *(*info_info)(rcm_info_tuple_t *);
79 const char *(*info_rsrc)(rcm_info_tuple_t *);
80 int (*request_offline_list)(rcm_handle_t *, char **, uint_t,
81 rcm_info_t **);
82 int (*notify_online_list)(rcm_handle_t *, char **, uint_t,
83 rcm_info_t **);
84 int (*request_suspend)(rcm_handle_t *, char *, uint_t, timespec_t *,
85 rcm_info_t **);
86 int (*notify_resume)(rcm_handle_t *, char *, uint_t, rcm_info_t **);
87 int (*notify_remove_list)(rcm_handle_t *, char **, uint_t,
88 rcm_info_t **);
89 int (*request_capacity_change)(rcm_handle_t *, char *, uint_t,
90 nvlist_t *, rcm_info_t **);
91 int (*notify_capacity_change)(rcm_handle_t *, char *, uint_t,
92 nvlist_t *, rcm_info_t **);
93 } rcmd_t;
95 static char *
96 ap_rcm_ops[] = {
97 "rcm_alloc_handle",
98 "rcm_free_handle",
99 "rcm_get_info",
100 "rcm_free_info",
101 "rcm_info_next",
102 "rcm_info_state",
103 "rcm_info_pid",
104 "rcm_info_error",
105 "rcm_info_info",
106 "rcm_info_rsrc",
107 "rcm_request_offline_list",
108 "rcm_notify_online_list",
109 "rcm_request_suspend",
110 "rcm_notify_resume",
111 "rcm_notify_remove_list",
112 "rcm_request_capacity_change",
113 "rcm_notify_capacity_change",
114 NULL
117 #define ALLOC_HANDLE 0
118 #define FREE_HANDLE 1
119 #define GET_INFO 2
120 #define FREE_INFO 3
121 #define INFO_TUPLE_NEXT 4
122 #define INFO_TUPLE_STATE 5
123 #define INFO_TUPLE_ID 6
124 #define INFO_TUPLE_ERROR 7
125 #define INFO_TUPLE_INFO 8
126 #define INFO_TUPLE_RSRC 9
127 #define REQUEST_OFFLINE 10
128 #define NOTIFY_ONLINE 11
129 #define REQUEST_SUSPEND 12
130 #define NOTIFY_RESUME 13
131 #define NOTIFY_REMOVE 14
132 #define REQUEST_CAP_CHANGE 15
133 #define NOTIFY_CAP_CHANGE 16
136 * There is no consumer for SUNW_OS. This is defined here
137 * for generic OS quiescence.
139 #define OS "SUNW_OS" /* XXX */
141 /* Max width of an RCM formatted message line */
142 #define RCM_MAX_FORMAT 80
144 #ifdef __sparcv9
145 #define RCMLIB "/lib/sparcv9/librcm.so";
146 #elif defined(__amd64)
147 #define RCMLIB "/lib/amd64/librcm.so";
148 #else
149 #define RCMLIB "/lib/librcm.so";
150 #endif
152 static cfga_err_t
153 ap_capinfo(apd_t *a, int firstcm, int lastcm, cap_info_t **capinfo)
155 int cm;
156 int ncm;
157 void *cap;
158 int *ncap;
159 cfga_stat_t *os;
160 cap_info_t *cinfo, *cp;
162 DBG("ap_capinfo(%p)\n", (void *)a);
164 if (capinfo == NULL) {
165 ap_err(a, ERR_PLUGIN, "null capinfo");
166 return (CFGA_LIB_ERROR);
170 * Assume there are components with valid capacity
171 * information and allocate space for them. If there
172 * are none at the end, free the allocated space.
174 ncm = lastcm - firstcm + 1;
176 cinfo = (cap_info_t *)calloc(ncm, sizeof (cap_info_t));
177 if (cinfo == NULL) {
178 ap_err(a, ERR_NOMEM);
179 return (CFGA_LIB_ERROR);
182 *capinfo = NULL;
183 ncm = 0;
184 for (cp = cinfo, cm = firstcm; cm <= lastcm; cm++, cp++) {
185 os = &cp->ostate;
186 ncap = &cp->ncap;
188 switch (ap_cm_type(a, cm)) {
189 case AP_CPU:
190 case AP_CMP:
191 cap = (void *)(cp->type.cpuid);
192 break;
193 case AP_MEM:
194 cap = (void *)&(cp->type.npages);
195 break;
196 default:
197 continue;
200 * Remember which components have valid
201 * capacity information.
203 if (ap_cm_capacity(a, cm, cap, ncap, os)) {
204 cp->valid = 1;
205 ncm++;
209 if (ncm == 0)
210 free(cinfo);
211 else
212 *capinfo = cinfo;
214 return (CFGA_OK);
217 static int
218 getsyscpuids(int *ncpuids, cpuid_t **cpuids)
220 int ncpu;
221 int maxncpu;
222 kstat_t *ksp;
223 kstat_ctl_t *kc = NULL;
224 cpuid_t *cp;
226 DBG("getsyscpuids\n");
228 if ((maxncpu = sysconf(_SC_NPROCESSORS_MAX)) == -1 ||
229 (kc = kstat_open()) == NULL ||
230 (cp = (cpuid_t *)calloc(maxncpu, sizeof (cpuid_t))) == NULL) {
231 /* if calloc failed, clean up kstats */
232 if (kc != NULL) {
233 (void) kstat_close(kc);
235 return (-1);
238 DBG("syscpuids: ");
239 for (ncpu = 0, ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
240 if (strcmp(ksp->ks_module, "cpu_info") == 0) {
241 cp[ncpu++] = ksp->ks_instance;
242 DBG("%d ", ksp->ks_instance);
245 DBG("\n");
247 (void) kstat_close(kc);
248 *cpuids = cp;
249 *ncpuids = ncpu;
250 return (0);
253 cfga_err_t
254 ap_rcm_init(apd_t *a)
256 int i;
257 char *err;
258 char *rcmlib;
259 void *sym;
260 void *lib;
261 char **op;
262 rcmd_t *rcm;
263 cfga_err_t rc;
264 struct stat buf;
266 DBG("ap_rcm_init(%p)\n", (void *)a);
269 * If the initial command is status, or the RCM feature is not
270 * available, or the RCM interface has already been initialized,
271 * just return.
274 if ((a->statonly != 0) || (a->norcm != 0) ||
275 ((rcm = (rcmd_t *)a->rcm) != NULL)) {
276 return (CFGA_OK);
279 rcmlib = RCMLIB;
280 rc = CFGA_LIB_ERROR;
282 DBG("Looking for %s\n", rcmlib);
284 * If the library is not present, there is nothing more
285 * to do. The RCM offline/suspend steps become no-ops
286 * in that case.
288 if (stat(rcmlib, &buf) == -1) {
289 if (errno == ENOENT) {
290 a->norcm++;
291 ap_msg(a, MSG_NORCM);
292 return (CFGA_OK);
293 } else {
294 ap_err(a, ERR_STAT, rcmlib);
295 return (rc);
298 DBG("%s found\n", rcmlib);
300 if ((a->rcm = calloc(1, sizeof (rcmd_t))) == NULL) {
301 ap_err(a, ERR_NOMEM);
302 return (rc);
305 rcm = (rcmd_t *)a->rcm;
307 if ((lib = dlopen(rcmlib, RTLD_NOW)) == NULL) {
308 if ((err = dlerror()) != NULL)
309 err = strdup(err);
310 ap_err(a, ERR_LIB_OPEN, rcmlib, err);
311 free(err);
312 return (rc);
315 rcm->lib = lib;
317 for (i = 0, op = ap_rcm_ops; *op != NULL; op++, i++) {
318 if ((sym = dlsym(lib, *op)) == NULL) {
319 ap_err(a, ERR_LIB_SYM, rcmlib, *op);
320 return (rc);
322 switch (i) {
323 case ALLOC_HANDLE:
324 rcm->alloc_handle = (int(*)
325 (char *, uint_t, void *, rcm_handle_t **))sym;
326 break;
327 case FREE_HANDLE:
328 rcm->free_handle = (void (*)(rcm_handle_t *))sym;
329 break;
330 case GET_INFO:
331 rcm->get_info = (int (*)
332 (rcm_handle_t *, char *, uint_t, rcm_info_t **))sym;
333 break;
334 case FREE_INFO:
335 rcm->free_info = (void (*)(rcm_info_t *))sym;
336 break;
337 case INFO_TUPLE_NEXT:
338 rcm->info_next = (rcm_info_tuple_t *(*)
339 (rcm_info_t *, rcm_info_tuple_t *))sym;
340 break;
341 case INFO_TUPLE_STATE:
342 rcm->info_state = (int (*)(rcm_info_tuple_t *))sym;
343 break;
344 case INFO_TUPLE_ID:
345 rcm->info_pid = (pid_t (*)(rcm_info_tuple_t *))sym;
346 break;
347 case INFO_TUPLE_ERROR:
348 rcm->info_error = (const char *(*)
349 (rcm_info_tuple_t *))sym;
350 break;
351 case INFO_TUPLE_INFO:
352 rcm->info_info = (const char *(*)
353 (rcm_info_tuple_t *))sym;
354 break;
355 case INFO_TUPLE_RSRC:
356 rcm->info_rsrc = (const char *(*)
357 (rcm_info_tuple_t *))sym;
358 break;
359 case REQUEST_OFFLINE:
360 rcm->request_offline_list = (int (*)
361 (rcm_handle_t *, char **, uint_t,
362 rcm_info_t **))sym;
363 break;
364 case NOTIFY_ONLINE:
365 rcm->notify_online_list = (int (*)
366 (rcm_handle_t *, char **, uint_t,
367 rcm_info_t **))sym;
368 break;
369 case REQUEST_SUSPEND:
370 rcm->request_suspend = (int (*)
371 (rcm_handle_t *, char *, uint_t,
372 timespec_t *, rcm_info_t **))sym;
373 break;
374 case NOTIFY_RESUME:
375 rcm->notify_resume = (int (*)
376 (rcm_handle_t *, char *, uint_t,
377 rcm_info_t **))sym;
378 break;
379 case NOTIFY_REMOVE:
380 rcm->notify_remove_list = (int (*)
381 (rcm_handle_t *, char **, uint_t,
382 rcm_info_t **))sym;
383 break;
384 case REQUEST_CAP_CHANGE:
385 rcm->request_capacity_change = (int (*)
386 (rcm_handle_t *, char *, uint_t,
387 nvlist_t *, rcm_info_t **))sym;
388 break;
389 case NOTIFY_CAP_CHANGE:
390 rcm->notify_capacity_change = (int (*)
391 (rcm_handle_t *, char *, uint_t,
392 nvlist_t *, rcm_info_t **))sym;
393 break;
394 default:
395 break;
399 if (rcm->alloc_handle == NULL ||
400 (*rcm->alloc_handle)(NULL, RCM_NOPID, NULL, &rcm->hd)
401 != RCM_SUCCESS) {
402 ap_err(a, ERR_RCM_HANDLE);
403 return (CFGA_LIB_ERROR);
407 * Offlining/onlining a board means offlining/onlining
408 * all components on the board. When operating on a
409 * single component no component sequence number is
410 * needed since the default is the current (target)
411 * component.
413 if (a->tgt == AP_BOARD) {
414 rcm->firstcm = 0;
415 rcm->lastcm = a->ncm - 1;
416 } else {
417 rcm->firstcm = CM_DFLT;
418 rcm->lastcm = CM_DFLT;
421 if (rcm->cpuids == NULL) {
422 int cm;
423 int ncpu;
426 * Allocate space for the cpu capacity change info.
427 * Not every cpu may be relevant to the capacity
428 * request, but allocating for the maximum makes
429 * it easier, and the space is insignifcant.
431 for (ncpu = 0, cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
433 ap_target_t type = ap_cm_type(a, cm);
435 if ((type == AP_CPU) || (type == AP_CMP)) {
436 ncpu += ap_cm_ncap(a, cm);
440 rcm->ncpus = ncpu;
441 if ((rcm->cpuids = (cpuid_t *)calloc(ncpu, sizeof (cpuid_t)))
442 == NULL) {
443 ap_err(a, ERR_NOMEM);
444 return (CFGA_LIB_ERROR);
449 * Remember initial capacity information.
450 * This information is based on the initial
451 * state of the ap_id, i.e. before any
452 * state change change operations were
453 * executed. We will later get the
454 * current capacity information in order
455 * to figure out exactly what has changed
456 * as the result of the executed command
457 * sequence.
459 rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &rcm->capinfo);
461 rcm->capcpus = sysconf(_SC_NPROCESSORS_CONF);
462 rcm->cappages = sysconf(_SC_PHYS_PAGES);
464 return (rc);
467 void
468 ap_rcm_fini(apd_t *a)
470 rcmd_t *rcm;
471 char **rp;
473 DBG("ap_rcm_fini(%p)\n", (void *)a);
475 if ((rcm = (rcmd_t *)a->rcm) == NULL)
476 return;
478 if (rcm->hd)
479 (*rcm->free_handle)(rcm->hd);
481 (void) dlclose(rcm->lib);
484 * Free all the names in the resource list, followed
485 * by the resource list itself.
487 if (rcm->rlist)
488 for (rp = rcm->rlist; *rp; rp++)
489 s_free(*rp);
490 s_free(rcm->rlist);
491 s_free(rcm->cpuids);
492 s_free(rcm->capinfo);
493 s_free(a->rcm);
496 static cfga_err_t
497 ap_rcm_rlist(apd_t *a, int firstcm, int lastcm, char ***rlist, int cmd)
499 int n;
500 int cm;
501 int ncap;
502 char *path;
503 char *cpuname;
504 char **rp;
506 DBG("ap_rcm_rlist(%p)\n", (void *)a);
509 * Allocate space for the maximum number of components
510 * that can be affected by this operation.
512 for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
513 ncap += ap_cm_ncap(a, cm);
516 DBG("ncap=%d\n", ncap);
518 if ((rp = (char **)calloc(ncap + 1, sizeof (char *))) == NULL) {
519 ap_err(a, ERR_NOMEM);
520 return (CFGA_LIB_ERROR);
523 n = 12; /* SUNW_cpu/cpuCCC */
524 /* <--- 12 ---> */
525 cpuname = "SUNW_cpu/cpuCCC";
527 * Set the RCM resource name for each component:
529 * io: <device-path>
530 * cpu: SUNW_cpu/cpu<cpuid>
533 for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
534 switch (ap_cm_type(a, cm)) {
535 case AP_CPU:
536 case AP_CMP: {
537 int i;
538 int len;
539 cap_info_t cap;
540 cfga_stat_t os;
541 cpuid_t *cpuid;
542 int *nc;
543 cap_info_t *prevcap;
544 rcmd_t *rcm;
545 int allow_op;
546 int capindex;
548 cpuid = cap.type.cpuid;
549 nc = &cap.ncap;
552 * See if the request target is a single
553 * (default) component
555 capindex = (cm == CM_DFLT) ? 0 : cm;
557 /* Get the previous capacity info */
558 rcm = (rcmd_t *)a->rcm;
559 prevcap = rcm->capinfo;
561 if (!ap_cm_capacity(a, cm, cpuid, nc, &os)) {
562 break;
565 len = (strlen(cpuname) - n) + 1;
568 * For CMD_RCM_OFFLINE and REMOVE, add the CPU to the
569 * list if it is currently configured. For
570 * CMD_RCM_ONLINE, do so only if the state has changed
571 * to CFGA_STAT_CONFIGURED.
573 allow_op = 0;
574 if ((cmd == CMD_RCM_OFFLINE) ||
575 (cmd == CMD_RCM_REMOVE)) {
576 if (os == CFGA_STAT_CONFIGURED)
577 allow_op = 1;
578 } else {
579 if ((os == CFGA_STAT_CONFIGURED) &&
580 ((prevcap == NULL) ||
581 (prevcap[capindex].ostate != os)))
582 allow_op = 1;
585 if (allow_op) {
586 for (i = 0; i < *nc; i++) {
587 if ((path = strdup(cpuname)) == NULL) {
588 ap_err(a, ERR_NOMEM);
589 return (CFGA_LIB_ERROR);
591 (void) snprintf(&path[n], len, "%d",
592 cpuid[i]);
594 DBG("rp[%d]=%s\n", ncap, path);
595 rp[ncap++] = path;
598 break;
600 case AP_IO:
601 if ((path = ap_cm_devpath(a, cm)) != NULL) {
602 DBG("rp[%d]=%s\n", ncap, path);
603 rp[ncap++] = path;
605 break;
606 case AP_MEM:
608 * Nothing to do for AP_MEM since only capacity
609 * change notifications apply to SUNW_memory
611 default:
612 break;
616 rp[ncap] = NULL;
617 if (rlist)
618 *rlist = rp;
619 return (CFGA_OK);
623 * Returns 1 if the cpu ID 'cpuid' is in the list of CPU IDs
624 * 'list' of length 'length'. Returns 0 otherwise.
626 static int
627 is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int length)
629 int i;
631 DBG("is_cpu_in_list\n");
633 if (list == NULL)
634 return (0);
636 for (i = 0; i < length; i++) {
637 if (list[i] == cpuid)
638 return (1);
640 return (0);
643 static int
644 ap_rcm_cap_cpu(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
645 rcm_info_t **rinfo, int cmd, int change)
647 int i;
648 int rv = RCM_FAILURE;
649 int ncpuids;
650 int oldncpuids;
651 int newncpuids;
652 char buf[32];
653 const char *fmt;
654 size_t size;
655 nvlist_t *nvl = NULL;
656 cpuid_t *cpuids = NULL;
657 cpuid_t *oldcpuids = NULL;
658 cpuid_t *newcpuids = NULL;
660 DBG("ap_rcm_cap_cpu(%p)\n", (void *)a);
663 * Get the current number of configured cpus.
665 if (getsyscpuids(&ncpuids, &cpuids) == -1)
666 return (rv);
667 else if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
668 free(cpuids);
669 goto done;
672 if (change == 1)
673 fmt = "(%d cpu)";
674 else
675 fmt = "(%d cpus)";
677 size = sizeof (cpuid_t);
679 if (cmd == CMD_RCM_CAP_DEL) {
681 * A delete request. rcm->cpuids represents the
682 * cpus that will be unconfigured. The current
683 * set of cpus, before the unconfigure operation,
684 * are the old CPUs. The new CPUs are those
685 * that would remain.
687 oldncpuids = ncpuids;
688 oldcpuids = cpuids;
691 * Fill newcpuids with the CPU IDs in the cpuids array,
692 * but not in rcm->cpuids.
694 newcpuids = (cpuid_t *)calloc(ncpuids, size);
695 if (newcpuids == NULL)
696 goto done;
698 newncpuids = 0;
699 for (i = 0; i < ncpuids; i++) {
700 if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
701 newcpuids[newncpuids++] = cpuids[i];
703 } else if (cmd == CMD_RCM_CAP_NOTIFY) {
705 * An unconfigure capacity change notification. This
706 * notification is sent after a DR unconfigure, whether
707 * or not the DR was successful. rcm->cpuids represents
708 * the CPUs that have been unconfigured.
711 /* New CPU IDs are the CPUs configured right now. */
712 newncpuids = ncpuids;
713 newcpuids = cpuids;
716 * Old CPU IDs are the CPUs configured right now
717 * in addition to those that have been unconfigured.
718 * We build the old CPU ID list by concatenating
719 * cpuids and rcm->cpuids.
721 oldcpuids = (cpuid_t *)calloc(ncpuids + change, size);
722 if (oldcpuids == NULL)
723 goto done;
725 oldncpuids = 0;
726 for (i = 0; i < ncpuids; i++) {
727 if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
728 oldcpuids[oldncpuids++] = cpuids[i];
730 for (i = 0; i < change; i++)
731 oldcpuids[oldncpuids++] = rcm->cpuids[i];
732 } else {
733 DBG("ap_rcm_cap_cpu: CPU capacity, old = %d, new = %d \n",
734 rcm->capcpus, ncpuids);
735 if (rcm->capcpus == ncpuids) {
736 /* No real change in CPU capacity */
737 rv = RCM_SUCCESS;
738 goto done;
742 * An add notification. rcm->cpuids represents the
743 * cpus that have been configured. The current
744 * set of cpus, after the configure operation,
745 * are the new CPU IDs.
747 newncpuids = ncpuids;
748 newcpuids = cpuids;
751 * Fill oldcpuids with the CPU IDs in the cpuids array,
752 * but not in rcm->cpuids.
754 oldcpuids = (cpuid_t *)calloc(ncpuids, size);
755 if (oldcpuids == NULL)
756 goto done;
758 oldncpuids = 0;
759 for (i = 0; i < ncpuids; i++) {
760 if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
761 oldcpuids[oldncpuids++] = cpuids[i];
765 DBG("oldcpuids: ");
766 for (i = 0; i < oldncpuids; i++)
767 DBG("%d ", oldcpuids[i]);
768 DBG("\n");
769 DBG("change : ");
770 for (i = 0; i < change; i++)
771 DBG("%d ", rcm->cpuids[i]);
772 DBG("\n");
773 DBG("newcpuids: ");
774 for (i = 0; i < newncpuids; i++)
775 DBG("%d ", newcpuids[i]);
776 DBG("\n");
778 if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
779 nvlist_add_int32(nvl, "old_total", oldncpuids) != 0 ||
780 nvlist_add_int32(nvl, "new_total", newncpuids) != 0 ||
781 nvlist_add_int32_array(nvl, "old_cpu_list", oldcpuids,
782 oldncpuids) != 0 ||
783 nvlist_add_int32_array(nvl, "new_cpu_list", newcpuids,
784 newncpuids) != 0)
785 goto done;
787 (void) snprintf(buf, sizeof (buf), fmt, change);
788 ap_msg(a, MSG_ISSUE, cmd, buf);
790 if (cmd == CMD_RCM_CAP_DEL) {
791 rv = (*rcm->request_capacity_change)(hd, "SUNW_cpu",
792 flags, nvl, rinfo);
793 } else {
794 rv = (*rcm->notify_capacity_change)(hd, "SUNW_cpu",
795 flags & ~RCM_FORCE, nvl, rinfo);
798 done:
799 nvlist_free(nvl);
800 s_free(oldcpuids);
801 s_free(newcpuids);
802 return (rv);
805 static int
806 ap_rcm_cap_mem(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
807 rcm_info_t **rinfo, int cmd, long change)
809 int rv;
810 int pgsize;
811 long oldpages;
812 long newpages;
813 long currpages;
814 char buf[32];
815 nvlist_t *nvl;
817 DBG("ap_rcm_cap_mem(%p)\n", (void *)a);
820 * Get the current amount of configured memory.
822 if ((pgsize = sysconf(_SC_PAGE_SIZE)) == -1 ||
823 (currpages = sysconf(_SC_PHYS_PAGES)) == -1 ||
824 nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) > 0)
825 return (RCM_FAILURE);
828 * If this is a (delete) request, change represents
829 * the amount of capacity that will be deleted from the
830 * system. If this is an (add) notification, change
831 * represents the amount of capacity that has already
832 * been added to the system.
834 if (cmd == CMD_RCM_CAP_DEL) {
835 oldpages = currpages;
836 newpages = currpages - change;
837 } else if (cmd == CMD_RCM_CAP_NOTIFY) {
838 newpages = currpages;
839 oldpages = rcm->cappages;
840 } else {
841 if (rcm->cappages == currpages) {
842 /* No real change in memory capacity */
843 DBG("ap_rcm_cap_mem: no change in capacity.\n");
844 nvlist_free(nvl);
845 return (RCM_SUCCESS);
848 oldpages = currpages - change;
849 newpages = currpages;
852 DBG("ap_rcm_cap_mem: Memory capacity, old = %ld, new = %ld\n",
853 oldpages, newpages);
855 if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
856 nvlist_add_int32(nvl, "page_size", pgsize) != 0 ||
857 nvlist_add_int32(nvl, "old_pages", oldpages) != 0 ||
858 nvlist_add_int32(nvl, "new_pages", newpages) != 0) {
859 nvlist_free(nvl);
860 return (RCM_FAILURE);
863 (void) snprintf(buf, sizeof (buf), "(%ld pages)", change);
864 ap_msg(a, MSG_ISSUE, cmd, buf);
866 if (cmd == CMD_RCM_CAP_DEL) {
867 rv = (*rcm->request_capacity_change)(hd, "SUNW_memory",
868 flags, nvl, rinfo);
869 } else {
870 rv = (*rcm->notify_capacity_change)(hd, "SUNW_memory",
871 flags & ~RCM_FORCE, nvl, rinfo);
874 nvlist_free(nvl);
876 return (rv);
879 static cfga_err_t
880 ap_rcm_request_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
881 int *rv, uint_t flags, rcm_info_t **rinfo)
883 int cm;
884 int ncpus;
885 long npages;
886 cap_info_t *capinfo;
887 ap_target_t type;
889 DBG("ap_rcm_request_cap(%p)\n", (void *)a);
891 if ((capinfo = rcm->capinfo) == NULL) {
892 ap_err(a, ERR_PLUGIN, "null capinfo");
893 return (CFGA_LIB_ERROR);
896 ncpus = npages = 0;
898 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
899 int i, j;
902 * See if the request target is a single
903 * (default) component
905 i = (cm == CM_DFLT) ? 0 : cm;
908 * We are interested only in those components
909 * in the configured state since they represent
910 * available capacity.
912 type = ap_cm_type(a, cm);
913 if (capinfo[i].valid == 0 ||
914 capinfo[i].ostate != CFGA_STAT_CONFIGURED)
915 continue;
916 else if ((type == AP_CPU) || (type == AP_CMP)) {
917 for (j = 0; j < capinfo[i].ncap; j++) {
918 rcm->cpuids[ncpus++] = capinfo[i].type.cpuid[j];
920 } else if (type == AP_MEM)
921 npages += capinfo[i].type.npages;
924 if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
925 CMD_RCM_CAP_DEL, ncpus)) != RCM_SUCCESS)) {
926 return (CFGA_LIB_ERROR);
928 if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
929 CMD_RCM_CAP_DEL, npages)) != RCM_SUCCESS)) {
930 return (CFGA_LIB_ERROR);
933 return (CFGA_OK);
936 static cfga_err_t
937 ap_rcm_add_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
938 int *rv, uint_t flags, rcm_info_t **rinfo)
940 int cm;
941 int ncpus;
942 long npages;
943 cap_info_t *capinfo, *prevcapinfo;
944 cfga_err_t rc;
946 DBG("ap_rcm_add_cap(%p)\n", (void *)a);
948 /* Get the new capacity info to figure out what has changed */
949 if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
950 CFGA_OK)
951 return (rc);
953 if (capinfo == NULL) {
954 DBG("no pertinent capacity info\n");
955 return (CFGA_OK);
958 ncpus = npages = 0;
959 prevcapinfo = rcm->capinfo;
961 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
962 int i, j;
963 cfga_stat_t os, prevos;
964 int prevvalidity;
965 ap_target_t type;
968 * See if the request target is a single
969 * (default) component
971 i = cm == CM_DFLT ? 0 : cm;
973 os = capinfo[i].ostate;
974 if (prevcapinfo == NULL) {
975 prevos = CFGA_STAT_EMPTY;
976 prevvalidity = 1;
977 } else {
978 prevos = prevcapinfo[i].ostate;
979 prevvalidity = prevcapinfo[i].valid;
982 type = ap_cm_type(a, cm);
984 DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
985 cm, prevvalidity, type, prevos, os);
988 * We are interested only in those components
989 * whose states have changed to configured as
990 * the result of the current cfgadm request.
992 if (prevvalidity == 0 || os != CFGA_STAT_CONFIGURED) {
993 capinfo[i].valid = 0;
994 continue;
995 } else if (prevos != CFGA_STAT_CONFIGURED) {
997 * The occupant state is configured, and
998 * the previous occupant state was not.
1000 if ((type == AP_CPU) || (type == AP_CMP)) {
1001 for (j = 0; j < capinfo[i].ncap; j++) {
1002 rcm->cpuids[ncpus++] =
1003 capinfo[i].type.cpuid[j];
1005 } else if (type == AP_MEM)
1006 npages += capinfo[i].type.npages;
1009 free(capinfo);
1011 if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1012 CMD_RCM_CAP_ADD, ncpus)) != RCM_SUCCESS)) {
1013 return (CFGA_LIB_ERROR);
1015 if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1016 CMD_RCM_CAP_ADD, npages)) != RCM_SUCCESS)) {
1017 return (CFGA_LIB_ERROR);
1020 return (CFGA_OK);
1024 * ap_rcm_notify_cap:
1026 * This routine handles the CMD_RCM_CAP_NOTIFY command. It
1027 * is called after a successful/failed DR unconfigure
1028 * operation. It filters out components that have changed
1029 * and passes this information on to ap_rcm_cap_{cpu,mem}.
1031 * ap_rcm_cap_{cpu,mem} will still be called if all the
1032 * components have not changed and at least one {cpu,mem}
1033 * component was originally configured.
1035 static cfga_err_t
1036 ap_rcm_notify_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
1037 int *rv, uint_t flags, rcm_info_t **rinfo)
1039 cfga_err_t rc;
1040 cap_info_t *capinfo;
1041 cap_info_t *prevcapinfo;
1042 int cm;
1043 long npages = 0;
1044 int ncpus = 0;
1045 int prev_mem = 0; /* # of prev. configured mem components */
1046 int prev_cpus = 0; /* # of prev. configured CPUs */
1048 DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
1050 /* Get the new capacity info to figure out what has changed */
1051 if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
1052 CFGA_OK)
1053 return (rc);
1055 if (capinfo == NULL) {
1056 DBG("no pertinent capacity info\n");
1057 return (CFGA_OK);
1060 /* The original capacity info */
1061 prevcapinfo = rcm->capinfo;
1064 * Cycle through all components that we are operating
1065 * on. Record which components' occupant states have
1066 * changed.
1068 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
1069 int i;
1070 cfga_stat_t prevos, os;
1071 ap_target_t type;
1072 int prev_conf = 0;
1073 int now_conf = 0;
1076 * See if the request target is a single
1077 * (default) component
1079 i = cm == CM_DFLT ? 0 : cm;
1081 os = capinfo[i].ostate;
1083 if (prevcapinfo == NULL) {
1084 prevos = CFGA_STAT_EMPTY;
1085 } else {
1086 prevos = prevcapinfo[i].ostate;
1087 if (prevcapinfo[i].valid == 0) {
1088 DBG("ap_rcm_notify_cap: skipping component "
1089 "due to prevvalidity == 0\n");
1090 continue;
1094 type = ap_cm_type(a, cm);
1096 prev_conf = (prevos == CFGA_STAT_CONFIGURED);
1097 now_conf = (os == CFGA_STAT_CONFIGURED);
1100 * Build up rcm->cpuids with the IDs of CPUs that
1101 * have been removed. Record the number of removed
1102 * CPUs and pages.
1104 if (type == AP_CPU || type == AP_CMP) {
1105 if (prev_conf)
1106 prev_cpus++;
1107 if (prev_conf && !now_conf) {
1108 int j;
1109 for (j = 0; j < capinfo[i].ncap; j++) {
1110 rcm->cpuids[ncpus++] =
1111 capinfo[i].type.cpuid[j];
1114 } else if (type == AP_MEM) {
1115 if (prev_conf)
1116 prev_mem++;
1117 if (prev_conf && !now_conf)
1118 npages += capinfo[i].type.npages;
1121 free(capinfo);
1124 * If any CPU or memory components were operated on,
1125 * successfully or not, the rcm_notify_capacity_change()
1126 * routine must be called.
1129 if (prev_cpus) {
1130 *rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1131 CMD_RCM_CAP_NOTIFY, ncpus);
1133 if (*rv != RCM_SUCCESS)
1134 return (CFGA_LIB_ERROR);
1137 if (prev_mem) {
1138 *rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1139 CMD_RCM_CAP_NOTIFY, npages);
1141 if (*rv != RCM_SUCCESS)
1142 return (CFGA_LIB_ERROR);
1145 return (CFGA_OK);
1148 cfga_err_t
1149 ap_rcm_ctl(apd_t *a, int cmd)
1151 int i;
1152 int rv;
1153 int noop;
1154 int ncpus;
1155 int cm;
1156 uint_t flags;
1157 char *rsrc;
1158 char **rlist;
1159 rcmd_t *rcm;
1160 rcm_info_t *rinfo;
1161 rcm_handle_t *hd;
1162 cfga_err_t rc;
1163 cpuid_t *growcpuids;
1165 DBG("ap_rcm_ctl(%p)\n", (void *)a);
1167 if ((rcm = (rcmd_t *)a->rcm) == NULL) {
1168 ap_msg(a, MSG_SKIP, cmd, a->target);
1169 return (CFGA_OK);
1172 hd = rcm->hd;
1173 rv = RCM_SUCCESS;
1174 rc = CFGA_OK;
1175 if (ap_getopt(a, OPT_FORCE))
1176 flags = RCM_FORCE;
1177 else
1178 flags = 0;
1179 rinfo = NULL;
1180 rlist = NULL;
1181 rsrc = NULL;
1182 noop = 0;
1184 switch (cmd) {
1185 case CMD_RCM_CAP_DEL:
1186 if (rcm->capinfo == NULL)
1187 noop++;
1188 else
1189 rc = ap_rcm_request_cap(a, rcm, hd, &rv, flags, &rinfo);
1190 break;
1191 case CMD_RCM_CAP_ADD:
1192 rc = ap_rcm_add_cap(a, rcm, hd, &rv, flags, &rinfo);
1193 break;
1194 case CMD_RCM_CAP_NOTIFY:
1195 rc = ap_rcm_notify_cap(a, rcm, hd, &rv, flags, &rinfo);
1196 break;
1197 case CMD_RCM_ONLINE:
1198 /* Refresh changed component states */
1199 if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1200 noop++;
1201 break;
1204 if (a->tgt == AP_BOARD) {
1205 rcm->firstcm = 0;
1206 rcm->lastcm = a->ncm - 1;
1208 /* Check if we need to grow our cpuids list */
1209 for (ncpus = 0, cm = rcm->firstcm; cm <= rcm->lastcm;
1210 cm++) {
1211 ap_target_t type = ap_cm_type(a, cm);
1212 if ((type == AP_CPU) || (type == AP_CMP))
1213 ncpus += ap_cm_ncap(a, cm);
1216 if (rcm->ncpus < ncpus) {
1217 if ((growcpuids =
1218 (cpuid_t *)realloc(rcm->cpuids,
1219 (ncpus * sizeof (cpuid_t)))) == NULL) {
1220 ap_err(a, ERR_NOMEM);
1221 return (CFGA_LIB_ERROR);
1223 rcm->ncpus = ncpus;
1224 rcm->cpuids = growcpuids;
1227 } else {
1228 rcm->firstcm = CM_DFLT;
1229 rcm->lastcm = CM_DFLT;
1232 /*FALLTHROUGH*/
1234 case CMD_RCM_OFFLINE:
1235 case CMD_RCM_REMOVE: {
1236 uint_t nrsrc;
1238 if (cmd == CMD_RCM_REMOVE) {
1240 * An unconfigure has just taken place, so
1241 * refresh the changed component states.
1243 if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1244 noop++;
1245 break;
1249 /* Check if this is an empty board, i.e. no components */
1250 if (a->ncm == 0) {
1251 noop++;
1252 break;
1255 if ((rlist = rcm->rlist) == NULL) {
1256 rc = ap_rcm_rlist(a, rcm->firstcm, rcm->lastcm, &rlist,
1257 cmd);
1258 if ((rc == CFGA_OK) && (rlist != NULL) &&
1259 (rlist[0] != NULL)) {
1260 rcm->rlist = rlist;
1261 } else {
1262 /* Do not pass up empty resource list to RCM */
1263 noop++;
1264 break;
1267 for (nrsrc = 0; rlist[nrsrc] != NULL; nrsrc++)
1268 ap_msg(a, MSG_ISSUE, cmd, rlist[nrsrc]);
1269 if (cmd == CMD_RCM_OFFLINE)
1270 rv = (*rcm->request_offline_list)(hd, rlist, flags,
1271 &rinfo);
1272 else if (cmd == CMD_RCM_ONLINE)
1273 rv = (*rcm->notify_online_list)(hd, rlist,
1274 flags & ~RCM_FORCE, &rinfo);
1275 else
1276 rv = (*rcm->notify_remove_list)(hd, rlist,
1277 flags & ~RCM_FORCE, &rinfo);
1278 break;
1280 case CMD_RCM_SUSPEND: {
1281 timespec_t t;
1282 t.tv_sec = (time_t)0;
1283 t.tv_nsec = (long)0;
1284 rsrc = OS;
1285 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1286 rv = (*rcm->request_suspend)(hd, rsrc, flags, &t, &rinfo);
1287 break;
1289 case CMD_RCM_RESUME:
1290 rsrc = OS;
1291 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1292 rv = (*rcm->notify_resume)(hd, rsrc, 0, &rinfo);
1293 break;
1294 default:
1295 ap_err(a, ERR_CMD_INVAL, cmd);
1296 return (CFGA_INVAL);
1299 if (rv != RCM_SUCCESS) {
1300 rcm->rinfo = rinfo;
1301 rcm->infot = NULL;
1302 ap_err(a, ERR_RCM_CMD, cmd);
1303 (*rcm->free_info)(rinfo);
1304 if (rc == CFGA_OK)
1305 rc = CFGA_LIB_ERROR; /* make sure error is set */
1307 if ((rc == CFGA_OK) && (noop == 0)) {
1308 if (rlist)
1309 for (i = 0; rlist[i]; i++)
1310 ap_msg(a, MSG_DONE, cmd, rlist[i]);
1311 else if (rsrc)
1312 ap_msg(a, MSG_DONE, cmd, rsrc);
1313 else
1314 ap_msg(a, MSG_DONE, cmd, a->target);
1317 return (rc);
1321 * ap_rcm_info
1323 * Takes an ap_id and a character pointer, and formats
1324 * the rcm_info_t data in the form of a table to the given character pointer.
1325 * Code duplicated from the scsi plugin.
1326 * Note: This function will go away when a generic librcm callback is
1327 * implemented to format RCM messages for plugins.
1330 ap_rcm_info(apd_t *a, char **msg)
1332 rcmd_t *rcm;
1333 rcm_info_t *rinfo;
1334 int i;
1335 size_t w;
1336 size_t width = 0;
1337 size_t w_rsrc = 0;
1338 size_t w_info = 0;
1339 size_t msg_size = 0;
1340 uint_t tuples = 0;
1341 rcm_info_tuple_t *tuple = NULL;
1342 char *rsrc;
1343 char *info;
1344 char *newmsg;
1345 static char format[RCM_MAX_FORMAT];
1346 const char *infostr;
1349 DBG("ap_rcm_info(%p)\n", (void *)a);
1351 /* Protect against invalid arguments */
1352 if ((a == NULL) || ((rcm = (rcmd_t *)a->rcm) == NULL) ||
1353 ((rinfo = rcm->rinfo) == NULL) || (msg == NULL)) {
1354 return (-1);
1357 /* Set localized table header strings */
1358 rsrc = dgettext(TEXT_DOMAIN, "Resource");
1359 info = dgettext(TEXT_DOMAIN, "Information");
1361 /* A first pass, to size up the RCM information */
1362 while (tuple = (*rcm->info_next)(rinfo, tuple)) {
1363 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1364 tuples++;
1365 if ((w = strlen((*rcm->info_rsrc)(tuple))) > w_rsrc)
1366 w_rsrc = w;
1367 if ((w = strlen(infostr)) > w_info)
1368 w_info = w;
1372 /* If nothing was sized up above, stop early */
1373 if (tuples == 0)
1374 return (0);
1376 /* Adjust column widths for column headings */
1377 if ((w = strlen(rsrc)) > w_rsrc)
1378 w_rsrc = w;
1379 else if ((w_rsrc - w) % 2)
1380 w_rsrc++;
1381 if ((w = strlen(info)) > w_info)
1382 w_info = w;
1383 else if ((w_info - w) % 2)
1384 w_info++;
1387 * Compute the total line width of each line,
1388 * accounting for intercolumn spacing.
1390 width = w_info + w_rsrc + 4;
1392 /* Allocate space for the table */
1393 msg_size = (2 + tuples) * (width + 1) + 2;
1394 if (*msg == NULL) {
1395 /* zero fill for the strcat() call below */
1396 *msg = calloc(msg_size, sizeof (char));
1397 if (*msg == NULL)
1398 return (-1);
1399 } else {
1400 newmsg = realloc(*msg, strlen(*msg) + msg_size);
1401 if (newmsg == NULL)
1402 return (-1);
1403 else
1404 *msg = newmsg;
1407 /* Place a table header into the string */
1409 /* The resource header */
1410 (void) strcat(*msg, "\n");
1411 w = strlen(rsrc);
1412 for (i = 0; i < ((w_rsrc - w) / 2); i++)
1413 (void) strcat(*msg, " ");
1414 (void) strcat(*msg, rsrc);
1415 for (i = 0; i < ((w_rsrc - w) / 2); i++)
1416 (void) strcat(*msg, " ");
1418 /* The information header */
1419 (void) strcat(*msg, " ");
1420 w = strlen(info);
1421 for (i = 0; i < ((w_info - w) / 2); i++)
1422 (void) strcat(*msg, " ");
1423 (void) strcat(*msg, info);
1424 for (i = 0; i < ((w_info - w) / 2); i++)
1425 (void) strcat(*msg, " ");
1427 /* Underline the headers */
1428 (void) strcat(*msg, "\n");
1429 for (i = 0; i < w_rsrc; i++)
1430 (void) strcat(*msg, "-");
1431 (void) strcat(*msg, " ");
1432 for (i = 0; i < w_info; i++)
1433 (void) strcat(*msg, "-");
1435 /* Construct the format string */
1436 (void) snprintf(format, RCM_MAX_FORMAT, "%%-%ds %%-%ds",
1437 (int)w_rsrc, (int)w_info);
1439 /* Add the tuples to the table string */
1440 tuple = NULL;
1441 while ((tuple = (*rcm->info_next)(rinfo, tuple)) != NULL) {
1442 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1443 (void) strcat(*msg, "\n");
1444 (void) sprintf(&((*msg)[strlen(*msg)]), format,
1445 (*rcm->info_rsrc)(tuple), infostr);
1449 DBG("ap_rcm_info(%p) success\n", (void *)a);
1450 return (0);