xref: /illumos-gate/usr/src/lib/cfgadm_plugins/sbd/common/ap_seq.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <macros.h>
36 #include <dirent.h>
37 #include <libgen.h>
38 #include <libdevinfo.h>
39 #define	CFGA_PLUGIN_LIB
40 #include <config_admin.h>
41 #include "ap.h"
42 
43 static cfga_err_t
44 ap_suspend_check(apd_t *a, int cmd, int first, int last, int *suspend)
45 {
46 	int c;
47 	int rc;
48 	int skip;
49 	int check;
50 
51 	skip = a->opts.skip;
52 
53 	/*
54 	 * Check if any of the steps in the sequence
55 	 * may require a suspension of service and ask
56 	 * the user to confirm.
57 	 */
58 	for (check = 0, c = first; c <= last; c++)
59 		if (mask(c) & skip)
60 			continue;
61 		else if ((rc = ap_suspend_query(a, c, &check)) != CFGA_OK)
62 			return (rc);
63 
64 	*suspend = check;
65 
66 	/*
67 	 * If a suspend is required, ask for user confirmation.
68 	 * The force flag overrides the user confirmation.
69 	 */
70 	if (check && (!ap_getopt(a, OPT_FORCE)) && (!ap_confirm(a))) {
71 		ap_err(a, ERR_CMD_NACK, cmd);
72 		return (CFGA_NACK);
73 	}
74 
75 	return (CFGA_OK);
76 }
77 
78 #define	AP_SEQ_OK	0
79 #define	AP_SEQ_NULL	1
80 #define	AP_SEQ_FAIL	-1
81 
82 /*
83  * Sequence a cfgadm state change command into driver commands.
84  * The rstate and ostate of the AP are needed at this point
85  * in order to compute the proper sequence.
86  */
87 static int
88 ap_seq_get(apd_t *a, int cmd, int *first, int *last)
89 {
90 	int done = 0;
91 	int f = CMD_NONE;
92 	int l = CMD_NONE;
93 	cfga_stat_t rs, os;
94 
95 	ap_state(a, &rs, &os);
96 
97 	switch (rs) {
98 	case CFGA_STAT_EMPTY:
99 		switch (os) {
100 		case CFGA_STAT_UNCONFIGURED:
101 			switch (cmd) {
102 			case CMD_UNCONFIGURE:
103 				done++;
104 				break;
105 			default:
106 				break;
107 			}
108 			break;
109 		default:
110 			break;
111 		}
112 		break;
113 	case CFGA_STAT_DISCONNECTED:
114 		switch (os) {
115 		case CFGA_STAT_UNCONFIGURED:
116 			switch (cmd) {
117 			case CMD_DISCONNECT:
118 				/*
119 				 * skip the disconnect command since
120 				 * the rstate is already disconnected
121 				 */
122 				f = CMD_DISCONNECT;
123 				a->opts.skip |= mask(CMD_DISCONNECT);
124 				l = CMD_UNASSIGN;
125 				break;
126 			case CMD_UNCONFIGURE:
127 				done++;
128 				break;
129 			case CMD_CONNECT:
130 				f = CMD_ASSIGN;
131 				l = CMD_CONNECT;
132 				break;
133 			case CMD_CONFIGURE:
134 				f = CMD_ASSIGN;
135 				l = CMD_RCM_CAP_ADD;
136 				break;
137 			default:
138 				break;
139 			}
140 			break;
141 		default:
142 			break;
143 		}
144 		break;
145 	case CFGA_STAT_CONNECTED:
146 		switch (os) {
147 		case CFGA_STAT_UNCONFIGURED:
148 			switch (cmd) {
149 			case CMD_CONNECT:
150 			case CMD_UNCONFIGURE:
151 				done++;
152 				break;
153 			case CMD_DISCONNECT:
154 				f = CMD_DISCONNECT;
155 				l = CMD_UNASSIGN;
156 				break;
157 			case CMD_CONFIGURE:
158 				f = CMD_CONFIGURE;
159 				l = CMD_RCM_CAP_ADD;
160 				break;
161 			default:
162 				break;
163 			}
164 			break;
165 		case CFGA_STAT_CONFIGURED:
166 			switch (cmd) {
167 			case CMD_CONNECT:
168 				done++;
169 				break;
170 			case CMD_DISCONNECT:
171 				f = CMD_SUSPEND_CHECK;
172 				l = CMD_UNASSIGN;
173 				break;
174 			case CMD_CONFIGURE:
175 				f = CMD_CONFIGURE;
176 				l = CMD_RCM_CAP_ADD;
177 				break;
178 			case CMD_UNCONFIGURE:
179 				f = CMD_SUSPEND_CHECK;
180 				l = CMD_RCM_CAP_NOTIFY;
181 				break;
182 			default:
183 				break;
184 			}
185 			break;
186 		default:
187 			break;
188 		}
189 		break;
190 	default:
191 		break;
192 	}
193 
194 	if (f == CMD_NONE) {
195 		if (done)
196 			return (AP_SEQ_NULL);
197 		ap_err(a, ERR_TRANS_INVAL, cmd);
198 		return (AP_SEQ_FAIL);
199 	}
200 
201 	*first = f;
202 	*last = l;
203 
204 	DBG("ap_seq(%d, %d, %d, %p, %p) = (%d, %d)\n",
205 		rs, os, cmd, (void *)first, (void *)last, f, l);
206 
207 	return (AP_SEQ_OK);
208 }
209 
210 #define	DBG_RECOVER_MSG(f, l) \
211 	DBG("Sequencing recovery: first = %s, last = %s\n", \
212 	ap_cmd_name(f), ap_cmd_name(l))
213 
214 cfga_err_t
215 ap_seq_exec(apd_t *a, int cmd, int first, int last)
216 {
217 	int c;
218 	int skip;
219 	int suspend;
220 	int resume;
221 	cfga_err_t rc;
222 	int recover_f = CMD_NONE;	/* first recovery cmd */
223 	int recover_l = CMD_NONE;	/* last recovery cmd */
224 
225 
226 	suspend = 0;
227 	resume = 0;
228 
229 	skip = a->opts.skip;
230 
231 	/*
232 	 * The unassign step is skipped unless explicity requested
233 	 * either by a -x request or as an option to a disconnect
234 	 * request.
235 	 */
236 	if (cmd != CMD_UNASSIGN && ap_getopt(a, OPT_UNASSIGN) == 0)
237 		skip |= mask(CMD_UNASSIGN);
238 
239 	/*
240 	 * Check for platform options
241 	 */
242 	rc = ap_platopts_check(a, first, last);
243 
244 	if (rc != CFGA_OK) {
245 		goto done;
246 	}
247 
248 	for (c = first; c <= last; c++) {
249 		if (mask(c) & skip) {
250 			ap_msg(a, MSG_SKIP, c, a->target);
251 			continue;
252 		}
253 
254 		DBG("exec %s\n", ap_cmd_name(c));
255 
256 		/*
257 		 * If the suspend operation does not
258 		 * succeed, resume any devices already
259 		 * suspended as well as the device on
260 		 * which the operation failed.
261 		 */
262 		switch (c) {
263 		case CMD_SUSPEND_CHECK:
264 			/*
265 			 * Check whether the user allows a suspend
266 			 * operation if the suspend is required.
267 			 * Next step is to allow RCM clients to
268 			 * interpose on the suspend operation.
269 			 */
270 			rc = ap_suspend_check(a, cmd,
271 				first + 1, last, &suspend);
272 			break;
273 		case CMD_RCM_SUSPEND:
274 			if (suspend && ((rc = ap_rcm_ctl(a, c)) == CFGA_OK)) {
275 				/*
276 				 * Mark the fact that a suspend operation
277 				 * is required, and that RCM clients have
278 				 * allowed the suspend.
279 				 */
280 				ap_setopt(a, OPT_SUSPEND_OK);
281 				resume++;
282 			}
283 			break;
284 		case CMD_RCM_RESUME:
285 			if (resume) {
286 				(void) ap_rcm_ctl(a, c);
287 				resume--;
288 			}
289 			break;
290 		case CMD_RCM_OFFLINE:
291 		case CMD_RCM_CAP_DEL:
292 			rc = ap_rcm_ctl(a, c);
293 			break;
294 		case CMD_RCM_ONLINE:
295 		case CMD_RCM_CAP_ADD:
296 		case CMD_RCM_REMOVE:
297 		case CMD_RCM_CAP_NOTIFY:
298 			(void) ap_rcm_ctl(a, c);
299 			break;
300 		default:
301 			rc = ap_ioctl(a, c);
302 			break;
303 		}
304 
305 		if (rc != CFGA_OK)
306 			break;
307 
308 	}
309 done:
310 
311 	if (resume)
312 		(void) ap_rcm_ctl(a, CMD_RCM_RESUME);
313 
314 	/*
315 	 * Check if any operations failed. If so, attempt to rollback
316 	 * to previously known states.
317 	 * Note: The rollback is currently limited to RCM operations.
318 	 */
319 	if (rc != CFGA_OK) {
320 		if (c == CMD_UNCONFIGURE ||
321 		    c == CMD_RCM_OFFLINE ||
322 		    c == CMD_RCM_CAP_DEL) {
323 			DBG("ap_seq_exec: %s failed\n", ap_cmd_name(c));
324 
325 			switch (c) {
326 			case CMD_UNCONFIGURE:
327 				/*
328 				 * If the unconfigure operation fails, perform
329 				 * an RCM_ONLINE and RCM_CAP_NOTIFY only. This
330 				 * keeps RCM clients consistent with the domain.
331 				 */
332 				recover_f = CMD_RCM_ONLINE;
333 				recover_l = CMD_RCM_ONLINE;
334 				DBG_RECOVER_MSG(recover_f, recover_l);
335 				(void) ap_seq_exec(a, cmd, recover_f,
336 				    recover_l);
337 
338 				recover_f = CMD_RCM_CAP_NOTIFY;
339 				recover_l = CMD_RCM_CAP_NOTIFY;
340 				DBG_RECOVER_MSG(recover_f, recover_l);
341 				(void) ap_seq_exec(a, cmd, recover_f,
342 				    recover_l);
343 				break;
344 			case CMD_RCM_OFFLINE:
345 				recover_f = CMD_RCM_ONLINE;
346 				recover_l = CMD_RCM_CAP_ADD;
347 				DBG_RECOVER_MSG(recover_f, recover_l);
348 				(void) ap_seq_exec(a, cmd, recover_f,
349 				    recover_l);
350 				break;
351 			case CMD_RCM_CAP_DEL:
352 				recover_f = CMD_RCM_CAP_ADD;
353 				recover_l = CMD_RCM_CAP_ADD;
354 				DBG_RECOVER_MSG(recover_f, recover_l);
355 				(void) ap_seq_exec(a, cmd, recover_f,
356 				    recover_l);
357 				break;
358 			default:
359 				break;
360 			}
361 
362 			DBG("recovery complete!\n");
363 		}
364 	}
365 	return (rc);
366 }
367 
368 cfga_err_t
369 ap_cmd_exec(apd_t *a, int cmd)
370 {
371 	return (ap_seq_exec(a, cmd, cmd, cmd));
372 }
373 
374 cfga_err_t
375 ap_cmd_seq(apd_t *a, int cmd)
376 {
377 	int first, last;
378 	cfga_err_t rc;
379 
380 	switch (ap_seq_get(a, cmd, &first, &last)) {
381 	case AP_SEQ_OK:
382 		rc = ap_seq_exec(a, cmd, first, last);
383 		break;
384 	case AP_SEQ_NULL:
385 		rc = CFGA_OK;
386 		break;
387 	case AP_SEQ_FAIL:
388 	default:
389 		rc = CFGA_LIB_ERROR;
390 		break;
391 	}
392 
393 	return (rc);
394 }
395