/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #define CFGA_PLUGIN_LIB #include #include "ap.h" static cfga_err_t ap_suspend_check(apd_t *a, int cmd, int first, int last, int *suspend) { int c; int rc; int skip; int check; skip = a->opts.skip; /* * Check if any of the steps in the sequence * may require a suspension of service and ask * the user to confirm. */ for (check = 0, c = first; c <= last; c++) if (mask(c) & skip) continue; else if ((rc = ap_suspend_query(a, c, &check)) != CFGA_OK) return (rc); *suspend = check; /* * If a suspend is required, ask for user confirmation. * The force flag overrides the user confirmation. */ if (check && (!ap_getopt(a, OPT_FORCE)) && (!ap_confirm(a))) { ap_err(a, ERR_CMD_NACK, cmd); return (CFGA_NACK); } return (CFGA_OK); } #define AP_SEQ_OK 0 #define AP_SEQ_NULL 1 #define AP_SEQ_FAIL -1 /* * Sequence a cfgadm state change command into driver commands. * The rstate and ostate of the AP are needed at this point * in order to compute the proper sequence. */ static int ap_seq_get(apd_t *a, int cmd, int *first, int *last) { int done = 0; int f = CMD_NONE; int l = CMD_NONE; cfga_stat_t rs, os; ap_state(a, &rs, &os); switch (rs) { case CFGA_STAT_EMPTY: switch (os) { case CFGA_STAT_UNCONFIGURED: switch (cmd) { case CMD_UNCONFIGURE: done++; break; default: break; } break; default: break; } break; case CFGA_STAT_DISCONNECTED: switch (os) { case CFGA_STAT_UNCONFIGURED: switch (cmd) { case CMD_DISCONNECT: /* * skip the disconnect command since * the rstate is already disconnected */ f = CMD_DISCONNECT; a->opts.skip |= mask(CMD_DISCONNECT); l = CMD_UNASSIGN; break; case CMD_UNCONFIGURE: done++; break; case CMD_CONNECT: f = CMD_ASSIGN; l = CMD_CONNECT; break; case CMD_CONFIGURE: f = CMD_ASSIGN; l = CMD_RCM_CAP_ADD; break; default: break; } break; default: break; } break; case CFGA_STAT_CONNECTED: switch (os) { case CFGA_STAT_UNCONFIGURED: switch (cmd) { case CMD_CONNECT: case CMD_UNCONFIGURE: done++; break; case CMD_DISCONNECT: f = CMD_DISCONNECT; l = CMD_UNASSIGN; break; case CMD_CONFIGURE: f = CMD_CONFIGURE; l = CMD_RCM_CAP_ADD; break; default: break; } break; case CFGA_STAT_CONFIGURED: switch (cmd) { case CMD_CONNECT: done++; break; case CMD_DISCONNECT: f = CMD_SUSPEND_CHECK; l = CMD_UNASSIGN; break; case CMD_CONFIGURE: f = CMD_CONFIGURE; l = CMD_RCM_CAP_ADD; break; case CMD_UNCONFIGURE: f = CMD_SUSPEND_CHECK; l = CMD_RCM_CAP_NOTIFY; break; default: break; } break; default: break; } break; default: break; } if (f == CMD_NONE) { if (done) return (AP_SEQ_NULL); ap_err(a, ERR_TRANS_INVAL, cmd); return (AP_SEQ_FAIL); } *first = f; *last = l; DBG("ap_seq(%d, %d, %d, %p, %p) = (%d, %d)\n", rs, os, cmd, (void *)first, (void *)last, f, l); return (AP_SEQ_OK); } #define DBG_RECOVER_MSG(f, l) \ DBG("Sequencing recovery: first = %s, last = %s\n", \ ap_cmd_name(f), ap_cmd_name(l)) cfga_err_t ap_seq_exec(apd_t *a, int cmd, int first, int last) { int c; int skip; int suspend; int resume; cfga_err_t rc; int recover_f = CMD_NONE; /* first recovery cmd */ int recover_l = CMD_NONE; /* last recovery cmd */ suspend = 0; resume = 0; skip = a->opts.skip; /* * The unassign step is skipped unless explicity requested * either by a -x request or as an option to a disconnect * request. */ if (cmd != CMD_UNASSIGN && ap_getopt(a, OPT_UNASSIGN) == 0) skip |= mask(CMD_UNASSIGN); /* * Check for platform options */ rc = ap_platopts_check(a, first, last); if (rc != CFGA_OK) { goto done; } for (c = first; c <= last; c++) { if (mask(c) & skip) { ap_msg(a, MSG_SKIP, c, a->target); continue; } DBG("exec %s\n", ap_cmd_name(c)); /* * If the suspend operation does not * succeed, resume any devices already * suspended as well as the device on * which the operation failed. */ switch (c) { case CMD_SUSPEND_CHECK: /* * Check whether the user allows a suspend * operation if the suspend is required. * Next step is to allow RCM clients to * interpose on the suspend operation. */ rc = ap_suspend_check(a, cmd, first + 1, last, &suspend); break; case CMD_RCM_SUSPEND: if (suspend && ((rc = ap_rcm_ctl(a, c)) == CFGA_OK)) { /* * Mark the fact that a suspend operation * is required, and that RCM clients have * allowed the suspend. */ ap_setopt(a, OPT_SUSPEND_OK); resume++; } break; case CMD_RCM_RESUME: if (resume) { (void) ap_rcm_ctl(a, c); resume--; } break; case CMD_RCM_OFFLINE: case CMD_RCM_CAP_DEL: rc = ap_rcm_ctl(a, c); break; case CMD_RCM_ONLINE: case CMD_RCM_CAP_ADD: case CMD_RCM_REMOVE: case CMD_RCM_CAP_NOTIFY: (void) ap_rcm_ctl(a, c); break; default: rc = ap_ioctl(a, c); break; } if (rc != CFGA_OK) break; } done: if (resume) (void) ap_rcm_ctl(a, CMD_RCM_RESUME); /* * Check if any operations failed. If so, attempt to rollback * to previously known states. * Note: The rollback is currently limited to RCM operations. */ if (rc != CFGA_OK) { if (c == CMD_UNCONFIGURE || c == CMD_RCM_OFFLINE || c == CMD_RCM_CAP_DEL) { DBG("ap_seq_exec: %s failed\n", ap_cmd_name(c)); switch (c) { case CMD_UNCONFIGURE: /* * If the unconfigure operation fails, perform * an RCM_ONLINE and RCM_CAP_NOTIFY only. This * keeps RCM clients consistent with the domain. */ recover_f = CMD_RCM_ONLINE; recover_l = CMD_RCM_ONLINE; DBG_RECOVER_MSG(recover_f, recover_l); (void) ap_seq_exec(a, cmd, recover_f, recover_l); recover_f = CMD_RCM_CAP_NOTIFY; recover_l = CMD_RCM_CAP_NOTIFY; DBG_RECOVER_MSG(recover_f, recover_l); (void) ap_seq_exec(a, cmd, recover_f, recover_l); break; case CMD_RCM_OFFLINE: recover_f = CMD_RCM_ONLINE; recover_l = CMD_RCM_CAP_ADD; DBG_RECOVER_MSG(recover_f, recover_l); (void) ap_seq_exec(a, cmd, recover_f, recover_l); break; case CMD_RCM_CAP_DEL: recover_f = CMD_RCM_CAP_ADD; recover_l = CMD_RCM_CAP_ADD; DBG_RECOVER_MSG(recover_f, recover_l); (void) ap_seq_exec(a, cmd, recover_f, recover_l); break; default: break; } DBG("recovery complete!\n"); } } return (rc); } cfga_err_t ap_cmd_exec(apd_t *a, int cmd) { return (ap_seq_exec(a, cmd, cmd, cmd)); } cfga_err_t ap_cmd_seq(apd_t *a, int cmd) { int first, last; cfga_err_t rc; switch (ap_seq_get(a, cmd, &first, &last)) { case AP_SEQ_OK: rc = ap_seq_exec(a, cmd, first, last); break; case AP_SEQ_NULL: rc = CFGA_OK; break; case AP_SEQ_FAIL: default: rc = CFGA_LIB_ERROR; break; } return (rc); }