Home | History | Annotate | Download | only in common
      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