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 if (nvl)
801 nvlist_free(nvl);
802 s_free(oldcpuids);
803 s_free(newcpuids);
804 return (rv);
805 }
806
807 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)808 ap_rcm_cap_mem(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
809 rcm_info_t **rinfo, int cmd, long change)
810 {
811 int rv;
812 int pgsize;
813 long oldpages;
814 long newpages;
815 long currpages;
816 char buf[32];
817 nvlist_t *nvl;
818
819 DBG("ap_rcm_cap_mem(%p)\n", (void *)a);
820
821 /*
822 * Get the current amount of configured memory.
823 */
824 if ((pgsize = sysconf(_SC_PAGE_SIZE)) == -1 ||
825 (currpages = sysconf(_SC_PHYS_PAGES)) == -1 ||
826 nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) > 0)
827 return (RCM_FAILURE);
828
829 /*
830 * If this is a (delete) request, change represents
831 * the amount of capacity that will be deleted from the
832 * system. If this is an (add) notification, change
833 * represents the amount of capacity that has already
834 * been added to the system.
835 */
836 if (cmd == CMD_RCM_CAP_DEL) {
837 oldpages = currpages;
838 newpages = currpages - change;
839 } else if (cmd == CMD_RCM_CAP_NOTIFY) {
840 newpages = currpages;
841 oldpages = rcm->cappages;
842 } else {
843 if (rcm->cappages == currpages) {
844 /* No real change in memory capacity */
845 DBG("ap_rcm_cap_mem: no change in capacity.\n");
846 nvlist_free(nvl);
847 return (RCM_SUCCESS);
848 }
849
850 oldpages = currpages - change;
851 newpages = currpages;
852 }
853
854 DBG("ap_rcm_cap_mem: Memory capacity, old = %ld, new = %ld\n",
855 oldpages, newpages);
856
857 if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
858 nvlist_add_int32(nvl, "page_size", pgsize) != 0 ||
859 nvlist_add_int32(nvl, "old_pages", oldpages) != 0 ||
860 nvlist_add_int32(nvl, "new_pages", newpages) != 0) {
861 nvlist_free(nvl);
862 return (RCM_FAILURE);
863 }
864
865 (void) snprintf(buf, sizeof (buf), "(%ld pages)", change);
866 ap_msg(a, MSG_ISSUE, cmd, buf);
867
868 if (cmd == CMD_RCM_CAP_DEL) {
869 rv = (*rcm->request_capacity_change)(hd, "SUNW_memory",
870 flags, nvl, rinfo);
871 } else {
872 rv = (*rcm->notify_capacity_change)(hd, "SUNW_memory",
873 flags & ~RCM_FORCE, nvl, rinfo);
874 }
875
876 nvlist_free(nvl);
877
878 return (rv);
879 }
880
881 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)882 ap_rcm_request_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
883 int *rv, uint_t flags, rcm_info_t **rinfo)
884 {
885 int cm;
886 int ncpus;
887 long npages;
888 cap_info_t *capinfo;
889 ap_target_t type;
890
891 DBG("ap_rcm_request_cap(%p)\n", (void *)a);
892
893 if ((capinfo = rcm->capinfo) == NULL) {
894 ap_err(a, ERR_PLUGIN, "null capinfo");
895 return (CFGA_LIB_ERROR);
896 }
897
898 ncpus = npages = 0;
899
900 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
901 int i, j;
902
903 /*
904 * See if the request target is a single
905 * (default) component
906 */
907 i = (cm == CM_DFLT) ? 0 : cm;
908
909 /*
910 * We are interested only in those components
911 * in the configured state since they represent
912 * available capacity.
913 */
914 type = ap_cm_type(a, cm);
915 if (capinfo[i].valid == 0 ||
916 capinfo[i].ostate != CFGA_STAT_CONFIGURED)
917 continue;
918 else if ((type == AP_CPU) || (type == AP_CMP)) {
919 for (j = 0; j < capinfo[i].ncap; j++) {
920 rcm->cpuids[ncpus++] = capinfo[i].type.cpuid[j];
921 }
922 } else if (type == AP_MEM)
923 npages += capinfo[i].type.npages;
924 }
925
926 if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
927 CMD_RCM_CAP_DEL, ncpus)) != RCM_SUCCESS)) {
928 return (CFGA_LIB_ERROR);
929 }
930 if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
931 CMD_RCM_CAP_DEL, npages)) != RCM_SUCCESS)) {
932 return (CFGA_LIB_ERROR);
933 }
934
935 return (CFGA_OK);
936 }
937
938 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)939 ap_rcm_add_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
940 int *rv, uint_t flags, rcm_info_t **rinfo)
941 {
942 int cm;
943 int ncpus;
944 long npages;
945 cap_info_t *capinfo, *prevcapinfo;
946 cfga_err_t rc;
947
948 DBG("ap_rcm_add_cap(%p)\n", (void *)a);
949
950 /* Get the new capacity info to figure out what has changed */
951 if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
952 CFGA_OK)
953 return (rc);
954
955 if (capinfo == NULL) {
956 DBG("no pertinent capacity info\n");
957 return (CFGA_OK);
958 }
959
960 ncpus = npages = 0;
961 prevcapinfo = rcm->capinfo;
962
963 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
964 int i, j;
965 cfga_stat_t os, prevos;
966 int prevvalidity;
967 ap_target_t type;
968
969 /*
970 * See if the request target is a single
971 * (default) component
972 */
973 i = cm == CM_DFLT ? 0 : cm;
974
975 os = capinfo[i].ostate;
976 if (prevcapinfo == NULL) {
977 prevos = CFGA_STAT_EMPTY;
978 prevvalidity = 1;
979 } else {
980 prevos = prevcapinfo[i].ostate;
981 prevvalidity = prevcapinfo[i].valid;
982 }
983
984 type = ap_cm_type(a, cm);
985
986 DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
987 cm, prevvalidity, type, prevos, os);
988
989 /*
990 * We are interested only in those components
991 * whose states have changed to configured as
992 * the result of the current cfgadm request.
993 */
994 if (prevvalidity == 0 || os != CFGA_STAT_CONFIGURED) {
995 capinfo[i].valid = 0;
996 continue;
997 } else if (prevos != CFGA_STAT_CONFIGURED) {
998 /*
999 * The occupant state is configured, and
1000 * the previous occupant state was not.
1001 */
1002 if ((type == AP_CPU) || (type == AP_CMP)) {
1003 for (j = 0; j < capinfo[i].ncap; j++) {
1004 rcm->cpuids[ncpus++] =
1005 capinfo[i].type.cpuid[j];
1006 }
1007 } else if (type == AP_MEM)
1008 npages += capinfo[i].type.npages;
1009 }
1010 }
1011 free(capinfo);
1012
1013 if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1014 CMD_RCM_CAP_ADD, ncpus)) != RCM_SUCCESS)) {
1015 return (CFGA_LIB_ERROR);
1016 }
1017 if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1018 CMD_RCM_CAP_ADD, npages)) != RCM_SUCCESS)) {
1019 return (CFGA_LIB_ERROR);
1020 }
1021
1022 return (CFGA_OK);
1023 }
1024
1025 /*
1026 * ap_rcm_notify_cap:
1027 *
1028 * This routine handles the CMD_RCM_CAP_NOTIFY command. It
1029 * is called after a successful/failed DR unconfigure
1030 * operation. It filters out components that have changed
1031 * and passes this information on to ap_rcm_cap_{cpu,mem}.
1032 *
1033 * ap_rcm_cap_{cpu,mem} will still be called if all the
1034 * components have not changed and at least one {cpu,mem}
1035 * component was originally configured.
1036 */
1037 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)1038 ap_rcm_notify_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
1039 int *rv, uint_t flags, rcm_info_t **rinfo)
1040 {
1041 cfga_err_t rc;
1042 cap_info_t *capinfo;
1043 cap_info_t *prevcapinfo;
1044 int cm;
1045 long npages = 0;
1046 int ncpus = 0;
1047 int prev_mem = 0; /* # of prev. configured mem components */
1048 int prev_cpus = 0; /* # of prev. configured CPUs */
1049
1050 DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
1051
1052 /* Get the new capacity info to figure out what has changed */
1053 if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
1054 CFGA_OK)
1055 return (rc);
1056
1057 if (capinfo == NULL) {
1058 DBG("no pertinent capacity info\n");
1059 return (CFGA_OK);
1060 }
1061
1062 /* The original capacity info */
1063 prevcapinfo = rcm->capinfo;
1064
1065 /*
1066 * Cycle through all components that we are operating
1067 * on. Record which components' occupant states have
1068 * changed.
1069 */
1070 for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
1071 int i;
1072 cfga_stat_t prevos, os;
1073 ap_target_t type;
1074 int prev_conf = 0;
1075 int now_conf = 0;
1076
1077 /*
1078 * See if the request target is a single
1079 * (default) component
1080 */
1081 i = cm == CM_DFLT ? 0 : cm;
1082
1083 os = capinfo[i].ostate;
1084
1085 if (prevcapinfo == NULL) {
1086 prevos = CFGA_STAT_EMPTY;
1087 } else {
1088 prevos = prevcapinfo[i].ostate;
1089 if (prevcapinfo[i].valid == 0) {
1090 DBG("ap_rcm_notify_cap: skipping component "
1091 "due to prevvalidity == 0\n");
1092 continue;
1093 }
1094 }
1095
1096 type = ap_cm_type(a, cm);
1097
1098 prev_conf = (prevos == CFGA_STAT_CONFIGURED);
1099 now_conf = (os == CFGA_STAT_CONFIGURED);
1100
1101 /*
1102 * Build up rcm->cpuids with the IDs of CPUs that
1103 * have been removed. Record the number of removed
1104 * CPUs and pages.
1105 */
1106 if (type == AP_CPU || type == AP_CMP) {
1107 if (prev_conf)
1108 prev_cpus++;
1109 if (prev_conf && !now_conf) {
1110 int j;
1111 for (j = 0; j < capinfo[i].ncap; j++) {
1112 rcm->cpuids[ncpus++] =
1113 capinfo[i].type.cpuid[j];
1114 }
1115 }
1116 } else if (type == AP_MEM) {
1117 if (prev_conf)
1118 prev_mem++;
1119 if (prev_conf && !now_conf)
1120 npages += capinfo[i].type.npages;
1121 }
1122 }
1123 free(capinfo);
1124
1125 /*
1126 * If any CPU or memory components were operated on,
1127 * successfully or not, the rcm_notify_capacity_change()
1128 * routine must be called.
1129 */
1130
1131 if (prev_cpus) {
1132 *rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1133 CMD_RCM_CAP_NOTIFY, ncpus);
1134
1135 if (*rv != RCM_SUCCESS)
1136 return (CFGA_LIB_ERROR);
1137 }
1138
1139 if (prev_mem) {
1140 *rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1141 CMD_RCM_CAP_NOTIFY, npages);
1142
1143 if (*rv != RCM_SUCCESS)
1144 return (CFGA_LIB_ERROR);
1145 }
1146
1147 return (CFGA_OK);
1148 }
1149
1150 cfga_err_t
ap_rcm_ctl(apd_t * a,int cmd)1151 ap_rcm_ctl(apd_t *a, int cmd)
1152 {
1153 int i;
1154 int rv;
1155 int noop;
1156 int ncpus;
1157 int cm;
1158 uint_t flags;
1159 char *rsrc;
1160 char **rlist;
1161 rcmd_t *rcm;
1162 rcm_info_t *rinfo;
1163 rcm_handle_t *hd;
1164 cfga_err_t rc;
1165 cpuid_t *growcpuids;
1166
1167 DBG("ap_rcm_ctl(%p)\n", (void *)a);
1168
1169 if ((rcm = (rcmd_t *)a->rcm) == NULL) {
1170 ap_msg(a, MSG_SKIP, cmd, a->target);
1171 return (CFGA_OK);
1172 }
1173
1174 hd = rcm->hd;
1175 rv = RCM_SUCCESS;
1176 rc = CFGA_OK;
1177 if (ap_getopt(a, OPT_FORCE))
1178 flags = RCM_FORCE;
1179 else
1180 flags = 0;
1181 rinfo = NULL;
1182 rlist = NULL;
1183 rsrc = NULL;
1184 noop = 0;
1185
1186 switch (cmd) {
1187 case CMD_RCM_CAP_DEL:
1188 if (rcm->capinfo == NULL)
1189 noop++;
1190 else
1191 rc = ap_rcm_request_cap(a, rcm, hd, &rv, flags, &rinfo);
1192 break;
1193 case CMD_RCM_CAP_ADD:
1194 rc = ap_rcm_add_cap(a, rcm, hd, &rv, flags, &rinfo);
1195 break;
1196 case CMD_RCM_CAP_NOTIFY:
1197 rc = ap_rcm_notify_cap(a, rcm, hd, &rv, flags, &rinfo);
1198 break;
1199 case CMD_RCM_ONLINE:
1200 /* Refresh changed component states */
1201 if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1202 noop++;
1203 break;
1204 }
1205
1206 if (a->tgt == AP_BOARD) {
1207 rcm->firstcm = 0;
1208 rcm->lastcm = a->ncm - 1;
1209
1210 /* Check if we need to grow our cpuids list */
1211 for (ncpus = 0, cm = rcm->firstcm; cm <= rcm->lastcm;
1212 cm++) {
1213 ap_target_t type = ap_cm_type(a, cm);
1214 if ((type == AP_CPU) || (type == AP_CMP))
1215 ncpus += ap_cm_ncap(a, cm);
1216 }
1217
1218 if (rcm->ncpus < ncpus) {
1219 if ((growcpuids =
1220 (cpuid_t *)realloc(rcm->cpuids,
1221 (ncpus * sizeof (cpuid_t)))) == NULL) {
1222 ap_err(a, ERR_NOMEM);
1223 return (CFGA_LIB_ERROR);
1224 }
1225 rcm->ncpus = ncpus;
1226 rcm->cpuids = growcpuids;
1227 }
1228
1229 } else {
1230 rcm->firstcm = CM_DFLT;
1231 rcm->lastcm = CM_DFLT;
1232 }
1233
1234 /*FALLTHROUGH*/
1235
1236 case CMD_RCM_OFFLINE:
1237 case CMD_RCM_REMOVE: {
1238 uint_t nrsrc;
1239
1240 if (cmd == CMD_RCM_REMOVE) {
1241 /*
1242 * An unconfigure has just taken place, so
1243 * refresh the changed component states.
1244 */
1245 if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1246 noop++;
1247 break;
1248 }
1249 }
1250
1251 /* Check if this is an empty board, i.e. no components */
1252 if (a->ncm == 0) {
1253 noop++;
1254 break;
1255 }
1256
1257 if ((rlist = rcm->rlist) == NULL) {
1258 rc = ap_rcm_rlist(a, rcm->firstcm, rcm->lastcm, &rlist,
1259 cmd);
1260 if ((rc == CFGA_OK) && (rlist != NULL) &&
1261 (rlist[0] != NULL)) {
1262 rcm->rlist = rlist;
1263 } else {
1264 /* Do not pass up empty resource list to RCM */
1265 noop++;
1266 break;
1267 }
1268 }
1269 for (nrsrc = 0; rlist[nrsrc] != NULL; nrsrc++)
1270 ap_msg(a, MSG_ISSUE, cmd, rlist[nrsrc]);
1271 if (cmd == CMD_RCM_OFFLINE)
1272 rv = (*rcm->request_offline_list)(hd, rlist, flags,
1273 &rinfo);
1274 else if (cmd == CMD_RCM_ONLINE)
1275 rv = (*rcm->notify_online_list)(hd, rlist,
1276 flags & ~RCM_FORCE, &rinfo);
1277 else
1278 rv = (*rcm->notify_remove_list)(hd, rlist,
1279 flags & ~RCM_FORCE, &rinfo);
1280 break;
1281 }
1282 case CMD_RCM_SUSPEND: {
1283 timespec_t t;
1284 t.tv_sec = (time_t)0;
1285 t.tv_nsec = (long)0;
1286 rsrc = OS;
1287 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1288 rv = (*rcm->request_suspend)(hd, rsrc, flags, &t, &rinfo);
1289 break;
1290 }
1291 case CMD_RCM_RESUME:
1292 rsrc = OS;
1293 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1294 rv = (*rcm->notify_resume)(hd, rsrc, 0, &rinfo);
1295 break;
1296 default:
1297 ap_err(a, ERR_CMD_INVAL, cmd);
1298 return (CFGA_INVAL);
1299 }
1300
1301 if (rv != RCM_SUCCESS) {
1302 rcm->rinfo = rinfo;
1303 rcm->infot = NULL;
1304 ap_err(a, ERR_RCM_CMD, cmd);
1305 (*rcm->free_info)(rinfo);
1306 if (rc == CFGA_OK)
1307 rc = CFGA_LIB_ERROR; /* make sure error is set */
1308 }
1309 if ((rc == CFGA_OK) && (noop == 0)) {
1310 if (rlist)
1311 for (i = 0; rlist[i]; i++)
1312 ap_msg(a, MSG_DONE, cmd, rlist[i]);
1313 else if (rsrc)
1314 ap_msg(a, MSG_DONE, cmd, rsrc);
1315 else
1316 ap_msg(a, MSG_DONE, cmd, a->target);
1317 }
1318
1319 return (rc);
1320 }
1321
1322 /*
1323 * ap_rcm_info
1324 *
1325 * Takes an ap_id and a character pointer, and formats
1326 * the rcm_info_t data in the form of a table to the given character pointer.
1327 * Code duplicated from the scsi plugin.
1328 * Note: This function will go away when a generic librcm callback is
1329 * implemented to format RCM messages for plugins.
1330 */
1331 int
ap_rcm_info(apd_t * a,char ** msg)1332 ap_rcm_info(apd_t *a, char **msg)
1333 {
1334 rcmd_t *rcm;
1335 rcm_info_t *rinfo;
1336 int i;
1337 size_t w;
1338 size_t width = 0;
1339 size_t w_rsrc = 0;
1340 size_t w_info = 0;
1341 size_t msg_size = 0;
1342 uint_t tuples = 0;
1343 rcm_info_tuple_t *tuple = NULL;
1344 char *rsrc;
1345 char *info;
1346 char *newmsg;
1347 static char format[RCM_MAX_FORMAT];
1348 const char *infostr;
1349
1350
1351 DBG("ap_rcm_info(%p)\n", (void *)a);
1352
1353 /* Protect against invalid arguments */
1354 if ((a == NULL) || ((rcm = (rcmd_t *)a->rcm) == NULL) ||
1355 ((rinfo = rcm->rinfo) == NULL) || (msg == NULL)) {
1356 return (-1);
1357 }
1358
1359 /* Set localized table header strings */
1360 rsrc = dgettext(TEXT_DOMAIN, "Resource");
1361 info = dgettext(TEXT_DOMAIN, "Information");
1362
1363 /* A first pass, to size up the RCM information */
1364 while (tuple = (*rcm->info_next)(rinfo, tuple)) {
1365 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1366 tuples++;
1367 if ((w = strlen((*rcm->info_rsrc)(tuple))) > w_rsrc)
1368 w_rsrc = w;
1369 if ((w = strlen(infostr)) > w_info)
1370 w_info = w;
1371 }
1372 }
1373
1374 /* If nothing was sized up above, stop early */
1375 if (tuples == 0)
1376 return (0);
1377
1378 /* Adjust column widths for column headings */
1379 if ((w = strlen(rsrc)) > w_rsrc)
1380 w_rsrc = w;
1381 else if ((w_rsrc - w) % 2)
1382 w_rsrc++;
1383 if ((w = strlen(info)) > w_info)
1384 w_info = w;
1385 else if ((w_info - w) % 2)
1386 w_info++;
1387
1388 /*
1389 * Compute the total line width of each line,
1390 * accounting for intercolumn spacing.
1391 */
1392 width = w_info + w_rsrc + 4;
1393
1394 /* Allocate space for the table */
1395 msg_size = (2 + tuples) * (width + 1) + 2;
1396 if (*msg == NULL) {
1397 /* zero fill for the strcat() call below */
1398 *msg = calloc(msg_size, sizeof (char));
1399 if (*msg == NULL)
1400 return (-1);
1401 } else {
1402 newmsg = realloc(*msg, strlen(*msg) + msg_size);
1403 if (newmsg == NULL)
1404 return (-1);
1405 else
1406 *msg = newmsg;
1407 }
1408
1409 /* Place a table header into the string */
1410
1411 /* The resource header */
1412 (void) strcat(*msg, "\n");
1413 w = strlen(rsrc);
1414 for (i = 0; i < ((w_rsrc - w) / 2); i++)
1415 (void) strcat(*msg, " ");
1416 (void) strcat(*msg, rsrc);
1417 for (i = 0; i < ((w_rsrc - w) / 2); i++)
1418 (void) strcat(*msg, " ");
1419
1420 /* The information header */
1421 (void) strcat(*msg, " ");
1422 w = strlen(info);
1423 for (i = 0; i < ((w_info - w) / 2); i++)
1424 (void) strcat(*msg, " ");
1425 (void) strcat(*msg, info);
1426 for (i = 0; i < ((w_info - w) / 2); i++)
1427 (void) strcat(*msg, " ");
1428
1429 /* Underline the headers */
1430 (void) strcat(*msg, "\n");
1431 for (i = 0; i < w_rsrc; i++)
1432 (void) strcat(*msg, "-");
1433 (void) strcat(*msg, " ");
1434 for (i = 0; i < w_info; i++)
1435 (void) strcat(*msg, "-");
1436
1437 /* Construct the format string */
1438 (void) snprintf(format, RCM_MAX_FORMAT, "%%-%ds %%-%ds",
1439 (int)w_rsrc, (int)w_info);
1440
1441 /* Add the tuples to the table string */
1442 tuple = NULL;
1443 while ((tuple = (*rcm->info_next)(rinfo, tuple)) != NULL) {
1444 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1445 (void) strcat(*msg, "\n");
1446 (void) sprintf(&((*msg)[strlen(*msg)]), format,
1447 (*rcm->info_rsrc)(tuple), infostr);
1448 }
1449 }
1450
1451 DBG("ap_rcm_info(%p) success\n", (void *)a);
1452 return (0);
1453 }
1454