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