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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
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"
44
45 typedef int32_t cpuid_t;
46
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;
56
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;
94
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
115 };
116
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
134
135 /*
136 * There is no consumer for SUNW_OS. This is defined here
137 * for generic OS quiescence.
138 */
139 #define OS "SUNW_OS" /* XXX */
140
141 /* Max width of an RCM formatted message line */
142 #define RCM_MAX_FORMAT 80
143
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
151
152 static cfga_err_t
ap_capinfo(apd_t * a,int firstcm,int lastcm,cap_info_t ** capinfo)153 ap_capinfo(apd_t *a, int firstcm, int lastcm, cap_info_t **capinfo)
154 {
155 int cm;
156 int ncm;
157 void *cap;
158 int *ncap;
159 cfga_stat_t *os;
160 cap_info_t *cinfo, *cp;
161
162 DBG("ap_capinfo(%p)\n", (void *)a);
163
164 if (capinfo == NULL) {
165 ap_err(a, ERR_PLUGIN, "null capinfo");
166 return (CFGA_LIB_ERROR);
167 }
168
169 /*
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.
173 */
174 ncm = lastcm - firstcm + 1;
175
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);
180 }
181
182 *capinfo = NULL;
183 ncm = 0;
184 for (cp = cinfo, cm = firstcm; cm <= lastcm; cm++, cp++) {
185 os = &cp->ostate;
186 ncap = &cp->ncap;
187
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;
198 }
199 /*
200 * Remember which components have valid
201 * capacity information.
202 */
203 if (ap_cm_capacity(a, cm, cap, ncap, os)) {
204 cp->valid = 1;
205 ncm++;
206 }
207 }
208
209 if (ncm == 0)
210 free(cinfo);
211 else
212 *capinfo = cinfo;
213
214 return (CFGA_OK);
215 }
216
217 static int
getsyscpuids(int * ncpuids,cpuid_t ** cpuids)218 getsyscpuids(int *ncpuids, cpuid_t **cpuids)
219 {
220 int ncpu;
221 int maxncpu;
222 kstat_t *ksp;
223 kstat_ctl_t *kc = NULL;
224 cpuid_t *cp;
225
226 DBG("getsyscpuids\n");
227
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);
234 }
235 return (-1);
236 }
237
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);
243 }
244 }
245 DBG("\n");
246
247 (void) kstat_close(kc);
248 *cpuids = cp;
249 *ncpuids = ncpu;
250 return (0);
251 }
252
253 cfga_err_t
ap_rcm_init(apd_t * a)254 ap_rcm_init(apd_t *a)
255 {
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;
265
266 DBG("ap_rcm_init(%p)\n", (void *)a);
267
268 /*
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.
272 */
273
274 if ((a->statonly != 0) || (a->norcm != 0) ||
275 ((rcm = (rcmd_t *)a->rcm) != NULL)) {
276 return (CFGA_OK);
277 }
278
279 rcmlib = RCMLIB;
280 rc = CFGA_LIB_ERROR;
281
282 DBG("Looking for %s\n", rcmlib);
283 /*
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.
287 */
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);
296 }
297 }
298 DBG("%s found\n", rcmlib);
299
300 if ((a->rcm = calloc(1, sizeof (rcmd_t))) == NULL) {
301 ap_err(a, ERR_NOMEM);
302 return (rc);
303 }
304
305 rcm = (rcmd_t *)a->rcm;
306
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 if (err != NULL)
312 free(err);
313 return (rc);
314 }
315
316 rcm->lib = lib;
317
318 for (i = 0, op = ap_rcm_ops; *op != NULL; op++, i++) {
319 if ((sym = dlsym(lib, *op)) == NULL) {
320 ap_err(a, ERR_LIB_SYM, rcmlib, *op);
321 return (rc);
322 }
323 switch (i) {
324 case ALLOC_HANDLE:
325 rcm->alloc_handle = (int(*)
326 (char *, uint_t, void *, rcm_handle_t **))sym;
327 break;
328 case FREE_HANDLE:
329 rcm->free_handle = (void (*)(rcm_handle_t *))sym;
330 break;
331 case GET_INFO:
332 rcm->get_info = (int (*)
333 (rcm_handle_t *, char *, uint_t, rcm_info_t **))sym;
334 break;
335 case FREE_INFO:
336 rcm->free_info = (void (*)(rcm_info_t *))sym;
337 break;
338 case INFO_TUPLE_NEXT:
339 rcm->info_next = (rcm_info_tuple_t *(*)
340 (rcm_info_t *, rcm_info_tuple_t *))sym;
341 break;
342 case INFO_TUPLE_STATE:
343 rcm->info_state = (int (*)(rcm_info_tuple_t *))sym;
344 break;
345 case INFO_TUPLE_ID:
346 rcm->info_pid = (pid_t (*)(rcm_info_tuple_t *))sym;
347 break;
348 case INFO_TUPLE_ERROR:
349 rcm->info_error = (const char *(*)
350 (rcm_info_tuple_t *))sym;
351 break;
352 case INFO_TUPLE_INFO:
353 rcm->info_info = (const char *(*)
354 (rcm_info_tuple_t *))sym;
355 break;
356 case INFO_TUPLE_RSRC:
357 rcm->info_rsrc = (const char *(*)
358 (rcm_info_tuple_t *))sym;
359 break;
360 case REQUEST_OFFLINE:
361 rcm->request_offline_list = (int (*)
362 (rcm_handle_t *, char **, uint_t,
363 rcm_info_t **))sym;
364 break;
365 case NOTIFY_ONLINE:
366 rcm->notify_online_list = (int (*)
367 (rcm_handle_t *, char **, uint_t,
368 rcm_info_t **))sym;
369 break;
370 case REQUEST_SUSPEND:
371 rcm->request_suspend = (int (*)
372 (rcm_handle_t *, char *, uint_t,
373 timespec_t *, rcm_info_t **))sym;
374 break;
375 case NOTIFY_RESUME:
376 rcm->notify_resume = (int (*)
377 (rcm_handle_t *, char *, uint_t,
378 rcm_info_t **))sym;
379 break;
380 case NOTIFY_REMOVE:
381 rcm->notify_remove_list = (int (*)
382 (rcm_handle_t *, char **, uint_t,
383 rcm_info_t **))sym;
384 break;
385 case REQUEST_CAP_CHANGE:
386 rcm->request_capacity_change = (int (*)
387 (rcm_handle_t *, char *, uint_t,
388 nvlist_t *, rcm_info_t **))sym;
389 break;
390 case NOTIFY_CAP_CHANGE:
391 rcm->notify_capacity_change = (int (*)
392 (rcm_handle_t *, char *, uint_t,
393 nvlist_t *, rcm_info_t **))sym;
394 break;
395 default:
396 break;
397 }
398 }
399
400 if (rcm->alloc_handle == NULL ||
401 (*rcm->alloc_handle)(NULL, RCM_NOPID, NULL, &rcm->hd)
402 != RCM_SUCCESS) {
403 ap_err(a, ERR_RCM_HANDLE);
404 return (CFGA_LIB_ERROR);
405 }
406
407 /*
408 * Offlining/onlining a board means offlining/onlining
409 * all components on the board. When operating on a
410 * single component no component sequence number is
411 * needed since the default is the current (target)
412 * component.
413 */
414 if (a->tgt == AP_BOARD) {
415 rcm->firstcm = 0;
416 rcm->lastcm = a->ncm - 1;
417 } else {
418 rcm->firstcm = CM_DFLT;
419 rcm->lastcm = CM_DFLT;
420 }
421
422 if (rcm->cpuids == NULL) {
423 int cm;
424 int ncpu;
425
426 /*
427 * Allocate space for the cpu capacity change info.
428 * Not every cpu may be relevant to the capacity
429 * request, but allocating for the maximum makes
430 * it easier, and the space is insignifcant.
431 */
432 for (ncpu = 0, cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
433
434 ap_target_t type = ap_cm_type(a, cm);
435
436 if ((type == AP_CPU) || (type == AP_CMP)) {
437 ncpu += ap_cm_ncap(a, cm);
438 }
439 }
440
441 rcm->ncpus = ncpu;
442 if ((rcm->cpuids = (cpuid_t *)calloc(ncpu, sizeof (cpuid_t)))
443 == NULL) {
444 ap_err(a, ERR_NOMEM);
445 return (CFGA_LIB_ERROR);
446 }
447 }
448
449 /*
450 * Remember initial capacity information.
451 * This information is based on the initial
452 * state of the ap_id, i.e. before any
453 * state change change operations were
454 * executed. We will later get the
455 * current capacity information in order
456 * to figure out exactly what has changed
457 * as the result of the executed command
458 * sequence.
459 */
460 rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &rcm->capinfo);
461
462 rcm->capcpus = sysconf(_SC_NPROCESSORS_CONF);
463 rcm->cappages = sysconf(_SC_PHYS_PAGES);
464
465 return (rc);
466 }
467
468 void
ap_rcm_fini(apd_t * a)469 ap_rcm_fini(apd_t *a)
470 {
471 rcmd_t *rcm;
472 char **rp;
473
474 DBG("ap_rcm_fini(%p)\n", (void *)a);
475
476 if ((rcm = (rcmd_t *)a->rcm) == NULL)
477 return;
478
479 if (rcm->hd)
480 (*rcm->free_handle)(rcm->hd);
481
482 (void) dlclose(rcm->lib);
483
484 /*
485 * Free all the names in the resource list, followed
486 * by the resource list itself.
487 */
488 if (rcm->rlist)
489 for (rp = rcm->rlist; *rp; rp++)
490 s_free(*rp);
491 s_free(rcm->rlist);
492 s_free(rcm->cpuids);
493 s_free(rcm->capinfo);
494 s_free(a->rcm);
495 }
496
497 static cfga_err_t
ap_rcm_rlist(apd_t * a,int firstcm,int lastcm,char *** rlist,int cmd)498 ap_rcm_rlist(apd_t *a, int firstcm, int lastcm, char ***rlist, int cmd)
499 {
500 int n;
501 int cm;
502 int ncap;
503 char *path;
504 char *cpuname;
505 char **rp;
506
507 DBG("ap_rcm_rlist(%p)\n", (void *)a);
508
509 /*
510 * Allocate space for the maximum number of components
511 * that can be affected by this operation.
512 */
513 for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
514 ncap += ap_cm_ncap(a, cm);
515 }
516
517 DBG("ncap=%d\n", ncap);
518
519 if ((rp = (char **)calloc(ncap + 1, sizeof (char *))) == NULL) {
520 ap_err(a, ERR_NOMEM);
521 return (CFGA_LIB_ERROR);
522 }
523
524 n = 12; /* SUNW_cpu/cpuCCC */
525 /* <--- 12 ---> */
526 cpuname = "SUNW_cpu/cpuCCC";
527 /*
528 * Set the RCM resource name for each component:
529 *
530 * io: <device-path>
531 * cpu: SUNW_cpu/cpu<cpuid>
532 *
533 */
534 for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
535 switch (ap_cm_type(a, cm)) {
536 case AP_CPU:
537 case AP_CMP: {
538 int i;
539 int len;
540 cap_info_t cap;
541 cfga_stat_t os;
542 cpuid_t *cpuid;
543 int *nc;
544 cap_info_t *prevcap;
545 rcmd_t *rcm;
546 int allow_op;
547 int capindex;
548
549 cpuid = cap.type.cpuid;
550 nc = &cap.ncap;
551
552 /*
553 * See if the request target is a single
554 * (default) component
555 */
556 capindex = (cm == CM_DFLT) ? 0 : cm;
557
558 /* Get the previous capacity info */
559 rcm = (rcmd_t *)a->rcm;
560 prevcap = rcm->capinfo;
561
562 if (!ap_cm_capacity(a, cm, cpuid, nc, &os)) {
563 break;
564 }
565
566 len = (strlen(cpuname) - n) + 1;
567
568 /*
569 * For CMD_RCM_OFFLINE and REMOVE, add the CPU to the
570 * list if it is currently configured. For
571 * CMD_RCM_ONLINE, do so only if the state has changed
572 * to CFGA_STAT_CONFIGURED.
573 */
574 allow_op = 0;
575 if ((cmd == CMD_RCM_OFFLINE) ||
576 (cmd == CMD_RCM_REMOVE)) {
577 if (os == CFGA_STAT_CONFIGURED)
578 allow_op = 1;
579 } else {
580 if ((os == CFGA_STAT_CONFIGURED) &&
581 ((prevcap == NULL) ||
582 (prevcap[capindex].ostate != os)))
583 allow_op = 1;
584 }
585
586 if (allow_op) {
587 for (i = 0; i < *nc; i++) {
588 if ((path = strdup(cpuname)) == NULL) {
589 ap_err(a, ERR_NOMEM);
590 return (CFGA_LIB_ERROR);
591 }
592 (void) snprintf(&path[n], len, "%d",
593 cpuid[i]);
594
595 DBG("rp[%d]=%s\n", ncap, path);
596 rp[ncap++] = path;
597 }
598 }
599 break;
600 }
601 case AP_IO:
602 if ((path = ap_cm_devpath(a, cm)) != NULL) {
603 DBG("rp[%d]=%s\n", ncap, path);
604 rp[ncap++] = path;
605 }
606 break;
607 case AP_MEM:
608 /*
609 * Nothing to do for AP_MEM since only capacity
610 * change notifications apply to SUNW_memory
611 */
612 default:
613 break;
614 }
615 }
616
617 rp[ncap] = NULL;
618 if (rlist)
619 *rlist = rp;
620 return (CFGA_OK);
621 }
622
623 /*
624 * Returns 1 if the cpu ID 'cpuid' is in the list of CPU IDs
625 * 'list' of length 'length'. Returns 0 otherwise.
626 */
627 static int
is_cpu_in_list(cpuid_t cpuid,cpuid_t * list,int length)628 is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int length)
629 {
630 int i;
631
632 DBG("is_cpu_in_list\n");
633
634 if (list == NULL)
635 return (0);
636
637 for (i = 0; i < length; i++) {
638 if (list[i] == cpuid)
639 return (1);
640 }
641 return (0);
642 }
643
644 static int
ap_rcm_cap_cpu(apd_t * a,rcmd_t * rcm,rcm_handle_t * hd,uint_t flags,rcm_info_t ** rinfo,int cmd,int change)645 ap_rcm_cap_cpu(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
646 rcm_info_t **rinfo, int cmd, int change)
647 {
648 int i;
649 int rv = RCM_FAILURE;
650 int ncpuids;
651 int oldncpuids;
652 int newncpuids;
653 char buf[32];
654 const char *fmt;
655 size_t size;
656 nvlist_t *nvl = NULL;
657 cpuid_t *cpuids = NULL;
658 cpuid_t *oldcpuids = NULL;
659 cpuid_t *newcpuids = NULL;
660
661 DBG("ap_rcm_cap_cpu(%p)\n", (void *)a);
662
663 /*
664 * Get the current number of configured cpus.
665 */
666 if (getsyscpuids(&ncpuids, &cpuids) == -1)
667 return (rv);
668 else if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
669 free(cpuids);
670 goto done;
671 }
672
673 if (change == 1)
674 fmt = "(%d cpu)";
675 else
676 fmt = "(%d cpus)";
677
678 size = sizeof (cpuid_t);
679
680 if (cmd == CMD_RCM_CAP_DEL) {
681 /*
682 * A delete request. rcm->cpuids represents the
683 * cpus that will be unconfigured. The current
684 * set of cpus, before the unconfigure operation,
685 * are the old CPUs. The new CPUs are those
686 * that would remain.
687 */
688 oldncpuids = ncpuids;
689 oldcpuids = cpuids;
690
691 /*
692 * Fill newcpuids with the CPU IDs in the cpuids array,
693 * but not in rcm->cpuids.
694 */
695 newcpuids = (cpuid_t *)calloc(ncpuids, size);
696 if (newcpuids == NULL)
697 goto done;
698
699 newncpuids = 0;
700 for (i = 0; i < ncpuids; i++) {
701 if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
702 newcpuids[newncpuids++] = cpuids[i];
703 }
704 } else if (cmd == CMD_RCM_CAP_NOTIFY) {
705 /*
706 * An unconfigure capacity change notification. This
707 * notification is sent after a DR unconfigure, whether
708 * or not the DR was successful. rcm->cpuids represents
709 * the CPUs that have been unconfigured.
710 */
711
712 /* New CPU IDs are the CPUs configured right now. */
713 newncpuids = ncpuids;
714 newcpuids = cpuids;
715
716 /*
717 * Old CPU IDs are the CPUs configured right now
718 * in addition to those that have been unconfigured.
719 * We build the old CPU ID list by concatenating
720 * cpuids and rcm->cpuids.
721 */
722 oldcpuids = (cpuid_t *)calloc(ncpuids + change, size);
723 if (oldcpuids == NULL)
724 goto done;
725
726 oldncpuids = 0;
727 for (i = 0; i < ncpuids; i++) {
728 if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
729 oldcpuids[oldncpuids++] = cpuids[i];
730 }
731 for (i = 0; i < change; i++)
732 oldcpuids[oldncpuids++] = rcm->cpuids[i];
733 } else {
734 DBG("ap_rcm_cap_cpu: CPU capacity, old = %d, new = %d \n",
735 rcm->capcpus, ncpuids);
736 if (rcm->capcpus == ncpuids) {
737 /* No real change in CPU capacity */
738 rv = RCM_SUCCESS;
739 goto done;
740 }
741
742 /*
743 * An add notification. rcm->cpuids represents the
744 * cpus that have been configured. The current
745 * set of cpus, after the configure operation,
746 * are the new CPU IDs.
747 */
748 newncpuids = ncpuids;
749 newcpuids = cpuids;
750
751 /*
752 * Fill oldcpuids with the CPU IDs in the cpuids array,
753 * but not in rcm->cpuids.
754 */
755 oldcpuids = (cpuid_t *)calloc(ncpuids, size);
756 if (oldcpuids == NULL)
757 goto done;
758
759 oldncpuids = 0;
760 for (i = 0; i < ncpuids; i++) {
761 if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
762 oldcpuids[oldncpuids++] = cpuids[i];
763 }
764 }
765
766 DBG("oldcpuids: ");
767 for (i = 0; i < oldncpuids; i++)
768 DBG("%d ", oldcpuids[i]);
769 DBG("\n");
770 DBG("change : ");
771 for (i = 0; i < change; i++)
772 DBG("%d ", rcm->cpuids[i]);
773 DBG("\n");
774 DBG("newcpuids: ");
775 for (i = 0; i < newncpuids; i++)
776 DBG("%d ", newcpuids[i]);
777 DBG("\n");
778
779 if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
780 nvlist_add_int32(nvl, "old_total", oldncpuids) != 0 ||
781 nvlist_add_int32(nvl, "new_total", newncpuids) != 0 ||
782 nvlist_add_int32_array(nvl, "old_cpu_list", oldcpuids,
783 oldncpuids) != 0 ||
784 nvlist_add_int32_array(nvl, "new_cpu_list", newcpuids,
785 newncpuids) != 0)
786 goto done;
787
788 (void) snprintf(buf, sizeof (buf), fmt, change);
789 ap_msg(a, MSG_ISSUE, cmd, buf);
790
791 if (cmd == CMD_RCM_CAP_DEL) {
792 rv = (*rcm->request_capacity_change)(hd, "SUNW_cpu",
793 flags, nvl, rinfo);
794 } else {
795 rv = (*rcm->notify_capacity_change)(hd, "SUNW_cpu",
796 flags & ~RCM_FORCE, nvl, rinfo);
797 }
798
799 done:
800 nvlist_free(nvl);
801 s_free(oldcpuids);
802 s_free(newcpuids);
803 return (rv);
804 }
805
806 static int
ap_rcm_cap_mem(apd_t * a,rcmd_t * rcm,rcm_handle_t * hd,uint_t flags,rcm_info_t ** rinfo,int cmd,long change)807 ap_rcm_cap_mem(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
808 rcm_info_t **rinfo, int cmd, long change)
809 {
810 int rv;
811 int pgsize;
812 long oldpages;
813 long newpages;
814 long currpages;
815 char buf[32];
816 nvlist_t *nvl;
817
818 DBG("ap_rcm_cap_mem(%p)\n", (void *)a);
819
820 /*
821 * Get the current amount of configured memory.
822 */
823 if ((pgsize = sysconf(_SC_PAGE_SIZE)) == -1 ||
824 (currpages = sysconf(_SC_PHYS_PAGES)) == -1 ||
825 nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) > 0)
826 return (RCM_FAILURE);
827
828 /*
829 * If this is a (delete) request, change represents
830 * the amount of capacity that will be deleted from the
831 * system. If this is an (add) notification, change
832 * represents the amount of capacity that has already
833 * been added to the system.
834 */
835 if (cmd == CMD_RCM_CAP_DEL) {
836 oldpages = currpages;
837 newpages = currpages - change;
838 } else if (cmd == CMD_RCM_CAP_NOTIFY) {
839 newpages = currpages;
840 oldpages = rcm->cappages;
841 } else {
842 if (rcm->cappages == currpages) {
843 /* No real change in memory capacity */
844 DBG("ap_rcm_cap_mem: no change in capacity.\n");
845 nvlist_free(nvl);
846 return (RCM_SUCCESS);
847 }
848
849 oldpages = currpages - change;
850 newpages = currpages;
851 }
852
853 DBG("ap_rcm_cap_mem: Memory capacity, old = %ld, new = %ld\n",
854 oldpages, newpages);
855
856 if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
857 nvlist_add_int32(nvl, "page_size", pgsize) != 0 ||
858 nvlist_add_int32(nvl, "old_pages", oldpages) != 0 ||
859 nvlist_add_int32(nvl, "new_pages", newpages) != 0) {
860 nvlist_free(nvl);
861 return (RCM_FAILURE);
862 }
863
864 (void) snprintf(buf, sizeof (buf), "(%ld pages)", change);
865 ap_msg(a, MSG_ISSUE, cmd, buf);
866
867 if (cmd == CMD_RCM_CAP_DEL) {
868 rv = (*rcm->request_capacity_change)(hd, "SUNW_memory",
869 flags, nvl, rinfo);
870 } else {
871 rv = (*rcm->notify_capacity_change)(hd, "SUNW_memory",
872 flags & ~RCM_FORCE, nvl, rinfo);
873 }
874
875 nvlist_free(nvl);
876
877 return (rv);
878 }
879
880 static cfga_err_t
ap_rcm_request_cap(apd_t * a,rcmd_t * rcm,rcm_handle_t * hd,int * rv,uint_t flags,rcm_info_t ** rinfo)881 ap_rcm_request_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
882 int *rv, uint_t flags, rcm_info_t **rinfo)
883 {
884 int cm;
885 int ncpus;
886 long npages;
887 cap_info_t *capinfo;
888 ap_target_t type;
889
890 DBG("ap_rcm_request_cap(%p)\n", (void *)a);
891
892 if ((capinfo = rcm->capinfo) == NULL) {
893 ap_err(a, ERR_PLUGIN, "null capinfo");
894 return (CFGA_LIB_ERROR);
895 }
896
897 ncpus = npages = 0;
898
899 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
900 int i, j;
901
902 /*
903 * See if the request target is a single
904 * (default) component
905 */
906 i = (cm == CM_DFLT) ? 0 : cm;
907
908 /*
909 * We are interested only in those components
910 * in the configured state since they represent
911 * available capacity.
912 */
913 type = ap_cm_type(a, cm);
914 if (capinfo[i].valid == 0 ||
915 capinfo[i].ostate != CFGA_STAT_CONFIGURED)
916 continue;
917 else if ((type == AP_CPU) || (type == AP_CMP)) {
918 for (j = 0; j < capinfo[i].ncap; j++) {
919 rcm->cpuids[ncpus++] = capinfo[i].type.cpuid[j];
920 }
921 } else if (type == AP_MEM)
922 npages += capinfo[i].type.npages;
923 }
924
925 if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
926 CMD_RCM_CAP_DEL, ncpus)) != RCM_SUCCESS)) {
927 return (CFGA_LIB_ERROR);
928 }
929 if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
930 CMD_RCM_CAP_DEL, npages)) != RCM_SUCCESS)) {
931 return (CFGA_LIB_ERROR);
932 }
933
934 return (CFGA_OK);
935 }
936
937 static cfga_err_t
ap_rcm_add_cap(apd_t * a,rcmd_t * rcm,rcm_handle_t * hd,int * rv,uint_t flags,rcm_info_t ** rinfo)938 ap_rcm_add_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
939 int *rv, uint_t flags, rcm_info_t **rinfo)
940 {
941 int cm;
942 int ncpus;
943 long npages;
944 cap_info_t *capinfo, *prevcapinfo;
945 cfga_err_t rc;
946
947 DBG("ap_rcm_add_cap(%p)\n", (void *)a);
948
949 /* Get the new capacity info to figure out what has changed */
950 if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
951 CFGA_OK)
952 return (rc);
953
954 if (capinfo == NULL) {
955 DBG("no pertinent capacity info\n");
956 return (CFGA_OK);
957 }
958
959 ncpus = npages = 0;
960 prevcapinfo = rcm->capinfo;
961
962 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
963 int i, j;
964 cfga_stat_t os, prevos;
965 int prevvalidity;
966 ap_target_t type;
967
968 /*
969 * See if the request target is a single
970 * (default) component
971 */
972 i = cm == CM_DFLT ? 0 : cm;
973
974 os = capinfo[i].ostate;
975 if (prevcapinfo == NULL) {
976 prevos = CFGA_STAT_EMPTY;
977 prevvalidity = 1;
978 } else {
979 prevos = prevcapinfo[i].ostate;
980 prevvalidity = prevcapinfo[i].valid;
981 }
982
983 type = ap_cm_type(a, cm);
984
985 DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
986 cm, prevvalidity, type, prevos, os);
987
988 /*
989 * We are interested only in those components
990 * whose states have changed to configured as
991 * the result of the current cfgadm request.
992 */
993 if (prevvalidity == 0 || os != CFGA_STAT_CONFIGURED) {
994 capinfo[i].valid = 0;
995 continue;
996 } else if (prevos != CFGA_STAT_CONFIGURED) {
997 /*
998 * The occupant state is configured, and
999 * the previous occupant state was not.
1000 */
1001 if ((type == AP_CPU) || (type == AP_CMP)) {
1002 for (j = 0; j < capinfo[i].ncap; j++) {
1003 rcm->cpuids[ncpus++] =
1004 capinfo[i].type.cpuid[j];
1005 }
1006 } else if (type == AP_MEM)
1007 npages += capinfo[i].type.npages;
1008 }
1009 }
1010 free(capinfo);
1011
1012 if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1013 CMD_RCM_CAP_ADD, ncpus)) != RCM_SUCCESS)) {
1014 return (CFGA_LIB_ERROR);
1015 }
1016 if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1017 CMD_RCM_CAP_ADD, npages)) != RCM_SUCCESS)) {
1018 return (CFGA_LIB_ERROR);
1019 }
1020
1021 return (CFGA_OK);
1022 }
1023
1024 /*
1025 * ap_rcm_notify_cap:
1026 *
1027 * This routine handles the CMD_RCM_CAP_NOTIFY command. It
1028 * is called after a successful/failed DR unconfigure
1029 * operation. It filters out components that have changed
1030 * and passes this information on to ap_rcm_cap_{cpu,mem}.
1031 *
1032 * ap_rcm_cap_{cpu,mem} will still be called if all the
1033 * components have not changed and at least one {cpu,mem}
1034 * component was originally configured.
1035 */
1036 static cfga_err_t
ap_rcm_notify_cap(apd_t * a,rcmd_t * rcm,rcm_handle_t * hd,int * rv,uint_t flags,rcm_info_t ** rinfo)1037 ap_rcm_notify_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
1038 int *rv, uint_t flags, rcm_info_t **rinfo)
1039 {
1040 cfga_err_t rc;
1041 cap_info_t *capinfo;
1042 cap_info_t *prevcapinfo;
1043 int cm;
1044 long npages = 0;
1045 int ncpus = 0;
1046 int prev_mem = 0; /* # of prev. configured mem components */
1047 int prev_cpus = 0; /* # of prev. configured CPUs */
1048
1049 DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
1050
1051 /* Get the new capacity info to figure out what has changed */
1052 if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
1053 CFGA_OK)
1054 return (rc);
1055
1056 if (capinfo == NULL) {
1057 DBG("no pertinent capacity info\n");
1058 return (CFGA_OK);
1059 }
1060
1061 /* The original capacity info */
1062 prevcapinfo = rcm->capinfo;
1063
1064 /*
1065 * Cycle through all components that we are operating
1066 * on. Record which components' occupant states have
1067 * changed.
1068 */
1069 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
1070 int i;
1071 cfga_stat_t prevos, os;
1072 ap_target_t type;
1073 int prev_conf = 0;
1074 int now_conf = 0;
1075
1076 /*
1077 * See if the request target is a single
1078 * (default) component
1079 */
1080 i = cm == CM_DFLT ? 0 : cm;
1081
1082 os = capinfo[i].ostate;
1083
1084 if (prevcapinfo == NULL) {
1085 prevos = CFGA_STAT_EMPTY;
1086 } else {
1087 prevos = prevcapinfo[i].ostate;
1088 if (prevcapinfo[i].valid == 0) {
1089 DBG("ap_rcm_notify_cap: skipping component "
1090 "due to prevvalidity == 0\n");
1091 continue;
1092 }
1093 }
1094
1095 type = ap_cm_type(a, cm);
1096
1097 prev_conf = (prevos == CFGA_STAT_CONFIGURED);
1098 now_conf = (os == CFGA_STAT_CONFIGURED);
1099
1100 /*
1101 * Build up rcm->cpuids with the IDs of CPUs that
1102 * have been removed. Record the number of removed
1103 * CPUs and pages.
1104 */
1105 if (type == AP_CPU || type == AP_CMP) {
1106 if (prev_conf)
1107 prev_cpus++;
1108 if (prev_conf && !now_conf) {
1109 int j;
1110 for (j = 0; j < capinfo[i].ncap; j++) {
1111 rcm->cpuids[ncpus++] =
1112 capinfo[i].type.cpuid[j];
1113 }
1114 }
1115 } else if (type == AP_MEM) {
1116 if (prev_conf)
1117 prev_mem++;
1118 if (prev_conf && !now_conf)
1119 npages += capinfo[i].type.npages;
1120 }
1121 }
1122 free(capinfo);
1123
1124 /*
1125 * If any CPU or memory components were operated on,
1126 * successfully or not, the rcm_notify_capacity_change()
1127 * routine must be called.
1128 */
1129
1130 if (prev_cpus) {
1131 *rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1132 CMD_RCM_CAP_NOTIFY, ncpus);
1133
1134 if (*rv != RCM_SUCCESS)
1135 return (CFGA_LIB_ERROR);
1136 }
1137
1138 if (prev_mem) {
1139 *rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1140 CMD_RCM_CAP_NOTIFY, npages);
1141
1142 if (*rv != RCM_SUCCESS)
1143 return (CFGA_LIB_ERROR);
1144 }
1145
1146 return (CFGA_OK);
1147 }
1148
1149 cfga_err_t
ap_rcm_ctl(apd_t * a,int cmd)1150 ap_rcm_ctl(apd_t *a, int cmd)
1151 {
1152 int i;
1153 int rv;
1154 int noop;
1155 int ncpus;
1156 int cm;
1157 uint_t flags;
1158 char *rsrc;
1159 char **rlist;
1160 rcmd_t *rcm;
1161 rcm_info_t *rinfo;
1162 rcm_handle_t *hd;
1163 cfga_err_t rc;
1164 cpuid_t *growcpuids;
1165
1166 DBG("ap_rcm_ctl(%p)\n", (void *)a);
1167
1168 if ((rcm = (rcmd_t *)a->rcm) == NULL) {
1169 ap_msg(a, MSG_SKIP, cmd, a->target);
1170 return (CFGA_OK);
1171 }
1172
1173 hd = rcm->hd;
1174 rv = RCM_SUCCESS;
1175 rc = CFGA_OK;
1176 if (ap_getopt(a, OPT_FORCE))
1177 flags = RCM_FORCE;
1178 else
1179 flags = 0;
1180 rinfo = NULL;
1181 rlist = NULL;
1182 rsrc = NULL;
1183 noop = 0;
1184
1185 switch (cmd) {
1186 case CMD_RCM_CAP_DEL:
1187 if (rcm->capinfo == NULL)
1188 noop++;
1189 else
1190 rc = ap_rcm_request_cap(a, rcm, hd, &rv, flags, &rinfo);
1191 break;
1192 case CMD_RCM_CAP_ADD:
1193 rc = ap_rcm_add_cap(a, rcm, hd, &rv, flags, &rinfo);
1194 break;
1195 case CMD_RCM_CAP_NOTIFY:
1196 rc = ap_rcm_notify_cap(a, rcm, hd, &rv, flags, &rinfo);
1197 break;
1198 case CMD_RCM_ONLINE:
1199 /* Refresh changed component states */
1200 if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1201 noop++;
1202 break;
1203 }
1204
1205 if (a->tgt == AP_BOARD) {
1206 rcm->firstcm = 0;
1207 rcm->lastcm = a->ncm - 1;
1208
1209 /* Check if we need to grow our cpuids list */
1210 for (ncpus = 0, cm = rcm->firstcm; cm <= rcm->lastcm;
1211 cm++) {
1212 ap_target_t type = ap_cm_type(a, cm);
1213 if ((type == AP_CPU) || (type == AP_CMP))
1214 ncpus += ap_cm_ncap(a, cm);
1215 }
1216
1217 if (rcm->ncpus < ncpus) {
1218 if ((growcpuids =
1219 (cpuid_t *)realloc(rcm->cpuids,
1220 (ncpus * sizeof (cpuid_t)))) == NULL) {
1221 ap_err(a, ERR_NOMEM);
1222 return (CFGA_LIB_ERROR);
1223 }
1224 rcm->ncpus = ncpus;
1225 rcm->cpuids = growcpuids;
1226 }
1227
1228 } else {
1229 rcm->firstcm = CM_DFLT;
1230 rcm->lastcm = CM_DFLT;
1231 }
1232
1233 /*FALLTHROUGH*/
1234
1235 case CMD_RCM_OFFLINE:
1236 case CMD_RCM_REMOVE: {
1237 uint_t nrsrc;
1238
1239 if (cmd == CMD_RCM_REMOVE) {
1240 /*
1241 * An unconfigure has just taken place, so
1242 * refresh the changed component states.
1243 */
1244 if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1245 noop++;
1246 break;
1247 }
1248 }
1249
1250 /* Check if this is an empty board, i.e. no components */
1251 if (a->ncm == 0) {
1252 noop++;
1253 break;
1254 }
1255
1256 if ((rlist = rcm->rlist) == NULL) {
1257 rc = ap_rcm_rlist(a, rcm->firstcm, rcm->lastcm, &rlist,
1258 cmd);
1259 if ((rc == CFGA_OK) && (rlist != NULL) &&
1260 (rlist[0] != NULL)) {
1261 rcm->rlist = rlist;
1262 } else {
1263 /* Do not pass up empty resource list to RCM */
1264 noop++;
1265 break;
1266 }
1267 }
1268 for (nrsrc = 0; rlist[nrsrc] != NULL; nrsrc++)
1269 ap_msg(a, MSG_ISSUE, cmd, rlist[nrsrc]);
1270 if (cmd == CMD_RCM_OFFLINE)
1271 rv = (*rcm->request_offline_list)(hd, rlist, flags,
1272 &rinfo);
1273 else if (cmd == CMD_RCM_ONLINE)
1274 rv = (*rcm->notify_online_list)(hd, rlist,
1275 flags & ~RCM_FORCE, &rinfo);
1276 else
1277 rv = (*rcm->notify_remove_list)(hd, rlist,
1278 flags & ~RCM_FORCE, &rinfo);
1279 break;
1280 }
1281 case CMD_RCM_SUSPEND: {
1282 timespec_t t;
1283 t.tv_sec = (time_t)0;
1284 t.tv_nsec = (long)0;
1285 rsrc = OS;
1286 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1287 rv = (*rcm->request_suspend)(hd, rsrc, flags, &t, &rinfo);
1288 break;
1289 }
1290 case CMD_RCM_RESUME:
1291 rsrc = OS;
1292 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1293 rv = (*rcm->notify_resume)(hd, rsrc, 0, &rinfo);
1294 break;
1295 default:
1296 ap_err(a, ERR_CMD_INVAL, cmd);
1297 return (CFGA_INVAL);
1298 }
1299
1300 if (rv != RCM_SUCCESS) {
1301 rcm->rinfo = rinfo;
1302 rcm->infot = NULL;
1303 ap_err(a, ERR_RCM_CMD, cmd);
1304 (*rcm->free_info)(rinfo);
1305 if (rc == CFGA_OK)
1306 rc = CFGA_LIB_ERROR; /* make sure error is set */
1307 }
1308 if ((rc == CFGA_OK) && (noop == 0)) {
1309 if (rlist)
1310 for (i = 0; rlist[i]; i++)
1311 ap_msg(a, MSG_DONE, cmd, rlist[i]);
1312 else if (rsrc)
1313 ap_msg(a, MSG_DONE, cmd, rsrc);
1314 else
1315 ap_msg(a, MSG_DONE, cmd, a->target);
1316 }
1317
1318 return (rc);
1319 }
1320
1321 /*
1322 * ap_rcm_info
1323 *
1324 * Takes an ap_id and a character pointer, and formats
1325 * the rcm_info_t data in the form of a table to the given character pointer.
1326 * Code duplicated from the scsi plugin.
1327 * Note: This function will go away when a generic librcm callback is
1328 * implemented to format RCM messages for plugins.
1329 */
1330 int
ap_rcm_info(apd_t * a,char ** msg)1331 ap_rcm_info(apd_t *a, char **msg)
1332 {
1333 rcmd_t *rcm;
1334 rcm_info_t *rinfo;
1335 int i;
1336 size_t w;
1337 size_t width = 0;
1338 size_t w_rsrc = 0;
1339 size_t w_info = 0;
1340 size_t msg_size = 0;
1341 uint_t tuples = 0;
1342 rcm_info_tuple_t *tuple = NULL;
1343 char *rsrc;
1344 char *info;
1345 char *newmsg;
1346 static char format[RCM_MAX_FORMAT];
1347 const char *infostr;
1348
1349
1350 DBG("ap_rcm_info(%p)\n", (void *)a);
1351
1352 /* Protect against invalid arguments */
1353 if ((a == NULL) || ((rcm = (rcmd_t *)a->rcm) == NULL) ||
1354 ((rinfo = rcm->rinfo) == NULL) || (msg == NULL)) {
1355 return (-1);
1356 }
1357
1358 /* Set localized table header strings */
1359 rsrc = dgettext(TEXT_DOMAIN, "Resource");
1360 info = dgettext(TEXT_DOMAIN, "Information");
1361
1362 /* A first pass, to size up the RCM information */
1363 while (tuple = (*rcm->info_next)(rinfo, tuple)) {
1364 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1365 tuples++;
1366 if ((w = strlen((*rcm->info_rsrc)(tuple))) > w_rsrc)
1367 w_rsrc = w;
1368 if ((w = strlen(infostr)) > w_info)
1369 w_info = w;
1370 }
1371 }
1372
1373 /* If nothing was sized up above, stop early */
1374 if (tuples == 0)
1375 return (0);
1376
1377 /* Adjust column widths for column headings */
1378 if ((w = strlen(rsrc)) > w_rsrc)
1379 w_rsrc = w;
1380 else if ((w_rsrc - w) % 2)
1381 w_rsrc++;
1382 if ((w = strlen(info)) > w_info)
1383 w_info = w;
1384 else if ((w_info - w) % 2)
1385 w_info++;
1386
1387 /*
1388 * Compute the total line width of each line,
1389 * accounting for intercolumn spacing.
1390 */
1391 width = w_info + w_rsrc + 4;
1392
1393 /* Allocate space for the table */
1394 msg_size = (2 + tuples) * (width + 1) + 2;
1395 if (*msg == NULL) {
1396 /* zero fill for the strcat() call below */
1397 *msg = calloc(msg_size, sizeof (char));
1398 if (*msg == NULL)
1399 return (-1);
1400 } else {
1401 newmsg = realloc(*msg, strlen(*msg) + msg_size);
1402 if (newmsg == NULL)
1403 return (-1);
1404 else
1405 *msg = newmsg;
1406 }
1407
1408 /* Place a table header into the string */
1409
1410 /* The resource header */
1411 (void) strcat(*msg, "\n");
1412 w = strlen(rsrc);
1413 for (i = 0; i < ((w_rsrc - w) / 2); i++)
1414 (void) strcat(*msg, " ");
1415 (void) strcat(*msg, rsrc);
1416 for (i = 0; i < ((w_rsrc - w) / 2); i++)
1417 (void) strcat(*msg, " ");
1418
1419 /* The information header */
1420 (void) strcat(*msg, " ");
1421 w = strlen(info);
1422 for (i = 0; i < ((w_info - w) / 2); i++)
1423 (void) strcat(*msg, " ");
1424 (void) strcat(*msg, info);
1425 for (i = 0; i < ((w_info - w) / 2); i++)
1426 (void) strcat(*msg, " ");
1427
1428 /* Underline the headers */
1429 (void) strcat(*msg, "\n");
1430 for (i = 0; i < w_rsrc; i++)
1431 (void) strcat(*msg, "-");
1432 (void) strcat(*msg, " ");
1433 for (i = 0; i < w_info; i++)
1434 (void) strcat(*msg, "-");
1435
1436 /* Construct the format string */
1437 (void) snprintf(format, RCM_MAX_FORMAT, "%%-%ds %%-%ds",
1438 (int)w_rsrc, (int)w_info);
1439
1440 /* Add the tuples to the table string */
1441 tuple = NULL;
1442 while ((tuple = (*rcm->info_next)(rinfo, tuple)) != NULL) {
1443 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1444 (void) strcat(*msg, "\n");
1445 (void) sprintf(&((*msg)[strlen(*msg)]), format,
1446 (*rcm->info_rsrc)(tuple), infostr);
1447 }
1448 }
1449
1450 DBG("ap_rcm_info(%p) success\n", (void *)a);
1451 return (0);
1452 }
1453