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