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 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 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 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 /* FALLTHROUGH */ 336 #endif /* __x86 */ 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 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 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