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 2004 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <assert.h>
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <macros.h>
33 #include <dirent.h>
34 #include <libgen.h>
35 #include <libdevinfo.h>
36 #define CFGA_PLUGIN_LIB
37 #include <config_admin.h>
38 #include "ap.h"
39
40 #ifdef __x86
41 #include <libscf_priv.h>
42
43 static int fastreboot_disabled;
44 #endif /* __x86 */
45
46 static cfga_err_t
ap_suspend_check(apd_t * a,int cmd,int first,int last,int * suspend)47 ap_suspend_check(apd_t *a, int cmd, int first, int last, int *suspend)
48 {
49 int c;
50 int rc;
51 int skip;
52 int check;
53
54 skip = a->opts.skip;
55
56 /*
57 * Check if any of the steps in the sequence
58 * may require a suspension of service and ask
59 * the user to confirm.
60 */
61 for (check = 0, c = first; c <= last; c++)
62 if (mask(c) & skip)
63 continue;
64 else if ((rc = ap_suspend_query(a, c, &check)) != CFGA_OK)
65 return (rc);
66
67 *suspend = check;
68
69 /*
70 * If a suspend is required, ask for user confirmation.
71 * The force flag overrides the user confirmation.
72 */
73 if (check && (!ap_getopt(a, OPT_FORCE)) && (!ap_confirm(a))) {
74 ap_err(a, ERR_CMD_NACK, cmd);
75 return (CFGA_NACK);
76 }
77
78 return (CFGA_OK);
79 }
80
81 #define AP_SEQ_OK 0
82 #define AP_SEQ_NULL 1
83 #define AP_SEQ_FAIL -1
84
85 /*
86 * Sequence a cfgadm state change command into driver commands.
87 * The rstate and ostate of the AP are needed at this point
88 * in order to compute the proper sequence.
89 */
90 static int
ap_seq_get(apd_t * a,int cmd,int * first,int * last)91 ap_seq_get(apd_t *a, int cmd, int *first, int *last)
92 {
93 int done = 0;
94 int f = CMD_NONE;
95 int l = CMD_NONE;
96 cfga_stat_t rs, os;
97
98 ap_state(a, &rs, &os);
99
100 switch (rs) {
101 case CFGA_STAT_EMPTY:
102 switch (os) {
103 case CFGA_STAT_UNCONFIGURED:
104 switch (cmd) {
105 case CMD_UNCONFIGURE:
106 done++;
107 break;
108 default:
109 break;
110 }
111 break;
112 default:
113 break;
114 }
115 break;
116 case CFGA_STAT_DISCONNECTED:
117 switch (os) {
118 case CFGA_STAT_UNCONFIGURED:
119 switch (cmd) {
120 case CMD_DISCONNECT:
121 /*
122 * skip the disconnect command since
123 * the rstate is already disconnected
124 */
125 f = CMD_DISCONNECT;
126 a->opts.skip |= mask(CMD_DISCONNECT);
127 l = CMD_UNASSIGN;
128 break;
129 case CMD_UNCONFIGURE:
130 done++;
131 break;
132 case CMD_CONNECT:
133 f = CMD_ASSIGN;
134 l = CMD_CONNECT;
135 break;
136 case CMD_CONFIGURE:
137 f = CMD_ASSIGN;
138 l = CMD_RCM_CAP_ADD;
139 break;
140 default:
141 break;
142 }
143 break;
144 default:
145 break;
146 }
147 break;
148 case CFGA_STAT_CONNECTED:
149 switch (os) {
150 case CFGA_STAT_UNCONFIGURED:
151 switch (cmd) {
152 case CMD_CONNECT:
153 case CMD_UNCONFIGURE:
154 done++;
155 break;
156 case CMD_DISCONNECT:
157 f = CMD_DISCONNECT;
158 l = CMD_UNASSIGN;
159 break;
160 case CMD_CONFIGURE:
161 f = CMD_CONFIGURE;
162 l = CMD_RCM_CAP_ADD;
163 break;
164 default:
165 break;
166 }
167 break;
168 case CFGA_STAT_CONFIGURED:
169 switch (cmd) {
170 case CMD_CONNECT:
171 done++;
172 break;
173 case CMD_DISCONNECT:
174 f = CMD_SUSPEND_CHECK;
175 l = CMD_UNASSIGN;
176 break;
177 case CMD_CONFIGURE:
178 f = CMD_CONFIGURE;
179 l = CMD_RCM_CAP_ADD;
180 break;
181 case CMD_UNCONFIGURE:
182 f = CMD_SUSPEND_CHECK;
183 l = CMD_RCM_CAP_NOTIFY;
184 break;
185 default:
186 break;
187 }
188 break;
189 default:
190 break;
191 }
192 break;
193 default:
194 break;
195 }
196
197 if (f == CMD_NONE) {
198 if (done)
199 return (AP_SEQ_NULL);
200 ap_err(a, ERR_TRANS_INVAL, cmd);
201 return (AP_SEQ_FAIL);
202 }
203
204 *first = f;
205 *last = l;
206
207 DBG("ap_seq(%d, %d, %d, %p, %p) = (%d, %d)\n",
208 rs, os, cmd, (void *)first, (void *)last, f, l);
209
210 return (AP_SEQ_OK);
211 }
212
213 #define DBG_RECOVER_MSG(f, l) \
214 DBG("Sequencing recovery: first = %s, last = %s\n", \
215 ap_cmd_name(f), ap_cmd_name(l))
216
217 cfga_err_t
ap_seq_exec(apd_t * a,int cmd,int first,int last)218 ap_seq_exec(apd_t *a, int cmd, int first, int last)
219 {
220 int c;
221 int skip;
222 int suspend;
223 int resume;
224 cfga_err_t rc;
225 int recover_f = CMD_NONE; /* first recovery cmd */
226 int recover_l = CMD_NONE; /* last recovery cmd */
227
228
229 suspend = 0;
230 resume = 0;
231
232 skip = a->opts.skip;
233
234 /*
235 * The unassign step is skipped unless explicity requested
236 * either by a -x request or as an option to a disconnect
237 * request.
238 */
239 if (cmd != CMD_UNASSIGN && ap_getopt(a, OPT_UNASSIGN) == 0)
240 skip |= mask(CMD_UNASSIGN);
241
242 /*
243 * Check for platform options
244 */
245 rc = ap_platopts_check(a, first, last);
246
247 if (rc != CFGA_OK) {
248 goto done;
249 }
250
251 for (c = first; c <= last; c++) {
252 if (mask(c) & skip) {
253 ap_msg(a, MSG_SKIP, c, a->target);
254 continue;
255 }
256
257 DBG("exec %s\n", ap_cmd_name(c));
258
259 /*
260 * If the suspend operation does not
261 * succeed, resume any devices already
262 * suspended as well as the device on
263 * which the operation failed.
264 */
265 switch (c) {
266 case CMD_SUSPEND_CHECK:
267 /*
268 * Check whether the user allows a suspend
269 * operation if the suspend is required.
270 * Next step is to allow RCM clients to
271 * interpose on the suspend operation.
272 */
273 rc = ap_suspend_check(a, cmd,
274 first + 1, last, &suspend);
275 break;
276 case CMD_RCM_SUSPEND:
277 if (suspend && ((rc = ap_rcm_ctl(a, c)) == CFGA_OK)) {
278 /*
279 * Mark the fact that a suspend operation
280 * is required, and that RCM clients have
281 * allowed the suspend.
282 */
283 ap_setopt(a, OPT_SUSPEND_OK);
284 resume++;
285 }
286 break;
287 case CMD_RCM_RESUME:
288 if (resume) {
289 (void) ap_rcm_ctl(a, c);
290 resume--;
291 }
292 break;
293 case CMD_RCM_OFFLINE:
294 case CMD_RCM_CAP_DEL:
295 rc = ap_rcm_ctl(a, c);
296 break;
297 case CMD_RCM_ONLINE:
298 case CMD_RCM_CAP_ADD:
299 case CMD_RCM_REMOVE:
300 case CMD_RCM_CAP_NOTIFY:
301 (void) ap_rcm_ctl(a, c);
302 break;
303
304 #ifdef __x86
305 /*
306 * Disable fast reboot if a CPU/MEM/IOH hotplug event happens.
307 * Note: this is a temporary solution and will be revised when
308 * fast reboot can support CPU/MEM/IOH DR operations in the
309 * future.
310 *
311 * ACPI BIOS generates some static ACPI tables, such as MADT,
312 * SRAT and SLIT, to describe the system hardware configuration
313 * on power-on. When a CPU/MEM/IOH hotplug event happens, those
314 * static tables won't be updated and will become stale.
315 *
316 * If we reset the system by fast reboot, BIOS will have no
317 * chance to regenerate those staled static tables. Fast reboot
318 * can't tolerate such inconsistency between staled ACPI tables
319 * and real hardware configuration yet.
320 *
321 * A temporary solution is introduced to disable fast reboot if
322 * CPU/MEM/IOH hotplug event happens. This solution should be
323 * revised when fast reboot is enhanced to support CPU/MEM/IOH
324 * DR operations.
325 */
326 case CMD_ASSIGN:
327 case CMD_POWERON:
328 case CMD_POWEROFF:
329 case CMD_UNASSIGN:
330 if (!fastreboot_disabled &&
331 scf_fastreboot_default_set_transient(B_FALSE) ==
332 SCF_SUCCESS) {
333 fastreboot_disabled = 1;
334 }
335 #endif /* __x86 */
336 /* FALLTHROUGH */
337
338 default:
339 rc = ap_ioctl(a, c);
340 break;
341 }
342
343 if (rc != CFGA_OK)
344 break;
345
346 }
347 done:
348
349 if (resume)
350 (void) ap_rcm_ctl(a, CMD_RCM_RESUME);
351
352 /*
353 * Check if any operations failed. If so, attempt to rollback
354 * to previously known states.
355 * Note: The rollback is currently limited to RCM operations.
356 */
357 if (rc != CFGA_OK) {
358 if (c == CMD_UNCONFIGURE ||
359 c == CMD_RCM_OFFLINE ||
360 c == CMD_RCM_CAP_DEL) {
361 DBG("ap_seq_exec: %s failed\n", ap_cmd_name(c));
362
363 switch (c) {
364 case CMD_UNCONFIGURE:
365 /*
366 * If the unconfigure operation fails, perform
367 * an RCM_ONLINE and RCM_CAP_NOTIFY only. This
368 * keeps RCM clients consistent with the domain.
369 */
370 recover_f = CMD_RCM_ONLINE;
371 recover_l = CMD_RCM_ONLINE;
372 DBG_RECOVER_MSG(recover_f, recover_l);
373 (void) ap_seq_exec(a, cmd, recover_f,
374 recover_l);
375
376 recover_f = CMD_RCM_CAP_NOTIFY;
377 recover_l = CMD_RCM_CAP_NOTIFY;
378 DBG_RECOVER_MSG(recover_f, recover_l);
379 (void) ap_seq_exec(a, cmd, recover_f,
380 recover_l);
381 break;
382 case CMD_RCM_OFFLINE:
383 recover_f = CMD_RCM_ONLINE;
384 recover_l = CMD_RCM_CAP_ADD;
385 DBG_RECOVER_MSG(recover_f, recover_l);
386 (void) ap_seq_exec(a, cmd, recover_f,
387 recover_l);
388 break;
389 case CMD_RCM_CAP_DEL:
390 recover_f = CMD_RCM_CAP_ADD;
391 recover_l = CMD_RCM_CAP_ADD;
392 DBG_RECOVER_MSG(recover_f, recover_l);
393 (void) ap_seq_exec(a, cmd, recover_f,
394 recover_l);
395 break;
396 default:
397 break;
398 }
399
400 DBG("recovery complete!\n");
401 }
402 }
403 return (rc);
404 }
405
406 cfga_err_t
ap_cmd_exec(apd_t * a,int cmd)407 ap_cmd_exec(apd_t *a, int cmd)
408 {
409 return (ap_seq_exec(a, cmd, cmd, cmd));
410 }
411
412 cfga_err_t
ap_cmd_seq(apd_t * a,int cmd)413 ap_cmd_seq(apd_t *a, int cmd)
414 {
415 int first, last;
416 cfga_err_t rc;
417
418 switch (ap_seq_get(a, cmd, &first, &last)) {
419 case AP_SEQ_OK:
420 rc = ap_seq_exec(a, cmd, first, last);
421 break;
422 case AP_SEQ_NULL:
423 rc = CFGA_OK;
424 break;
425 case AP_SEQ_FAIL:
426 default:
427 rc = CFGA_LIB_ERROR;
428 break;
429 }
430
431 return (rc);
432 }
433