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