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
27 #include "cfga_fp.h"
28
29 static fpcfga_ret_t fp_rcm_init(char *, cfga_flags_t, char **, uint_t *,
30 char **rsrc_fixed);
31 static int fp_rcm_process_node(di_node_t, void *);
32 static fpcfga_ret_t fp_rcm_info_table(rcm_info_t *, char **);
33 static char *chop_minor(char *);
34
35 #define MAX_FORMAT 80
36 #define DEVICES "/devices"
37
38 typedef struct {
39 char *bus_path;
40 char *filter;
41 char **errstring;
42 fpcfga_ret_t ret;
43 cfga_flags_t flags;
44 fpcfga_ret_t (*func)(char *, char *, char **, cfga_flags_t);
45 } walkargs_t;
46
47 static fpcfga_ret_t fp_rcm_info_table(rcm_info_t *, char **);
48 static int fp_rcm_process_node(di_node_t, void *);
49 static fpcfga_ret_t fp_rcm_init(char *, cfga_flags_t, char **, uint_t *,
50 char **);
51 static char *chop_minor(char *);
52
53 static rcm_handle_t *rcm_handle = NULL;
54 static mutex_t rcm_handle_lock;
55
56 /*
57 * fp_rcm_offline()
58 *
59 * Offline FP resource consumers.
60 */
61 fpcfga_ret_t
fp_rcm_offline(char * rsrc,char ** errstring,cfga_flags_t flags)62 fp_rcm_offline(char *rsrc, char **errstring, cfga_flags_t flags)
63 {
64 int rret;
65 uint_t rflags = 0;
66 char *rsrc_fixed;
67 rcm_info_t *rinfo = NULL;
68 fpcfga_ret_t ret = FPCFGA_OK;
69
70 if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed))
71 != FPCFGA_OK)
72 return (ret);
73
74 if ((rret = rcm_request_offline(rcm_handle, rsrc_fixed, rflags, &rinfo))
75 != RCM_SUCCESS) {
76 cfga_err(errstring, 0, ERRARG_RCM_OFFLINE, rsrc_fixed, 0);
77 if (rinfo) {
78 (void) fp_rcm_info_table(rinfo, errstring);
79 rcm_free_info(rinfo);
80 }
81 if (rret == RCM_FAILURE)
82 (void) fp_rcm_online(rsrc, errstring, flags);
83 ret = FPCFGA_BUSY;
84 }
85
86 S_FREE(rsrc_fixed);
87
88 return (ret);
89 }
90
91 /*
92 * fp_rcm_online()
93 *
94 * Online FP resource consumers that were previously offlined.
95 */
96 fpcfga_ret_t
fp_rcm_online(char * rsrc,char ** errstring,cfga_flags_t flags)97 fp_rcm_online(char *rsrc, char **errstring, cfga_flags_t flags)
98 {
99 char *rsrc_fixed;
100 rcm_info_t *rinfo = NULL;
101 fpcfga_ret_t ret = FPCFGA_OK;
102
103 if ((ret = fp_rcm_init(rsrc, flags, errstring, NULL, &rsrc_fixed))
104 != FPCFGA_OK)
105 return (ret);
106
107 if (rcm_notify_online(rcm_handle, rsrc_fixed, 0, &rinfo)
108 != RCM_SUCCESS && rinfo != NULL) {
109 cfga_err(errstring, 0, ERRARG_RCM_ONLINE, rsrc_fixed, 0);
110 (void) fp_rcm_info_table(rinfo, errstring);
111 rcm_free_info(rinfo);
112 ret = FPCFGA_ERR;
113 }
114
115 S_FREE(rsrc_fixed);
116
117 return (ret);
118 }
119
120 /*
121 * fp_rcm_remove()
122 *
123 * Remove FP resource consumers after their kernel removal.
124 */
125 fpcfga_ret_t
fp_rcm_remove(char * rsrc,char ** errstring,cfga_flags_t flags)126 fp_rcm_remove(char *rsrc, char **errstring, cfga_flags_t flags)
127 {
128 char *rsrc_fixed;
129 rcm_info_t *rinfo = NULL;
130 fpcfga_ret_t ret = FPCFGA_OK;
131
132 if ((ret = fp_rcm_init(rsrc, flags, errstring, NULL, &rsrc_fixed))
133 != FPCFGA_OK)
134 return (ret);
135
136 if (rcm_notify_remove(rcm_handle, rsrc_fixed, 0, &rinfo)
137 != RCM_SUCCESS) {
138 cfga_err(errstring, 0, ERRARG_RCM_REMOVE, rsrc_fixed, 0);
139 if (rinfo) {
140 (void) fp_rcm_info_table(rinfo, errstring);
141 rcm_free_info(rinfo);
142 }
143 ret = FPCFGA_ERR;
144 }
145
146 S_FREE(rsrc_fixed);
147
148 return (ret);
149 }
150
151 /*
152 * fp_rcm_suspend()
153 *
154 * Suspend FP resource consumers before a bus quiesce.
155 */
156 fpcfga_ret_t
fp_rcm_suspend(char * rsrc,char * filter,char ** errstring,cfga_flags_t flags)157 fp_rcm_suspend(char *rsrc, char *filter, char **errstring, cfga_flags_t flags)
158 {
159 int rret;
160 uint_t rflags = 0;
161 char *rsrc_fixed;
162 char *filter_fixed;
163 char *rsrc_devpath;
164 rcm_info_t *rinfo = NULL;
165 di_node_t node;
166 fpcfga_ret_t ret = FPCFGA_OK;
167 walkargs_t walkargs;
168 timespec_t zerotime = { 0, 0 };
169
170 if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed))
171 != FPCFGA_OK)
172 return (ret);
173
174 /* If a filter is provided, ensure that it makes sense */
175 if (filter != NULL && strstr(filter, rsrc) != filter) {
176 S_FREE(rsrc_fixed);
177 cfga_err(errstring, 0, ERR_APID_INVAL, 0);
178 return (FPCFGA_ERR);
179 }
180
181 /*
182 * If no filter is specified: attempt a suspension on the resource,
183 * directly.
184 */
185 if (filter == NULL) {
186 if ((rret = rcm_request_suspend(rcm_handle, rsrc_fixed, rflags,
187 &zerotime, &rinfo)) != RCM_SUCCESS) {
188 cfga_err(errstring, 0, ERRARG_RCM_SUSPEND, rsrc_fixed,
189 0);
190 if (rinfo) {
191 (void) fp_rcm_info_table(rinfo, errstring);
192 rcm_free_info(rinfo);
193 }
194 if (rret == RCM_FAILURE)
195 (void) fp_rcm_resume(rsrc, filter, errstring,
196 (flags & (~CFGA_FLAG_FORCE)));
197 ret = FPCFGA_BUSY;
198 }
199 S_FREE(rsrc_fixed);
200 return (ret);
201 }
202
203 /*
204 * If a filter is specified: open the resource with libdevinfo, walk
205 * through its nodes, and attempt a suspension of each node that
206 * mismatches the filter.
207 */
208
209 /* Chop off the filter's minor name */
210 if ((filter_fixed = chop_minor(filter)) == NULL)
211 return (FPCFGA_ERR);
212
213 /* get a libdevinfo snapshot of the resource's subtree */
214 rsrc_devpath = rsrc_fixed;
215 if (strstr(rsrc_fixed, DEVICES) != NULL)
216 rsrc_devpath += strlen(DEVICES);
217 node = di_init(rsrc_devpath, DINFOSUBTREE | DINFOMINOR);
218 if (node == DI_NODE_NIL) {
219 cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0);
220 ret = FPCFGA_ERR;
221 }
222
223 /* apply the filter, and suspend all resources not filtered out */
224 if (ret == FPCFGA_OK) {
225
226 walkargs.bus_path = rsrc_fixed;
227 walkargs.filter = filter_fixed;
228 walkargs.errstring = errstring;
229 walkargs.ret = FPCFGA_OK;
230 walkargs.flags = rflags;
231 walkargs.func = fp_rcm_suspend;
232
233 if (di_walk_node(node, 0, &walkargs, fp_rcm_process_node) < 0)
234 cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0);
235
236 ret = walkargs.ret;
237 }
238
239 if (node != DI_NODE_NIL)
240 di_fini(node);
241
242 S_FREE(rsrc_fixed);
243 S_FREE(filter_fixed);
244
245 if (ret != FPCFGA_OK)
246 (void) fp_rcm_resume(rsrc, filter, errstring,
247 (flags & (~CFGA_FLAG_FORCE)));
248
249 return (ret);
250 }
251
252 /*
253 * fp_rcm_resume()
254 *
255 * Resume FP resource consumers after a bus has been unquiesced.
256 */
257 fpcfga_ret_t
fp_rcm_resume(char * rsrc,char * filter,char ** errstring,cfga_flags_t flags)258 fp_rcm_resume(char *rsrc, char *filter, char **errstring, cfga_flags_t flags)
259 {
260 uint_t rflags = 0;
261 char *rsrc_fixed;
262 char *filter_fixed;
263 char *rsrc_devpath;
264 rcm_info_t *rinfo = NULL;
265 di_node_t node;
266 fpcfga_ret_t ret = FPCFGA_OK;
267 walkargs_t walkargs;
268
269 if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed))
270 != FPCFGA_OK)
271 return (ret);
272
273 /* If a filter is provided, ensure that it makes sense */
274 if (filter != NULL && strstr(filter, rsrc) != filter) {
275 S_FREE(rsrc_fixed);
276 cfga_err(errstring, 0, ERR_APID_INVAL, 0);
277 return (FPCFGA_ERR);
278 }
279
280 /*
281 * If no filter is specified: resume the resource directly.
282 */
283 if (filter == NULL) {
284 if (rcm_notify_resume(rcm_handle, rsrc_fixed, rflags, &rinfo)
285 != RCM_SUCCESS && rinfo != NULL) {
286 cfga_err(errstring, 0, ERRARG_RCM_RESUME, rsrc_fixed,
287 0);
288 (void) fp_rcm_info_table(rinfo, errstring);
289 rcm_free_info(rinfo);
290 ret = FPCFGA_BUSY;
291 }
292 S_FREE(rsrc_fixed);
293 return (ret);
294 }
295
296 /*
297 * If a filter is specified: open the resource with libdevinfo, walk
298 * through its nodes, and resume each of its nodes that mismatches
299 * the filter.
300 */
301
302 /* Chop off the filter's minor name */
303 if ((filter_fixed = chop_minor(filter)) == NULL)
304 return (FPCFGA_ERR);
305
306 /* get a libdevinfo snapshot of the resource's subtree */
307 rsrc_devpath = rsrc_fixed;
308 if (strstr(rsrc_fixed, DEVICES) != NULL)
309 rsrc_devpath += strlen(DEVICES);
310 node = di_init(rsrc_devpath, DINFOSUBTREE | DINFOMINOR);
311 if (node == DI_NODE_NIL) {
312 cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0);
313 ret = FPCFGA_ERR;
314 }
315
316 /* apply the filter, and resume all resources not filtered out */
317 if (ret == FPCFGA_OK) {
318
319 walkargs.bus_path = rsrc_fixed;
320 walkargs.filter = filter_fixed;
321 walkargs.errstring = errstring;
322 walkargs.ret = FPCFGA_OK;
323 walkargs.flags = rflags;
324 walkargs.func = fp_rcm_resume;
325
326 if (di_walk_node(node, 0, &walkargs, fp_rcm_process_node) < 0)
327 cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0);
328
329 ret = walkargs.ret;
330 }
331
332 if (node != DI_NODE_NIL)
333 di_fini(node);
334
335 S_FREE(rsrc_fixed);
336 S_FREE(filter_fixed);
337
338 return (ret);
339 }
340
341 /*
342 * fp_rcm_info
343 *
344 * Queries RCM information for resources, and formats it into a table.
345 * The table is appended to the info argument. If the info argument is a
346 * null pointer, then a new string is malloc'ed. If the info argument is
347 * not a null pointer, then it is realloc'ed to the required size.
348 */
349 fpcfga_ret_t
fp_rcm_info(char * rsrc,char ** errstring,char ** info)350 fp_rcm_info(char *rsrc, char **errstring, char **info)
351 {
352 char *rsrc_fixed;
353 rcm_info_t *rinfo = NULL;
354 fpcfga_ret_t ret = FPCFGA_OK;
355
356 if ((ret = fp_rcm_init(rsrc, 0, errstring, NULL, &rsrc_fixed))
357 != FPCFGA_OK)
358 return (ret);
359
360 if (info == NULL) {
361 S_FREE(rsrc_fixed);
362 return (FPCFGA_ERR);
363 }
364
365 if (rcm_get_info(rcm_handle, rsrc_fixed, 0, &rinfo)
366 != RCM_SUCCESS) {
367 cfga_err(errstring, 0, ERRARG_RCM_INFO, rsrc_fixed, 0);
368 ret = FPCFGA_ERR;
369 } else if (rinfo == NULL)
370 ret = FPCFGA_OK;
371
372 if (rinfo) {
373 if ((ret = fp_rcm_info_table(rinfo, info)) != FPCFGA_OK)
374 cfga_err(errstring, 0, ERRARG_RCM_INFO, rsrc_fixed, 0);
375 rcm_free_info(rinfo);
376 }
377
378 S_FREE(rsrc_fixed);
379
380 return (ret);
381 }
382
383 /*
384 * fp_rcm_init()
385 *
386 * Contains common initialization code for entering a fp_rcm_xx()
387 * routine.
388 */
389 static fpcfga_ret_t
fp_rcm_init(char * rsrc,cfga_flags_t flags,char ** errstring,uint_t * rflags,char ** rsrc_fixed)390 fp_rcm_init(char *rsrc, cfga_flags_t flags, char **errstring, uint_t *rflags,
391 char **rsrc_fixed)
392 {
393 /* Validate the rsrc argument */
394 if (rsrc == NULL) {
395 cfga_err(errstring, 0, ERR_APID_INVAL, 0);
396 return (FPCFGA_ERR);
397 }
398
399 /* Translate the cfgadm flags to RCM flags */
400 if (rflags && (flags & CFGA_FLAG_FORCE))
401 *rflags |= RCM_FORCE;
402
403 /* Get a handle for the RCM operations */
404 (void) mutex_lock(&rcm_handle_lock);
405 if (rcm_handle == NULL) {
406 if (rcm_alloc_handle(NULL, RCM_NOPID, NULL, &rcm_handle) !=
407 RCM_SUCCESS) {
408 cfga_err(errstring, 0, ERR_RCM_HANDLE, 0);
409 (void) mutex_unlock(&rcm_handle_lock);
410 return (FPCFGA_LIB_ERR);
411 }
412 }
413 (void) mutex_unlock(&rcm_handle_lock);
414
415 /* Chop off the rsrc's minor, if it has one */
416 if ((*rsrc_fixed = chop_minor(rsrc)) == NULL)
417 return (FPCFGA_ERR);
418
419 return (FPCFGA_OK);
420 }
421
422 /*
423 * fp_rcm_process_node
424 *
425 * Helper routine for fp_rcm_{suspend,resume}. This is a di_walk_node()
426 * callback that will apply a filter to every node it sees, and either suspend
427 * or resume it if it doesn't match the filter.
428 */
429 static int
fp_rcm_process_node(di_node_t node,void * argp)430 fp_rcm_process_node(di_node_t node, void *argp)
431 {
432 char *devfs_path;
433 walkargs_t *walkargs;
434 fpcfga_ret_t ret = FPCFGA_OK;
435 char disk_path[MAXPATHLEN];
436
437 /* Guard against bad arguments */
438 if ((walkargs = (walkargs_t *)argp) == NULL)
439 return (DI_WALK_TERMINATE);
440 if (walkargs->filter == NULL || walkargs->errstring == NULL) {
441 walkargs->ret = FPCFGA_ERR;
442 return (DI_WALK_TERMINATE);
443 }
444
445 /* If the node has no minors, then skip it */
446 if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL)
447 return (DI_WALK_CONTINUE);
448
449 /* Construct the devices path */
450 if ((devfs_path = di_devfs_path(node)) == NULL)
451 return (DI_WALK_CONTINUE);
452 (void) snprintf(disk_path, MAXPATHLEN, "%s%s", DEVICES, devfs_path);
453 di_devfs_path_free(devfs_path);
454
455 /*
456 * If the node does not correspond to the targeted FP bus or the
457 * disk being filtered out, then use the appropriate suspend/resume
458 * function.
459 */
460 if (strcmp(disk_path, walkargs->bus_path) != 0 &&
461 strcmp(disk_path, walkargs->filter) != 0)
462 ret = (*walkargs->func)(disk_path, NULL, walkargs->errstring,
463 walkargs->flags);
464
465 /* Stop the walk early if the above operation failed */
466 if (ret != FPCFGA_OK) {
467 walkargs->ret = ret;
468 return (DI_WALK_TERMINATE);
469 }
470
471 return (DI_WALK_CONTINUE);
472 }
473
474 /*
475 * fp_rcm_info_table
476 *
477 * Takes an opaque rcm_info_t pointer and a character pointer, and appends
478 * the rcm_info_t data in the form of a table to the given character pointer.
479 */
480 static fpcfga_ret_t
fp_rcm_info_table(rcm_info_t * rinfo,char ** table)481 fp_rcm_info_table(rcm_info_t *rinfo, char **table)
482 {
483 int i;
484 size_t w;
485 size_t width = 0;
486 size_t w_rsrc = 0;
487 size_t w_info = 0;
488 size_t table_size = 0;
489 uint_t tuples = 0;
490 rcm_info_tuple_t *tuple = NULL;
491 char *rsrc;
492 char *info;
493 char *newtable;
494 static char format[MAX_FORMAT];
495 const char *info_info_str, *info_rsrc_str;
496
497 /* Protect against invalid arguments */
498 if (rinfo == NULL || table == NULL)
499 return (FPCFGA_ERR);
500
501 /* Set localized table header strings */
502 rsrc = gettext("Resource");
503 info = gettext("Information");
504
505 /* A first pass, to size up the RCM information */
506 while (tuple = rcm_info_next(rinfo, tuple)) {
507 info_info_str = rcm_info_info(tuple);
508 info_rsrc_str = rcm_info_rsrc(tuple);
509 if ((info_info_str != NULL) && (info_rsrc_str != NULL)) {
510 tuples++;
511 if ((w = strlen(info_rsrc_str)) > w_rsrc)
512 w_rsrc = w;
513 if ((w = strlen(info_info_str)) > w_info)
514 w_info = w;
515 }
516 }
517
518 /* If nothing was sized up above, stop early */
519 if (tuples == 0)
520 return (FPCFGA_OK);
521
522 /* Adjust column widths for column headings */
523 if ((w = strlen(rsrc)) > w_rsrc)
524 w_rsrc = w;
525 else if ((w_rsrc - w) % 2)
526 w_rsrc++;
527 if ((w = strlen(info)) > w_info)
528 w_info = w;
529 else if ((w_info - w) % 2)
530 w_info++;
531
532 /*
533 * Compute the total line width of each line,
534 * accounting for intercolumn spacing.
535 */
536 width = w_info + w_rsrc + 4;
537
538 /* Allocate space for the table */
539 table_size = (2 + tuples) * (width + 1) + 2;
540 if (*table == NULL)
541 *table = malloc(table_size);
542 else {
543 newtable = realloc(*table, strlen(*table) + table_size);
544 if (newtable != NULL)
545 *table = newtable;
546 }
547 if (*table == NULL)
548 return (FPCFGA_ERR);
549
550 /* Place a table header into the string */
551
552 /* The resource header */
553 (void) strcat(*table, "\n");
554 w = strlen(rsrc);
555 for (i = 0; i < ((w_rsrc - w) / 2); i++)
556 (void) strcat(*table, " ");
557 (void) strcat(*table, rsrc);
558 for (i = 0; i < ((w_rsrc - w) / 2); i++)
559 (void) strcat(*table, " ");
560
561 /* The information header */
562 (void) strcat(*table, " ");
563 w = strlen(info);
564 for (i = 0; i < ((w_info - w) / 2); i++)
565 (void) strcat(*table, " ");
566 (void) strcat(*table, info);
567 for (i = 0; i < ((w_info - w) / 2); i++)
568 (void) strcat(*table, " ");
569
570 /* Underline the headers */
571 (void) strcat(*table, "\n");
572 for (i = 0; i < w_rsrc; i++)
573 (void) strcat(*table, "-");
574 (void) strcat(*table, " ");
575 for (i = 0; i < w_info; i++)
576 (void) strcat(*table, "-");
577
578 /* Construct the format string */
579 (void) snprintf(format, MAX_FORMAT, "%%-%ds %%-%ds", w_rsrc, w_info);
580
581 /* Add the tuples to the table string */
582 tuple = NULL;
583 while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) {
584 info_info_str = rcm_info_info(tuple);
585 info_rsrc_str = rcm_info_rsrc(tuple);
586 if ((info_info_str != NULL) && (info_rsrc_str != NULL)) {
587 (void) strcat(*table, "\n");
588 (void) sprintf(&((*table)[strlen(*table)]),
589 format, info_rsrc_str, info_info_str);
590 }
591 }
592
593 return (FPCFGA_OK);
594 }
595
596 /*
597 * chop_minor()
598 *
599 * Chops off the minor name portion of a resource. Allocates storage for
600 * the returned string. Caller must free the storage if return is non-NULL.
601 */
602 static char *
chop_minor(char * rsrc)603 chop_minor(char *rsrc)
604 {
605 char *rsrc_fixed;
606 char *cp;
607
608 if ((rsrc_fixed = strdup(rsrc)) == NULL)
609 return (NULL);
610 if ((cp = strrchr(rsrc_fixed, ':')) != NULL)
611 *cp = '\0';
612 return (rsrc_fixed);
613 }
614